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

时间:2017-05-02 21:16:07

标签: arrays 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的数组,我想返回所有组合。重要的是要注意每个子数组中只有一个项可以在结果中。 (例如&#34;已完成的个人资料&#34;也可以在&#34;未完成的个人资料&#34;中显示结果;)

背景:

用户可能有一些任务:例如,&#34;完成个人资料&#34;或者&#34;设置电子邮件&#34;管他呢。这些任务可以这样表示:

@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"]
]

所以表示所有组合的数组数组。显然&#34;完成的配置文件&#34;也不能和#34;未完成的配置文件在同一个数组中,&#34;等

谢谢!

3 个答案:

答案 0 :(得分:9)

看起来你想要计算数组的笛卡尔积。计算笛卡尔积的方法(不太令人惊讶)称为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"]]

答案 1 :(得分:1)

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

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

使用数学计算每个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计算组合。

答案 2 :(得分:1)

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

笛卡儿产品 您要求的内容称为Cartesian Product,它已经内置Array#product

例如:

[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]]

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

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

["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帮助我以及其他提供答案的人。