尝试学习一点Scala并遇到了这个问题。我找到了一个没有重复here的所有组合的解决方案,我有点理解它背后的想法,但是一些语法让我搞砸了。我也不认为该解决方案适用于重复的情况。我想知道是否有人可以建议我可以使用的一些代码。我有很多关于组合学的材料,并且理解问题和迭代解决方案,我只是在寻找scala-y的方法。
由于
答案 0 :(得分:7)
我现在明白你的问题。我认为实现目标的最简单方法是执行以下操作:
def mycomb[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(el <- l;
sl <- mycomb(n-1, l dropWhile { _ != el } ))
yield el :: sl
}
def comb[T](n: Int, l: List[T]): List[List[T]] = mycomb(n, l.removeDuplicates)
comb
方法只调用mycomb
,并从输入列表中删除重复项。删除重复项意味着以后更容易测试两个元素是否“相同”。我对mycomb
方法所做的唯一更改是,当递归调用该方法时,我会删除列表中el
之前出现的元素。这是为了阻止输出中出现重复。
> comb(3, List(1,2,3))
> List[List[Int]] = List(
List(1, 1, 1), List(1, 1, 2), List(1, 1, 3), List(1, 2, 2),
List(1, 2, 3), List(1, 3, 3), List(2, 2, 2), List(2, 2, 3),
List(2, 3, 3), List(3, 3, 3))
> comb(6, List(1,2,1,2,1,2,1,2,1,2))
> List[List[Int]] = List(
List(1, 1, 1, 1, 1, 1), List(1, 1, 1, 1, 1, 2), List(1, 1, 1, 1, 2, 2),
List(1, 1, 1, 2, 2, 2), List(1, 1, 2, 2, 2, 2), List(1, 2, 2, 2, 2, 2),
List(2, 2, 2, 2, 2, 2))
答案 1 :(得分:5)
同时,组合已成为scala集合的组成部分:
scala> val li = List (1, 1, 0, 0)
li: List[Int] = List(1, 1, 0, 0)
scala> li.combinations (2) .toList
res210: List[List[Int]] = List(List(1, 1), List(1, 0), List(0, 0))
正如我们所看到的,它不允许重复,但允许它们很简单但是组合:枚举集合的每个元素(0到li.size-1)并映射到列表中的元素:
scala> (0 to li.length-1).combinations (2).toList .map (v=>(li(v(0)), li(v(1))))
res214: List[(Int, Int)] = List((1,1), (1,0), (1,0), (1,0), (1,0), (0,0))
答案 2 :(得分:1)
这个问题在其中一个答案中得到了改写 - 我希望问题本身也会被编辑。其他人回答了正确的问题。我会在下面留下这些代码,以防有人发现它有用。
这个解决方案确实令人困惑。没有重复的“组合”称为排列。它可以是这样的:
def perm[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(el <- l;
sl <- perm(n-1, l filter (_ != el)))
yield el :: sl
}
如果输入列表不能保证包含唯一元素,如另一个答案所示,则可能会有点困难。我们需要删除第一个元素,而不是删除所有元素的过滤器。
def perm[T](n: Int, l: List[T]): List[List[T]] = {
def perm1[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(el <- l;
(hd, tl) = l span (_ != el);
sl <- perm(n-1, hd ::: tl.tail))
yield el :: sl
}
perm1(n, l).removeDuplicates
}
只是一点解释。在for中,我们获取列表中的每个元素,并返回由它组成的列表,然后是列表中除所选元素之外的所有元素的排列。
例如,如果我们采用List(1,2,3),我们将组成由1和perm(List(2,3)),2和perm(List(1,3))和3形成的列表和烫发(清单(1,2))。
由于我们正在进行任意大小的排列,因此我们会跟踪每个子变换的时间长度。如果subpermutation的大小为0,重要的是我们返回一个包含空列表的列表。请注意,这不是一个空列表!如果我们在0的情况下返回Nil,那么在调用perm中就没有sl的元素,并且整个“for”将产生Nil。这样,sl将被分配为Nil,我们将组成一个列表el :: Nil,产生List(el)。
我正在考虑原始问题,我会在此处发布我的解决方案以供参考。如果由于输入中的重复元素而导致答案中没有重复元素,只需添加removeDuplicates,如下所示。
def comb[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(i <- (0 to (l.size - n)).toList;
l1 = l.drop(i);
sl <- comb(n-1, l1.tail))
yield l1.head :: sl
}
我知道这有点难看。我必须使用toList将范围(由“to”返回)转换为List,以便“for”本身返回List。我可以取消“l1”,但我认为这更清楚我正在做什么。由于此处没有过滤器,因此修改它以删除重复项要容易得多:
def comb[T](n: Int, l: List[T]): List[List[T]] = {
def comb1[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(i <- (0 to (l.size - n)).toList;
l1 = l.drop(i);
sl <- comb(n-1, l1.tail))
yield l1.head :: sl
}
comb1(n, l).removeDuplicates
}
答案 3 :(得分:1)
我在博客中写了一个类似问题的解决方案:http://gabrielsw.blogspot.com/2009/05/my-take-on-99-problems-in-scala-23-to.html
首先,我想到生成所有可能的组合并删除重复项,(或使用集合,负责复制本身),但由于问题是通过列表指定的,所有可能的组合太多,我'我想出了一个问题的递归解决方案:
获取大小为n的组合,取集合中的一个元素并将其附加到剩余元素大小为n-1的所有组合,将其余元素的大小为n的组合合并。 这就是代码的作用
//P26
def combinations[A](n:Int, xs:List[A]):List[List[A]]={
def lift[A](xs:List[A]):List[List[A]]=xs.foldLeft(List[List[A]]())((ys,y)=>(List(y)::ys))
(n,xs) match {
case (1,ys)=> lift(ys)
case (i,xs) if (i==xs.size) => xs::Nil
case (i,ys)=> combinations(i-1,ys.tail).map(zs=>ys.head::zs):::combinations(i,ys.tail)
}
}
如何阅读:
我必须创建一个辅助功能,将列表“提升”到列表列表
逻辑在匹配语句中:
如果你想要列表元素大小为1的所有组合,只需创建一个列表列表,其中每个子列表包含原始元素的元素(即“提升”功能)
如果组合是列表的总长度,只需返回一个列表,其中唯一的元素是元素列表(只有一个可能的组合!)
否则,取下列表的头部和尾部,计算尾部大小n-1的所有组合(递归调用),并将头部附加到每个结果列表中(.map(ys.head ::) zs))将结果与列表尾部的大小n的所有组合(另一个递归调用)连接起来
有意义吗?
答案 4 :(得分:0)
丹尼尔 - 我不确定亚历克斯的副本是什么意思,可能以下内容提供了更合适的答案:
def perm[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(el <- l.removeDuplicates;
sl <- perm(n-1, l.slice(0, l.findIndexOf {_ == el}) ++ l.slice(1 + l.findIndexOf {_ == el}, l.size)))
yield el :: sl
}
以
运行perm(2, List(1,2,2,2,1))
这给出了:
List(List(2, 2), List(2, 1), List(1, 2), List(1, 1))
而不是:
List(
List(1, 2), List(1, 2), List(1, 2), List(2, 1),
List(2, 1), List(2, 1), List(2, 1), List(2, 1),
List(2, 1), List(1, 2), List(1, 2), List(1, 2)
)
嵌套的perm调用中的肮脏是从列表中删除一个'el',我想有一个更好的方法可以做到这一点,但我想不出一个。
答案 5 :(得分:0)
此解决方案已发布在Rosetta代码:http://rosettacode.org/wiki/Combinations_with_repetitions#Scala
上def comb[A](as: List[A], k: Int): List[List[A]] =
(List.fill(k)(as)).flatten.combinations(k).toList
答案 6 :(得分:0)
目前还不清楚你要求的是什么。它可能是一些不同的东西之一。首先是列表中不同元素的简单组合。 Scala使用集合中的combinations()
方法提供。如果元素是不同的,那么行为正是您对“组合”的经典定义的期望。对于p元素的n元素组合,将存在p!/ n!(p-n)!输出中的组合。
如果列表中有重复的元素,Scala将生成组合中出现多次的项目组合。但只是不同的可能组合,元素可能会复制多次,因为它们存在于输入中。它只生成一组可能的组合,因此重复元素,但不重复组合。我不确定它是否存在实际的Set
的迭代器。
现在,如果我理解的话,你实际意味着来自给定的一组不同p元素的组合,其中一个元素可以在组合中重复出现n次。
好吧,回过头来,当输入中有重复的元素时生成组合,并且你想看到输出中重复的组合,那么它的方法就是通过“暴力”生成它使用n个嵌套循环。请注意,它实际上并没有什么粗野,它只是组合的自然数,实际上,对于小n来说是O(p ^ n),并且你无能为力。你应该小心选择这些值,如下所示:
val a = List(1,1,2,3,4)
def comb = for (i <- 0 until a.size - 1; j <- i+1 until a.size) yield (a(i), a(j))
导致
scala> comb
res55: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (1,2), (1,3), (1,4), (1,2), (1,3), (1,4), (2,3), (2,4), (3,4))
通过首先创建0 until a.size
的中间组合为(i,j)...
现在要创建“重复组合”,你只需改变这样的索引:
val a = List('A','B','C')
def comb = for (i <- 0 until a.size; j <- i until a.size) yield (a(i), a(j))
将产生
List((A,A), (A,B), (A,C), (B,B), (B,C), (C,C))
但我不确定将此概括为更大组合的最佳方式是什么。
现在,当我发现这篇文章时,我接近我正在寻找的东西:一个从包含重复元素的输入生成组合的函数,以及由combinations()
生成的中间索引。很高兴这个方法产生一个列表而不是一个元组,所以这意味着我们可以使用“地图地图”来实际解决问题,这是我不确定其他人在这里提出的,但这非常漂亮,看到它后,会让你对FP和Scala的爱变得更多!
def comb[N](p:Seq[N], n:Int) = (0 until p.size).combinations(n) map { _ map p }
结果
scala> val a = List('A','A','B','C')
scala> comb(a, 2).toList
res60: List[scala.collection.immutable.IndexedSeq[Int]] = List(Vector(1, 1), Vector(1, 2), Vector(1, 3), Vector(1, 2), Vector(1, 3), Vector(2, 3))