给定一个大小为y的数组,包含大小为n的数组,如何使用Ruby返回所有逻辑组合?

我想要做的是处理n组,而我下面提供的代码恰好适用于4组。

def show_combinations @combos = [] ['A', 'no A'].each do |a| ['B', 'no B'].each do |b| ['C', 'no C'].each do |c| ['D', 'no D'].each do |d| @combos << [a, b, c, d] end end end end end 

我如何重构以下代码来处理以下场景:鉴于我有一个大小为y的数组包含大小为n的数组,我想返回所有组合。 重要的是要注意每个子arrays中只有一个项目可以在结果中。 (例如“已完成的配置文件”也不能出现在“未完成配置文件”的结果中)

背景:

用户可能有一些任务:例如,“完成配置文件”或“设置电子邮件”等等。 这些任务可以表示如下:

 @task_states = [["Completed Profile, NOT Completed Profile"], ["Set up Email", "NOT set up Email"]] 

然后,将@task_states传递给方法,结果应为:

 [ ["Completed Profile", "Set up Email"], ["Completed Profile", "NOT set up Email"], ["NOT Completed Profile", "Set up Email"], ["NOT Completed Profile", "NOT Set up Email"] ] 

所以表示所有组合的数组数组。 显然,“已完成的配置文件”也不能与“未完成的配置文件”等在同一个数组中。

谢谢!

看起来你想要计算数组的笛卡尔积。 计算笛卡尔积的方法(不太令人惊讶)称为Array#product

 @task_states.first.product(*@task_states.drop(1)) 

所以,例如:

 ['A', 'no A'].product(['B', 'no B'], ['C', 'no C'], ['D', 'no D']) #=> [[ "A", "B", "C", "D"], # [ "A", "B", "C", "no D"], # [ "A", "B", "no C", "D"], # [ "A", "B", "no C", "no D"], # [ "A", "no B", "C", "D"], # [ "A", "no B", "C", "no D"], # [ "A", "no B", "no C", "D"], # [ "A", "no B", "no C", "no D"], # ["no A", "B", "C", "D"], # ["no A", "B", "C", "no D"], # ["no A", "B", "no C", "D"], # ["no A", "B", "no C", "no D"], # ["no A", "no B", "C", "D"], # ["no A", "no B", "C", "no D"], # ["no A", "no B", "no C", "D"], # ["no A", "no B", "no C", "no D"]] 

无论如何,使用Array#产品 ,但在Ruby的情况下,还有其他方法。 这是两个。

 arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 

使用math来计算每个arr.size**arr.first.size组合

 def cartesian_product(arr) n, m = arr.size, arr.first.size (0..n**m-1).map do |x| (n-1).downto(0).map do |i| x, r = x.divmod(m) arr[i][r] end.reverse end end cartesian_product arr #=> [[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 5, 7], [1, 5, 8], [1, 5, 9], # [1, 6, 7], [1, 6, 8], [1, 6, 9], # [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 5, 7], [2, 5, 8], [2, 5, 9], # [2, 6, 7], [2, 6, 8], [2, 6, 9], # [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 5, 7], [3, 5, 8], [3, 5, 9], # [3, 6, 7], [3, 6, 8], [3, 6, 9]] 

使用递归

 def cartesian_product(arr) first, *rest = arr return first.map { |o| [o] } if rest.empty? c = cartesian_product(rest) first.flat_map { |o| c.map { |a| [o] + a } } end cartesian_product arr #=> same as above 

在此应用程序中, arr每个元素(数组)都不包含重复项。 在其他情况下,可能存在重复项(并且返回的产品不包含重复项),应首先计算

 arr_without_dups = arr.map(&:uniq) 

然后从arr_without_dups计算组合。

虽然提供的答案很棒,而Jörg是我接受的答案,但我想分享我的一位朋友提供给我的答案,以防其他人遇到这个问题。 基本上这是Jörg的答案,但更深入一点。

笛卡尔积您所要求的是笛卡尔积 ,它已经与Array#产品一起内置。

例如:

 [0, 1].product([0, 1]) # => [[0, 0], [0, 1], [1, 0], [1, 1]] 

请注意,这为我们提供了所有可能的2位数字,从00-11开始

您可以为Array#产品提供任意数量的数组:

 [0, 1].product([0, 1], [0, 1]) # => [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] 

请注意,这为000-111提供了所有可能的3位数字。

你的问题看看你确切的问题:

 ["A", "no A"].product(["B", "no B"], ["C", "no C"], ["D", "no D"]) => [["A", "B", "C", "D"], ["A", "B", "C", "no D"], ["A", "B", "no C", "D"], ["A", "B", "no C", "no D"], ["A", "no B", "C", "D"], ["A", "no B", "C", "no D"], ["A", "no B", "no C", "D"], ["A", "no B", "no C", "no D"], ["no A", "B", "C", "D"], ["no A", "B", "C", "no D"], ["no A", "B", "no C", "D"], ["no A", "B", "no C", "no D"], ["no A", "no B", "C", "D"], ["no A", "no B", "C", "no D"], ["no A", "no B", "no C", "D"], ["no A", "no B", "no C", "no D"]] gives you all the possible combinations from "ABCD" to "noA noB noC noD" 

通用解决方案通过利用splat *运算符,我们可以使用任何通用的数组数组。

 def combinations(arrays) first, *rest = arrays first.product(*rest) end 

然后我们可以说:

 arrays_to_combine = [["A", "no A"], ["B", "no B"], ["C", "no C"], ["D", "no D"]] combinations(arrays_to_combine) => [["A", "B", "C", "D"], ["A", "B", "C", "no D"], ["A", "B", "no C", "D"], ["A", "B", "no C", "no D"], ["A", "no B", "C", "D"], ["A", "no B", "C", "no D"], ["A", "no B", "no C", "D"], ["A", "no B", "no C", "no D"], ["no A", "B", "C", "D"], ["no A", "B", "C", "no D"], ["no A", "B", "no C", "D"], ["no A", "B", "no C", "no D"], ["no A", "no B", "C", "D"], ["no A", "no B", "C", "no D"], ["no A", "no B", "no C", "D"], ["no A", "no B", "no C", "no D"]] 

特别感谢Thoughtbot的 Joel Quenneville帮助我以及其他提供答案的人。