在Scala中枚举置换的代码
我编写了一个函数来枚举给定列表的所有排列。你觉得下面的代码怎么样在Scala中枚举置换的代码,scala,permutation,Scala,Permutation,我编写了一个函数来枚举给定列表的所有排列。你觉得下面的代码怎么样 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
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
}
}
我认为标准库中已经存在这样一个函数:。那么,为什么要重新发明轮子呢?给定一个Seq,通过调用
置换
方法就可以进行置换
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)
此外,还有一种用于组合的方法:
scala> List(1,2,3).combinations(2).mkString("\n")
res4: String =
List(1, 2)
List(1, 3)
List(2, 3)
关于你的实施,我想说三件事:
(1) 很好看
(2) 提供迭代器(这是允许丢弃元素的std集合方法)。否则,您可以获得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)
考虑一下这里的区别:你的版本
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)
参考版本:
scala> List(1,1,2).permutations foreach println
List(1, 1, 2)
List(1, 2, 1)
List(2, 1, 1)
我猜您正在练习Scala编程技能。这是另一个,其思想是将不同的元素作为序列头,并通过
过滤器删除重复。由于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
}
}
也许这条线已经饱和了,但我想我应该把我的溶液加入其中:
假设没有重复元素:
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
}
def permList(l:List[Int]):List[List[Int]]=l匹配{
案例列表(ele)=>列表(列表(ele))
案例列表=>
为了{
我
为了{
我认为我的解决方案比其他的好
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)
这是相同的3行代码,但支持可变排列长度,并且消除了列表串联
我使第二个更具表意性(这样可以防止累加器的平面图合并,这也使它更具有尾部递归性),并扩展到多集排列,这样你就可以说“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)
作为generalizaiton,您可以使用multisets function获得带/不带替换的结果。例如
//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)
这是一个基于的版本
def perms[T](xs:List[T]):List[List[T]=xs匹配{
案例列表()=>列表(xs)
(xdef置换器[T](list:list[T]):list[list[T]={
定义p(总计:整数,列表:列表[T]):列表[T]={
如果(总计==0){
//递归树的结尾
list.map(list(u2;))
}否则{
//受控组合
//总数>0时爆炸
for(i_p)返回3*3列表(列表(A,A)。。。
//total=2=>\u p返回3*3*3元素列表(列表(A,A,A)。。。
}
}
_p(列表长度-1,列表)
}
置换器(列表(“A”、“B”、“C”))
//输出:
列表(A,A,A),列表(A,A,B),列表(A,A,C),列表(A,B,A),列表(A,B,B),
列表(A,B,C),列表(A,C,A),列表(A,C,B),列表(A,C,C),列表(B,A,A),
列表(B,A,B),列表(B,A,C),列表(B,B,A),列表(B,B,B),列表(B,B,C),
列表(B,C,A),列表(B,C,B),列表(B,C,C),列表(C,A,A),列表(C,A,B),
列表(C,A,C),列表(C,B,A),列表(C,B,B),列表(C,B,C),列表(C,C,C,A),
列表(C,C,B),列表(C,C,C)
这是一个基于循环概念的实现,是一个带有两个元素的置换的简单实现。
它不处理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)
}
我从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))
}
上述解决方案可转换为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
}
def perms(list:list[Int]):list[list[Int]={
如果(list.size==1)列表(list)
其他的{
x
我的灵感来源于下图所示的想法,图中显示了从空(图根)到所有置换(图叶)构建部分置换图:这可能是codereview.SE。@Raphael我不得不用谷歌搜索它,所以这里是给懒惰的人看的,我认为OP适合这样做。人们需要看看其他人在改进Scala编程的过程中会如何处理一些问题,这里的排列。@Icn,但堆栈溢出的目的是回答有关编程的特定问题。这里有这里没有问题。这篇文章的定义是代码回顾——“你觉得下面的代码怎么样?”它最适合CR。但是迁移它太晚了。哇。如果在没有阅读/理解文档的情况下使用,参考实现可能会破坏算法。通常,您会期望x.permutations.size==faculty(x.size)
。请解释答案,仅代码的答案没有多大帮助。相信我,SICP是理解递归和函数编程的最佳书籍。练习问题令人兴奋。如果有人(像我一样)希望在置换(n)
中有一个n
参数(用于置换多少个),您可以改为使用组合(n).flatMap(u.permutations)
。
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)
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)
}
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))
}
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
}
def perms(in:List[Int]):List[List[Int]] = {
def perms0(in: List[Int], tmp: List[Int]): List[List[Int]] =
if (in.isEmpty) List(tmp)
else in.foldLeft(Nil: List[List[Int]])((acc, el) => perms0(in.filter(en => en != el) , el :: tmp) ++ acc)
perms0(in, Nil)
}