在 Scala 中枚举排列的代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8124440/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 03:40:06  来源:igfitidea点击:

Code to enumerate permutations in Scala

scalapermutation

提问by Michael

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]] = {
  l match { 
    case Nil => List(List(x))
    case (head::tail) =>
      (x :: head :: tail) :: interleave(x, tail).map(head :: _)
  }
}

def permutations(l:List[Int]):List[List[Int]] = {
  l match {
    case Nil => List(List())
    case (head::tail) =>
      for(p0 <- permutations(tail); p1 <- interleave(head, p0)) yield p1
  }
}

回答by JaimeJorge

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

给定一个 Seq,可以通过调用该permutations方法来进行排列。

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:

此外,还有一种方法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

(1) 好看

(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.

(2) 提供一个迭代器(这是允许丢弃元素的标准集合方法)。否则,您可以获得 1000 个列表!可能不适合内存的元素。

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 :

(3) 您应该删除重复的排列(如 Luigi 所观察到的),因为:

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))

回答by Luigi Plinge

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)

回答by Peter Schmitz

I think such a function already exists in the standard library: Seq.permutations. So why reinventing the wheel again?

我认为标准库中已经存在这样的函数:Seq.permutations。那么为什么要重新发明轮子呢?

回答by Matthew Saltz

Maybe this thread is already well-saturated, but I thought I'd throw my solution into the mix:

也许这个线程已经很饱和了,但我想我会把我的解决方案混在一起:

Assuming no repeat elements:

假设没有重复元素:

def permList(l: List[Int]): List[List[Int]] = l match {
   case List(ele) => List(List(ele))
   case list =>
     for {
       i <- List.range(0, list.length)
       p <- permList(list.slice(0, i) ++ list.slice(i + 1, list.length))
     } yield list(i) :: p
}

With repeat elements, preventing duplicates (not as pretty):

使用重复元素,防止重复(不那么漂亮):

def permList(l: List[Int]): List[List[Int]] = l match {
  case List(ele) => List(List(ele))
  case list =>
    for {
      i <- List.range(0, list.length)
      val traversedList = list.slice(0, i)
      val nextEle = list(i)
      if !(traversedList contains nextEle)
      p <- permList(traversedList ++ list.slice(i + 1, list.length))
    } yield list(i) :: p
}

It's potentially not the most "list-y", given that it uses slice and an index on the list, but it's rather concise and a slightly different way of looking at it. It works by singling out each element in the list and computing the permutations of what's remaining, and then concatenating the single element to each of those permutations. If there's a more idiomatic way to do this, I'd love to hear about it.

鉴于它使用切片和列表上的索引,它可能不是最“列表-y”的,但它相当简洁,并且查看方式略有不同。它的工作原理是挑出列表中的每个元素并计算剩余内容的排列,然后将单个元素连接到这些排列中的每一个。如果有更惯用的方法来做到这一点,我很想听听。

回答by Adam Duracz

Here is a version based on span.

这是一个基于span的版本。

def perms[T](xs: List[T]): List[List[T]] = xs match {
  case List(_) => List(xs)
  case _ => for ( x <- xs
                ; val (l, r) = xs span { x!= }
                ; ys <- perms(l ++ r.tail)
                ) yield x :: ys
}

回答by uranio

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)

回答by lcn

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, .

我猜您正在练习 Scala 编程技能。这是另一个,其中的想法是将不同的元素作为序列的头部,并通过 删除重复filter。代码的复杂性应该没问题,因为 O(n)+O(n 或 n^2)+O(n)*P(n-1) 由 O(n)*P(n-1) 支配,其中 P(n) 是置换数,无法改进,。

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
  }
}

回答by Val

I think that mine solution is better than others

