【问题标题】:Finding all permutations of numbers plucked from an array which sum to 16查找从总和为 16 的数组中提取的数字的所有排列
【发布时间】:2017-07-05 13:31:49
【问题描述】:

我想找出从 [2,3,4,5,6,7,8] 中抽取 3、4 或 5 个数字的所有排列,允许重复,使得它们的总和为 16。所以 [8, 5,3]、[8,3,5] 和 [4,3,3,3,3] 是有效的排列。还应删除循环排列,因此 [3,3,3,3,4] 也不会添加到答案中。 我可以在 Ruby 中做到这一点,而不允许像这样重复:

d = [2,3,4,5,6,7,8]
number_of_divisions = [3,4,5]
number_of_divisions.collect do |n|
  d.permutation(n).to_a.reject do |p|
    p[0..n].inject(0) { |sum,x| sum + x } != 16
  end
end

如何允许重复以包含 [3,3,3,3,4]?

【问题讨论】:

标签: arrays ruby circular-permutations


【解决方案1】:

对于所有排列,包括重复,可以使用Array#repeated_permutation

d = [2,3,4,5,6,7,8]
number_of_divisions = [3,4,5]
number_of_divisions.flat_map do |n|
  d.repeated_permutation(n).reject do |p| # no need `to_a`
    p.inject(:+) != 16
  end
end

或者,Array#repeated_combination 会更好:

number_of_divisions.flat_map do |n|
  d.repeated_combination(n).reject do |p| # no need `to_a`
    p.inject(:+) != 16
  end
end

【讨论】:

  • 您可以避免使用map(&:sort)uniq,而改用repeated_combination
  • repeated_permutationrepeated_combination 只是门票!谢谢大家
  • @iceツ 确实如此。更新了答案。
  • 再想一想... OP 显然对排列感兴趣(位置很重要,即 [8,3,5][8,5,3] 有效),但您的答案转换为组合,即 [8,3,5][8,5,3]被认为是相同的。所以最好留下你原来的答案,除了删除mapuniq
  • 使用d = [2,3,4,5]; number_of_divisions = [3] 和所需的总和12(而不是16),两个sn-ps 都返回[[2, 5, 5], [3, 4, 5], [4, 4, 4]],缺少[3,5,4]。在#1 中,问题是sort 后跟uniq 删除了太多,而在#2 中,repeated_combination 将返回数组a,这样如果i < jd[i] 将始终在d[j] 之前repeated_combination 返回的元素包含d 的这两个元素。例如,如果d 已排序,则所有组合都将排序。
【解决方案2】:

重复组合比重复排列要少得多,所以让我们找到总和为给定值的重复组合,然后排列每个组合。此外,通过在每个计算步骤中应用uniq,我们可以显着减少所考虑的重复组合和排列的数量。

代码

require 'set'

def rep_perms_for_all(arr, n_arr, tot)
  n_arr.flat_map { |n| rep_perms_for_1(arr, n, tot) }
end

def rep_perms_for_1(arr, n, tot)
   rep_combs_to_rep_perms(rep_combs_for_1(arr, n, tot)).uniq
end

def rep_combs_for_1(arr, n, tot)
  arr.repeated_combination(n).uniq.select { |c| c.sum == tot }
end

def rep_combs_to_rep_perms(combs)
  combs.flat_map { |c| comb_to_perms(c) }.uniq
end

def comb_to_perms(comb)
  comb.permutation(comb.size).uniq.uniq do |p| 
    p.size.times.with_object(Set.new) { |i,s| s << p.rotate(i) } 
  end
end

示例

rep_perms_for_all([2,3,4,5], [3], 12)
  #=> [[2, 5, 5], [3, 4, 5], [3, 5, 4], [4, 4, 4]]
rep_perms_for_all([2,3,4,5,6,7,8], [3,4,5], 16).size
  #=> 93
rep_perms_for_all([2,3,4,5,6,7,8], [3,4,5], 16)
  #=> [[2, 6, 8], [2, 8, 6], [2, 7, 7], [3, 5, 8], [3, 8, 5], [3, 6, 7],
  #    [3, 7, 6], [4, 4, 8], [4, 5, 7], [4, 7, 5], [4, 6, 6], [5, 5, 6],
  #    [2, 2, 4, 8], [2, 2, 8, 4], [2, 4, 2, 8], [2, 2, 5, 7], [2, 2, 7, 5],
  #    [2, 5, 2, 7], [2, 2, 6, 6], [2, 6, 2, 6], [2, 3, 3, 8], [2, 3, 8, 3], 
  #    ...
  #    [3, 3, 3, 7], [3, 3, 4, 6], [3, 3, 6, 4], [3, 4, 3, 6], [3, 3, 5, 5], 
  #    [3, 5, 3, 5], [3, 4, 4, 5], [3, 4, 5, 4], [3, 5, 4, 4], [4, 4, 4, 4],
  #    ...
  #    [2, 2, 4, 5, 3], [2, 2, 5, 3, 4], [2, 2, 5, 4, 3], [2, 3, 2, 4, 5],
  #    [2, 3, 2, 5, 4], [2, 3, 4, 2, 5], [2, 3, 5, 2, 4], [2, 4, 2, 5, 3],
  #    ...
  #    [2, 5, 3, 3, 3], [2, 3, 3, 4, 4], [2, 3, 4, 3, 4], [2, 3, 4, 4, 3],
  #    [2, 4, 3, 3, 4], [2, 4, 3, 4, 3], [2, 4, 4, 3, 3], [3, 3, 3, 3, 4]]

说明

rep_combs_for_1 使用方法Enumerable#sum,它在Ruby v2.4 中首次亮相。对于早期版本,请使用 c.reduce(:0) == tot

comb_to_perms 中,第一个uniq 只是删除重复项。第二个uniq 带有一个块,除了可以通过旋转任何其他p-1 元素获得的p.size 元素(数组)之外的所有元素(数组)。例如,

p = [1,2,3]
p.size.times.with_object(Set.new) { |i,s| s << p.rotate(i) }
  #=> #<Set: {[1, 2, 3], [2, 3, 1], [3, 1, 2]}> 

【讨论】:

  • 这很有趣。我想知道以前的解决方案是否包含太少的结果,但找不到丢失的结果。
猜你喜欢
  • 2014-05-30
  • 2015-12-17
  • 2020-07-31
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多