Code to enumerate permutations in Scala

后端 未结 11 2024
臣服心动
臣服心动 2020-12-02 16:06

I coded a function to enumerate all permutations of a given list. What do you think of the code below?

def interleave(x:Int, l:List[Int]):List[List[Int]] = {         


        
相关标签:
11条回答
  • 2020-12-02 16:39

    Given a Seq, one can already have permutations by invoking the permutations method.

    scala> List(1,2,3).permutations.mkString("\n")
    res3: String = 
    List(1, 2, 3)
    List(1, 3, 2)
    List(2, 1, 3)
    List(2, 3, 1)
    List(3, 1, 2)
    List(3, 2, 1)
    

    Furthermore there is also a method for combinations:

    scala> List(1,2,3).combinations(2).mkString("\n")
    res4: String = 
    List(1, 2)
    List(1, 3)
    List(2, 3)
    

    Regarding your implementation I would say three things:

    (1) Its good looking

    (2) Provide an iterator (which is the std collections approach that allows to discard elements). Otherwise, you can get lists with 1000! elements which may not fit in memory.

    scala> val longList = List((1 to 1000):_*)
    longList: List[Int] = List(1, 2, 3,...
    
    
    scala> permutations(longList)
    java.lang.OutOfMemoryError: Java heap space
        at scala.collection.immutable.List.$colon$colon(List.scala:67)
        at .interleave(<console>:11)
        at .interleave(<console>:11)
        at .interleave(<console>:11)
    

    (3) You should remove duplicated permutations (as observed by Luigi), since :

    scala> permutations(List(1,1,3))
    res4: List[List[Int]] = List(List(1, 1, 3), List(1, 1, 3), List(1, 3, 1), List(1, 3, 1), List(3, 1, 1), List(3, 1, 1))
    
    scala> List(1,1,3).permutations.toList
    res5: List[List[Int]] = List(List(1, 1, 3), List(1, 3, 1), List(3, 1, 1))
    
    0 讨论(0)
  • 2020-12-02 16:39
    def permutator[T](list: List[T]): List[List[T]] = {
    
      def _p(total: Int, list: List[T]): List[List[T]] = {
        if (total == 0) {
          // End of the recursion tree
          list.map(List(_))
        } else {
          // Controlled combinatorial 
          // explosion while total > 0          
          for (i <- list;
               j <- _p(total - 1, list)) 
            yield { i :: j }
    
          // It is a recursion tree to generate the 
          // permutations of the elements
          // --------------------------------------
          // total = 0 => _p returns 3 elements (A, B, C) 
          // total = 1 => _p returns 3 * 3 List(List(A, A)...
          // total = 2 => _p returns 3 * 3 * 3 elements List(List(A, A, A)...
    
        }
      }
    
      _p(list.length - 1, list)
    }
    
    permutator(List("A", "B", "C"))
    
    // output:
    List(A, A, A),List(A, A, B),List(A, A, C),List(A, B, A),List(A, B, B),
    List(A, B, C),List(A, C, A),List(A, C, B),List(A, C, C),List(B, A, A),
    List(B, A, B),List(B, A, C),List(B, B, A),List(B, B, B),List(B, B, C),
    List(B, C, A),List(B, C, B),List(B, C, C),List(C, A, A),List(C, A, B),
    List(C, A, C),List(C, B, A),List(C, B, B),List(C, B, C),List(C, C, A),
    List(C, C, B),List(C, C, C)
    
    0 讨论(0)
  • 2020-12-02 16:41

    Consider the difference here: your version

    scala> permutations(List(1,1,2)) foreach println
    List(1, 1, 2)
    List(1, 1, 2)
    List(1, 2, 1)
    List(1, 2, 1)
    List(2, 1, 1)
    List(2, 1, 1)
    

    The reference version:

    scala> List(1,1,2).permutations foreach println
    List(1, 1, 2)
    List(1, 2, 1)
    List(2, 1, 1)
    
    0 讨论(0)
  • 2020-12-02 16:46

    I guess you are practicing your Scala programming skill. Here is another one, where the idea is to take different elements as head of sequence and repeats are removed through filter. Complexity of the code shall be fine, since O(n)+O(n or maybe n^2)+O(n)*P(n-1) is dominated by O(n)*P(n-1), where P(n) is the permutation number and cannot be improved, .

    def permute(xs:List[Int]):List[List[Int]] = xs match {
      case Nil => List(List())
      case head::tail => {
        val len = xs.length
        val tps = (0 to len-1).map(xs.splitAt(_)).toList.filter(tp => !tp._1.contains(tp._2.head))
        tps.map(tp => permute(tp._1:::tp._2.tail).map(tp._2.head :: _)).flatten
      }
    }
    
    0 讨论(0)
  • 2020-12-02 16:50

    This is an implementation which is based on the concept of cycle and a trivial implementation of permute with two elements. It does not handle duplicates and stack overflow aspect in the permute method

    object ImmuPermute extends App {
      def nextCycle(nums: List[Int]): List[Int] = {
        nums match {
          case Nil => Nil
          case head :: tail => tail :+ head
        }
      }
      def cycles(nums: List[Int]): List[List[Int]] = {
        def loop(l: List[Int], acc: List[List[Int]]): List[List[Int]] = {
          if (acc.size == nums.size)
            acc
          else {
            val next = nextCycle(l)
            loop(next, next :: acc)
          }
        }
        loop(nums, List(nums))
      }
      def permute(nums: List[Int]): List[List[Int]] = {
        nums match {
          case Nil => Nil
          case head :: Nil => List(List(head))
          case first :: second :: Nil => List(List(first, second), List(second, first))
          case _ => {
            val cycledList = cycles(nums)
            cycledList.flatMap { cycle =>
              val h = cycle.head
              val t = cycle.tail
              val permutedList = permute(t)
              permutedList map { pList =>
                h :: pList
              }
            }
          }
        }
      }
      val l = permute(List(1, 2, 3, 4))
      l foreach println
      println(l.size)
    }
    
    0 讨论(0)
提交回复
热议问题