元素的组合

时间:2014-08-02 22:01:25

标签: scala

问题:

给定Seq seqInt 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))
}

你对这个问题有什么看法?这种方法将被称为很多,因此应该最有效。

库中有一些方法,如subsetscombinations(我选择了相同的名称),它们返回迭代器。我也考虑过这一点,但我还不知道如何设计这个懒惰。

2 个答案:

答案 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

的数组