给定尺寸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的数组,因此我想返回所有组合。请务必注意,每个子阵列中只有一个项目可以在结果中。(例如,"完成的个人资料"也不能以"未完成的个人资料"的结果出现(

背景:

用户可能有一些任务:例如,"完整配置文件"或"设置电子邮件"或其他任何任务。这些任务可以像这样表示:

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

,无论如何,使用数组#产品,但是就像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的组合。

虽然提供的答案很棒,而乔尔格(Jörg's(是我接受的答案,但我想分享一个答案,即我的一个朋友为我提供的答案,以防其他人遇到这个问题。从本质上讲,这是Jörg的答案,但深度更深。

笛卡尔产品您要要求的称为笛卡尔产品,它已经与数组#产品一起内置。

例如:

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

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

您可以给予数组#产品任何数量的数组:

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

特别感谢Thinkbot的Joel Quenneville,以及提供答案的其他人。

最新更新