从= =指定数量的数组生成组合?

时间:2014-06-05 17:11:04

标签: ruby arrays algorithm

我需要从[{1}}获取所有可能的数字组合,其等于denom_arr

amt

这种情况会产生:

  1. denom_arr = [4,3,1] amt = 10
  2. [4, 4, 1, 1]
  3. [3, 3, 3, 1]
  4. [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
  5. [4, 3, 1, 1, 1]
  6. 。 。 。 (其他情况......)
  7. 问题是我写的代码在[4, 3, 3]后破解了,我不知道如何让它循环到同一个索引以获取案例1-3

    4-6+

    更新

    我知道这可以通过使用memoization / dynamic编程技术进行递归来完成,但我试图在循环中严格执行此操作以便测试理论。

1 个答案:

答案 0 :(得分:5)

我会递归地执行此操作

def possible_sums(arr, amt)
  return [[]] if amt == 0
  return []   if amt < 0

  arr.reduce([]) do |sums, e|
    sums.concat(
      possible_sums(arr, amt-e)
        .map { |sum| sum.unshift(e).sort }
    )
  end.uniq
end

p possible_sums([4,3,1], 10)
  # => [
  #      [1, 1, 4, 4], [3, 3, 4], [1, 1, 1, 3, 4], [1, 1, 1, 1, 1, 1, 4], 
  #      [1, 3, 3, 3], [1, 1, 1, 1, 3, 3], [1, 1, 1, 1, 1, 1, 1, 3], 
  #      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
  #    ]

虽然这可能是低效的,因为它重复工作,但这可以通过使用动态编程(基本上,记忆递归函数的结果)来缓解。

UPDATE 以下是一个迭代解决方案:

def possible_sums_it(arr, amt)
  sums = Array.new(amt+1) { [] }
  sums[0] << []

  (1..amt).each do |i|
    arr.each do |e|
      if i-e >= 0
        sums[i].concat(
          sums[i-e].map { |s| [e, *s].sort }
        )
      end
    end

    sums[i].uniq!
  end

  sums[amt]
end

这实际上是问题的动态编程算法。

因此,如果你恰到好处地眯着眼睛,你会看到它正在做的事情,就是计算0amt数组sums之前的所有可能总和使用什么基本上是递归算法,但我们在sums中查找我们事先计算过的值,而不是递归调用。

这是有效的,因为我们知道sums[i] sums[j]之前我们不需要j < i