再次使用Haskell解决方案(因为它更容易使用惰性列表,因为它们是内置的):
combinations 0 _ = [[]]
combinations k [] = []
combinations k (x:xs) = map (x:) (combinations (k-1) xs) ++ combinations k xs
第一和第二种情况是由
二项式系数的性质导出的,更具体地说:对于所有
n
,包括
n = 0
,有
n choose 0 = 1
(这就是为什么要先处理
0 choose 0
的原因)。另一个是
0 choose k = 0
。第三个方程式是组合的递归定义的确切翻译。
不幸的是,当你将它应用到无限列表上时,它会返回一个平凡的解:
> take 10 $ combinations 3 [1..]
[[1,2,3],[1,2,4],[1,2,5],[1,2,6],[1,2,7],[1,2,8],[1,2,9],[1,2,10],[1,2,11],[1,2,12]]
编辑:好的,所以我们真正想要在有限步骤内遍历每个组合。使用上述版本时,我们显然仅使用左侧的表达式 ++
生成以1开头的组合。我们可以通过定义一个有趣的列表压缩函数来解决这个问题,该函数通过交替选择其参数列表的每个头部(在第二个参数中重要的是非严格)来构建列表:
merge [] ys = ys
merge (x:xs) ys = x:merge ys xs
使用它代替++
:
combinations k (x:xs) = map (x:) (combinations (k-1) xs) `merge` combinations k xs
让我们看看:
> let comb_10_3 = combinations 3 [1..10]
> let comb_inf_3 = combinations 3 [1..]
> take 10 comb_inf_3
[[1,2,3],[2,3,4],[1,3,4],[3,4,5],[1,2,4],[2,4,5],[1,4,5],[4,5,6],[1,2,5],[2,3,5]]
> comb_10_3 `intersect` comb_inf_3 == comb_10_3
True
> last $ combinations 3 [1..10]
[6,8,10]
> elemIndex [6,8,10] $ combinations 3 [1..]
Just 351
所有的10 choose 3
组合都在这里!