我认为我的解决方案比其他解决方案更好

  def withReplacements(chars: String, n: Int) {

        def internal(path: String, acc: List[String]): List[String] = {
          if (path.length == n) path :: acc else
            chars.toList.flatMap {c => internal(path + c, acc)}

        }

        val res = internal("", Nil)
        println("there are " + res.length + " " + n + "-permutations with replacement for " + chars + " = " + res)
      }                                       //> withReplacements: (chars: String, n: Int)Unit




      def noReplacements(chars: String, n: Int) {
        //val set = chars.groupBy(c => c).map {case (c, list) => (c -> list.length)}.toList

      import scala.collection.immutable.Queue

        type Set = Queue[Char]
        val set = Queue[Char](chars.toList: _*)

        type Result = Queue[String]

        // The idea is that recursions will scan the set with one element excluded.
        // Queue was chosen to implement the set to enable excluded element to bubble through it.
        def internal(set: Set, path: String, acc: Result): Result = {
          if (path.length == n) acc.enqueue(path)
          else
            set.foldLeft(acc, set.dequeue){case ((acc, (consumed_el, q)), e) =>
              (internal(q, consumed_el + path, acc), q.enqueue(consumed_el).dequeue)
            }. _1

        }

        val res = internal(set, "", Queue.empty)
        println("there are " + res.length + " " + n + "-permutations without replacement for " + set + " = " + res)

      }                                       //> noReplacements: (chars: String, n: Int)Unit



    withReplacements("abc", 2)                    //> there are 9 2-permutations with replacement for abc = List(aa, ab, ac, ba, 
                                                  //| bb, bc, ca, cb, cc)
    noReplacements("abc", 2)                      //> there are 6 2-permutations without replacement for Queue(a, b, c) = Queue(b
                                                  //| a, ca, cb, ab, ac, bc)


    noReplacements("abc", 3)                      //> there are 6 3-permutations without replacement for Queue(a, b, c) = Queue(c
                                                  //| ba, bca, acb, cab, bac, abc)


    withReplacements("abc", 3)                    //> there are 27 3-permutations with replacement for abc = List(aaa, aab, aac, 
                                                  //| aba, abb, abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, 
                                                  //| caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc)
  // you can run with replacements (3 chars, n = 4) but noReplacements will fail for obvious reason -- you cannont combine 3 chars to produce 4
    withReplacements("abc", 4)                    //> there are 81 4-permutations with replacement for abc = List(aaaa, aaab, aaa
                                                  //| c, aaba, aabb, aabc, aaca, aacb, aacc, abaa, abab, abac, abba, abbb, abbc, 
                                                  //| abca, abcb, abcc, acaa, acab, acac, acba, acbb, acbc, acca, accb, accc, baa
                                                  //| a, baab, baac, baba, babb, babc, baca, bacb, bacc, bbaa, bbab, bbac, bbba, 
                                                  //| bbbb, bbbc, bbca, bbcb, bbcc, bcaa, bcab, bcac, bcba, bcbb, bcbc, bcca, bcc
                                                  //| b, bccc, caaa, caab, caac, caba, cabb, cabc, caca, cacb, cacc, cbaa, cbab, 
                                                  //| cbac, cbba, cbbb, cbbc, cbca, cbcb, cbcc, ccaa, ccab, ccac, ccba, ccbb, ccb
                                                  //| c, ccca, cccb, cccc)
(1 to 3) foreach (u =>   noReplacements("aab", u))//> there are 3 1-permutations without replacement for Queue(a, a, b) = Queue(a
                                                  //| , a, b)
                                                  //| there are 6 2-permutations without replacement for Queue(a, a, b) = Queue(a
                                                  //| a, ba, ba, aa, ab, ab)
                                                  //| there are 6 3-permutations without replacement for Queue(a, a, b) = Queue(b
                                                  //| aa, aba, aba, baa, aab, aab)

These are the same 3 lines of code but variable permutation lengths are supported and list concatenations are eliminated.

这些是相同的 3 行代码,但支持可变排列长度并消除了列表串联。

