如何创建从多个阵列中选择的单个元素的每个组合?

时间:2014-03-07 06:05:42

标签: ruby arrays combinatorics

我有5个阵列:

["A", "B", "C"]
["A", "B", "C", "D", "E"]
["A"]
["A", "B", "C", "D", "E", "F"]
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]

我想创建一个每个组合的列表:

["AAAAA","AAAAB","AAAAC", "AAAAD"...
 "BAAAA","BAAAB","BAAAC", "BAAAD"...]

2 个答案:

答案 0 :(得分:14)

a = [
  ["A", "B", "C"],
  ["A", "B", "C", "D", "E"],
  ["A"],
  ["A", "B", "C", "D", "E", "F"],
  ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]
]

a.inject(&:product).map(&:join)
# => ["AAAAA", "AAAAB", "AAAAC", ..., "CEAFM", "CEAFN", "CEAFO"]

感谢bluexuemei的改进答案。原始解决方案是a.shift.product(*a).map(&:join)


更传统的解决方案

有了这么方便的图书馆,这些红宝石单行似乎就像是作弊。

这是解决这个常见问题的一种更传统的方法,可以很容易地编码到其他编程语言中:

N = a.reduce(1) { |product,list| product * list.size } # 1350

combinations = []
0.upto(N-1) do |q|
  combo = []
  a.reverse.each do |list|
    q, r = q.divmod list.size
    combo << list[r]
  end
  combinations.push combo.reverse.join
end
combinations
# => ["AAAAA", "AAAAB", "AAAAC", ..., "CEAFM", "CEAFN", "CEAFO"]

基本思想是首先计算组合总数N,这只是所有列表长度的乘积。从0N-1的每个整数然后编码为每个列表提供唯一索引以生成每个组合所需的所有信息。考虑它的一种方法是索引变量q可以表示为5位数字,其中每个数字位于不同的基数中,其中基数是相应列表的大小。也就是说,第一个数字是base-3,第二个数字是base-5,第三个数字是base-1(总是0),第4个是base-6,第5个是base-15。要从q中提取这些值,这只是采用一系列重复的整数除法和余数,就像在内循环中所做的那样。当然,这需要一些功课,或许可以看一些简单的例子来完全消化。

答案 1 :(得分:1)

a.reduce(&:product).map(&:join).size