2

我正在研究S-99:九十九个 Scala 问题并且已经停留在问题 26 上。 生成从列表的 N 个元素中选择的 K 个不同对象的组合。 浪费了几个小时后,我决定看一下用 Haskell 编写的解决方案:

combinations :: Int -> [a] -> [[a]]
combinations 0 _  = [ [] ]
combinations n xs = [ y:ys | y:xs' <- tails xs
                       , ys <- combinations (n-1) xs']

它看起来很简单,所以我决定翻译成 Scala。(我知道这是作弊。)这是我到目前为止得到的:

def combinations[T](n: Int, ls: List[T]): List[List[T]] = (n, ls) match {
case (0, _) => List[List[T]]()
case (n, xs) => {
  for {
    y :: xss <- allTails(xs).reverse
    ys <- combinations((n - 1), xss)
  } yield y :: ys
}
} 

我的辅助功能:

def allTails[T](ls: List[T]): List[List[T]] = {
ls./:(0, List[List[T]]())((acc, c) => {
  (acc._1 + 1, ls.drop(acc._1) :: acc._2)
})._2 }

allTails(List(0, 1, 2, 3)).reverse              
//> res1: List[List[Int]] = List(List(0, 1, 2, 3), List(1, 2, 3), List(2, 3), List(3))

但是,我的组合返回一个空列表。任何的想法?其他带有解释的解决方案也非常受欢迎。谢谢

编辑:问题的描述

生成从列表的 N 个元素中选择的 K 个不同对象的组合。从 12 个人中选出一个 3 人的委员会有几种方法?我们都知道有 C(12,3) = 220 种可能性(C(N,K) 表示众所周知的二项式系数)。对于纯数学家来说,这个结果可能很棒。但我们想真正产生所有的可能性。

示例: scala> combination(3, List('a, 'b, 'c, 'd, 'e, 'f)) res0: List[List[Symbol]] = List(List('a, 'b, ' c), 列表('a, 'b, 'd), 列表('a, 'b, 'e), ...

4

4 回答 4

2

正如诺亚指出的那样,我的问题是for空列表不会产生。然而,Noah 建议的 hacky 工作是错误的。它将一个空列表添加到每个递归步骤的结果中。无论如何,这是我的最终解决方案。我将基本情况更改为“case (1, xs)”。(n 匹配 1)

def combinations[T](n: Int, ls: List[T]): List[List[T]] = (n, ls) match {
case (1, xs) =>  xs.map(List(_))
case (n, xs) => {
  val tails = allTails(xs).reverse
  for {
    y :: xss <- allTails(xs).reverse
    ys <- combinations((n - 1), xss)
  } yield y :: ys
}
}
//combinations(3, List(1, 2, 3, 4))
//List(List(1, 2, 3), List(1, 2, 4), List(1, 3, 4), List(2, 3, 4))
//combinations(2, List(0, 1, 2, 3))
//List(List(0, 1), List(0, 2), List(0, 3), List(1, 2), List(1, 3), List(2, 3))

def allTails[T](ls: List[T]): List[List[T]] = {
ls./:(0, List[List[T]]())((acc, c) => {
  (acc._1 + 1, ls.drop(acc._1) :: acc._2)
})._2
}
//allTails(List(0,1,2,3)) 
//List(List(3), List(2, 3), List(1, 2, 3), List(0, 1, 2, 3))
于 2013-06-08T16:27:52.923 回答
2

您在这里翻译 Haskell 版本时犯了一个错误:

case (0, _) => List[List[T]]()

这将返回一个空列表。而 Haskell 版本

combinations 0 _  = [ [] ]

返回一个包含单个元素的列表,并且该元素是一个空列表。

这本质上是说有一种方法可以选择零项,这很重要,因为对于我们选择更多项的情况,代码递归地构建在这种情况下。如果没有办法选择零项,那么也没有办法选择一项,以此类推。这就是您的代码中发生的事情。

如果您将 Scala 版本修复为与 Haskell 版本相同:

case (0, _) => List(List[T]())

它按预期工作。

于 2013-06-08T16:40:32.340 回答
1

您的问题是使用for列表的理解。如果for检测到一个空列表,那么它会短路并返回一个空列表,而不是“cons”你的 head 元素。这是一个例子:

scala> for { xs <- List() } yield println("It worked!") // This never prints
res0: List[Unit] = List()

因此,针对您的组合功能的一种 hacky 工作将是:

  def combinations[T](n: Int, ls: List[T]): List[List[T]] = (n, ls) match {
    case (0, _) => List[List[T]]()
    case (n, xs) => {
      val tails = allTails(xs).reverse
      println(tails)
      for {
        y :: xss <- tails
        ys <- Nil :: combinations((n - 1), xss) //Now we're sure to keep evaulating even with an empty list
      } yield y :: ys
    }
  }

scala> combinations(2, List(1, 2, 3))
List(List(1, 2, 3), List(2, 3), List(3))
List(List(2, 3), List(3))
List(List(3))
List()
res5: List[List[Int]] = List(List(1), List(1, 2), List(1, 3), List(2), List(2, 3), List(3))
于 2013-06-07T20:53:48.223 回答
0

另一种解决方法。

def combinations[T](n: Int, ls: List[T]): List[List[T]] = {
var ms: List[List[T]] = List[List[T]]();
val len = ls.size
if (n > len)
  throw new Error();
else if (n == len)
  List(ls)
else if (n == 1)
  ls map (a => List(a))
else {
  for (i <- n to len) {
    val take: List[T] = ls take i;
    val temp = combinations(n - 1, take.init) map (a => take.last :: a)
    ms = ms ::: temp
  }
  ms
}
}

所以combinations(2, List(1, 2, 3))给出:List[List[Int]] = List(List(2, 1), List(3, 1), List(3, 2))

于 2013-06-08T10:22:35.640 回答