I have made the second more ideomatic (so that flat-map merges of accumulator are prevented, which also makes it more tail-recursive) and extended into multiset permutations, so that you can say that "aab", "aba" and "baa" are permutations (of each other). The idea is that letter "a" is replacable two times instead infinitly (with replacement case) or availble only one time (without replacements). So, you need a counter, which tells you how many times every letter is avaiable for replacement.

我已经使第二个更加意识形态化(这样可以防止累加器的平面图合并,这也使其更具尾递归性)并扩展到多集排列,以便您可以说“aab”,“aba”和“baa” " 是(彼此的)排列。这个想法是字母“a”可以无限地替换两次(带替换情况)或仅可用一次(不带替换)。因此,您需要一个计数器,它会告诉您每个字母可以替换多少次。

  // Rewrite with replacement a bit to eliminate flat-map merges.

    def norep2(chars: String, n: Int/* = chars.length*/) {

    import scala.collection.immutable.Queue

      type Set = Queue[Char]
      val set = Queue[Char](chars.toList: _*)

      type Result = Queue[String]

        def siblings(set: (Char, Set), offset: Int, path: String, acc: Result): Result = set match {case (bubble, queue) =>
            val children = descend(queue, path + bubble, acc) // bubble was used, it is not available for children that will produce combinations in other positions
            if (offset == 0) children else siblings(queue.enqueue(bubble).dequeue, offset - 1, path, children) // siblings will produce different chars at the same position, fetch next char for them
        }

      def descend(set: Set, path: String, acc: Result): Result = {
        if (path.length == n) acc.enqueue(path) else siblings(set.dequeue, set.size-1, path, acc)
      }

      val res = descend(set, "", Queue.empty)
      println("there are " + res.length + " " + n + "-permutations without replacement for " + set + " = " + res)

    }                                             //> norep2: (chars: String, n: Int)Unit

    assert(norep2("abc", 2) == noReplacements("abc", 2))
                                                  //> there are 6 2-permutations without replacement for Queue(a, b, c) = Queue(a
                                                  //| b, ac, bc, ba, ca, cb)
                                                  //| there are 6 2-permutations without replacement for Queue(a, b, c) = Queue(b
                                                  //| a, ca, cb, ab, ac, bc)
    assert(norep2("abc", 3) == noReplacements("abc", 3))
                                                  //> there are 6 3-permutations without replacement for Queue(a, b, c) = Queue(a
                                                  //| bc, acb, bca, bac, cab, cba)
                                                  //| there are 6 3-permutations without replacement for Queue(a, b, c) = Queue(c
                                                  //| ba, bca, acb, cab, bac, abc)


    def multisets(chars: String, n: Int/* = chars.length*/) {

      import scala.collection.immutable.Queue

      type Set = Queue[Bubble]
      type Bubble = (Char, Int)
      type Result = Queue[String]

        def siblings(set: (Bubble, Set), offset: Int, path: String, acc: Result): Result = set match {case ((char, avail), queue) =>
            val children = descend(if (avail - 1 == 0) queue else queue.enqueue(char -> {avail-1}), path + char, acc) // childern can reuse the symbol while if it is available
            if (offset == 0) children else siblings(queue.enqueue((char, avail)).dequeue, offset - 1, path, children)
        }

      def descend(set: Set, path: String, acc: Result): Result = {
        if (path.length == n) acc.enqueue(path) else siblings(set.dequeue, set.size-1, path, acc)
      }

      val set = Queue[Bubble]((chars.toList groupBy (c => c) map {case (k, v)  => (k, v.length)}).toList: _*)
      val res = descend(set, "", Queue.empty)
      println("there are " + res.length + " multiset " + n + "-permutations for " + set + " = " + res)

    }                                             //> multisets: (chars: String, n: Int)Unit



assert(multisets("abc", 2)  == norep2("abc", 2))  //> there are 6 multiset 2-permutations for Queue((b,1), (a,1), (c,1)) = Queue(
                                                  //| ba, bc, ac, ab, cb, ca)
                                                  //| there are 6 2-permutations without replacement for Queue(a, b, c) = Queue(a
                                                  //| b, ac, bc, ba, ca, cb)
