问题:
给定Seq
seq
和Int
n
。
我基本上希望元素的所有组合最大大小为n。安排重要,意味着例如[1,2]与[2,1]不同。
def combinations[T](seq: Seq[T], size: Int) = ...
示例:
combinations(List(1,2,3), 0)
//Seq(Seq())
combinations(List(1,2,3), 1)
//Seq(Seq(), Seq(1), Seq(2), Seq(3))
combinations(List(1,2,3), 2)
//Seq(Seq(), Seq(1), Seq(2), Seq(3), Seq(1,2), Seq(2,1), Seq(1,3), Seq(3,1),
//Seq(2,3), Seq(3,2))
...
到目前为止我有什么:
def combinations[T](seq: Seq[T], size: Int) = {
@tailrec
def inner(seq: Seq[T], soFar: Seq[Seq[T]]): Seq[Seq[T]] = seq match {
case head +: tail => inner(tail, soFar ++ {
val insertList = Seq(head)
for {
comb <- soFar
if comb.size < size
index <- 0 to comb.size
} yield comb.patch(index, insertList, 0)
})
case _ => soFar
}
inner(seq, IndexedSeq(IndexedSeq.empty))
}
你对这个问题有什么看法?这种方法将被称为很多,因此应该最有效。
库中有一些方法,如subsets
或combinations
(我选择了相同的名称),它们返回迭代器。我也考虑过这一点,但我还不知道如何设计这个懒惰。
答案 0 :(得分:6)
不确定这是否足够有效,但这是最简单的方法。
def combinations[T](seq: Seq[T], size: Int) : Seq[Seq[T]] = {
(1 to size).flatMap(i => seq.combinations(i).flatMap(_.permutations))
}
编辑: 为了使它变得懒惰你可以使用视图
def combinations[T](seq: Seq[T], size: Int) : Iterable[Seq[T]] = {
(1 to size).view.flatMap(i => seq.combinations(i).flatMap(_.permutations))
}
答案 1 :(得分:3)
从排列理论我们知道从一组N个元素中取出的K个元素的排列数是
N! / (N - K)!
(见http://en.wikipedia.org/wiki/Permutation)
因此,如果您想要全部构建它们,您将拥有
algorithm complexity = number of permutations * cost of building each permutation
该算法的潜在优化在于通过使用在appending
中运行的具有prepending
/ O(1)
运算的数据结构来最小化构建每个排列的成本。
您正在使用IndexedSeq
,这是针对O(1)随机访问优化的集合。当集合针对随机访问进行优化时,它们由数组支持。当使用这样的集合时(这对java ArrayList
也有效),你放弃了低成本插入操作的保证:有时阵列不够大,集合必须创建一个新集合并复制所有元素。
当使用linked lists
(例如scala List
,这是Seq的默认实现)时,您采取相反的选择:您放弃恒定时间访问以进行恒定时间插入。特别是,scala List
是一个递归数据结构,在前面有恒定的时间插入。
因此,如果您正在寻找高性能并且需要热切地使用该集合,请使用Seq.empty
而不是IndexedSeq.empty
,并且在每次迭代时将新元素添加到{{{ 1}}。如果您需要懒惰的东西,请使用Seq
,这将最大限度地减少内存占用。值得探索的其他策略是为您的第一次迭代创建一个空的IndexedSeq,但不是通过Indexed.empty创建。请使用构建器并尝试提供具有正确大小Stream