assert(multisets("abc", 3)  == norep2("abc", 3))  //> there are 6 multiset 3-permutations for Queue((b,1), (a,1), (c,1)) = Queue(
                                                  //| bac, bca, acb, abc, cba, cab)
                                                  //| there are 6 3-permutations without replacement for Queue(a, b, c) = Queue(a
                                                  //| bc, acb, bca, bac, cab, cba)

assert (multisets("aaab", 2) == multisets2("aaab".toList, 2) )
                                                  //> there are 3 multiset 2-permutations for Queue((b,1), (a,3)) = Queue(ba, ab,
                                                  //|  aa)
                                                  //| there are 3 multiset 2-permutations for Queue((b,1), (a,3)) = List(List(a, 
                                                  //| a), List(b, a), List(a, b))
multisets("aab", 2)                               //> there are 3 multiset 2-permutations for Queue((b,1), (a,2)) = Queue(ba, ab,
                                                  //|  aa)

multisets("aab", 3)                               //> there are 3 multiset 3-permutations for Queue((b,1), (a,2)) = Queue(baa, ab
                                                  //| a, aab)
norep2("aab", 3)                                  //> there are 6 3-permutations without replacement for Queue(a, a, b) = Queue(a
                                                  //| ab, aba, aba, aab, baa, baa)

As generalizaiton, you can obtain with/without replacements using multisets funciton. For instance,

作为概括,您可以使用多重集函数获得有/无替换。例如,

//take far more letters than resulting permutation length to emulate withReplacements
assert(multisets("aaaaabbbbbccccc", 3) == withReplacements("abc", 3))
                                                  //> there are 27 multiset 3-permutations for Queue((b,5), (a,5), (c,5)) = Queue
                                                  //| (bac, bab, baa, bcb, bca, bcc, bba, bbc, bbb, acb, aca, acc, aba, abc, abb,
                                                  //|  aac, aab, aaa, cba, cbc, cbb, cac, cab, caa, ccb, cca, ccc)
                                                  //| there are 27 3-permutations with replacement for abc = List(aaa, aab, aac, 
                                                  //| aba, abb, abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, 
                                                  //| caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc)


//take one letter of each to emulate withoutReplacements
assert(multisets("aaaaabbbbbccccc", 3) == noReplacements("abc", 3))
                                                  //> there are 27 multiset 3-permutations for Queue((b,5), (a,5), (c,5)) = Queue
                                                  //| (bac, bab, baa, bcb, bca, bcc, bba, bbc, bbb, acb, aca, acc, aba, abc, abb,
                                                  //|  aac, aab, aaa, cba, cbc, cbb, cac, cab, caa, ccb, cca, ccc)
                                                  //| there are 6 3-permutations without replacement for Queue(a, b, c) = Queue(c
                                                  //| ba, bca, acb, cab, bac, abc)

If you are more interested about permutations, you may look at

如果你对排列更感兴趣,你可以看看

回答by RajV

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

这是一个基于循环概念和具有两个元素的 permute 的简单实现的实现。它不处理 permute 方法中的重复和堆栈溢出方面

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)
}

回答by arun rajput

I got the following approach from SICP. It takes me fare amount of time to understand it. But it's worth and beautiful to see. How recursion works behind the scenes.

我从 SICP 得到了以下方法。我需要花费大量的时间来理解它。但值得一看,很漂亮。递归如何在幕后工作。

def permutations(list: List[Int]): List[List[Int]] = list match {
  case Nil => Nil
  case List(x) => List(List(x))
  case _ => list
    .flatMap(x => 
       permutations(list.filterNot(_==x))
       .map(p => x :: p))
}

The above solution can be translated in for loop as follows:

上面的解决方案可以翻译成for循环如下:

def perms(list: List[Int]): List[List[Int]] = {
  if (list.size == 1) List(list)
  else for {
    x <- list
    y <- perms(list.filterNot(_ == x))
  } yield x :: y
}