Ruby 资源效率-找到一个可能的数字组合以达到给定的总和

Ruby 资源效率-找到一个可能的数字组合以达到给定的总和,ruby,dynamic-programming,Ruby,Dynamic Programming,我在Ruby中使用这些算法: def subset_sum(inp, s) arr = [] loop.with_index{|_, i| arr << inp.combination(i+1).to_a.find{ |c| (c.inject(0) {|sum,x| sum + x}) == s}; break if !a.none? || i == inp.count } arr end subset_sum([1,

我在Ruby中使用这些算法:

  def subset_sum(inp, s)
        arr = []
        loop.with_index{|_, i| arr << inp.combination(i+1).to_a.find{
    |c| (c.inject(0) {|sum,x| sum + x})  == s}; break if !a.none? || i == inp.count }
    arr
  end

  subset_sum([1,2,3,4,5,6], 15)
不幸的是,这两条路径都是资源密集型的。如果我增加传入数组,计算将花费很长时间。告诉我,有没有解决这个问题的最佳Ruby方法?

def find\u one\u subset\u suminp,sum 候选项=inp.filter{| i | i0 散列[key]={candidates[key]=>[candidates[key]]} 如果哈希[key-1][sum].nil? 散列[key-1]。每个do | s,c| 散列[key][s]=c 新的_s=s+候选人[关键] 下一步除非散列[key][new_s].nil? hash[key][new_s]=c+[candidates[key]] 如果new_s==sum,则中断 终止 其他的 散列[key][sum]=散列[key-1][sum] 终止 散列[键] 其他的 散列[key]={candidates.first=>[candidates.first]} 终止 终止 sum_combi_hash[candidates.size-1][sum] 终止
我知道数组的所有元素都是非负的

尝试以下递归

def subset_sum((first, *rest), target)
  return (first == target ? [first] : nil) if rest.empty?
  rv = subset_sum(rest, target)
  return rv unless rv.nil?
  return nil if first > target
  return [first] if first == target
  rv = subset_sum(rest, target-first)
  rv.nil? ? nil : [first, *rv]
end
在第一个示例中,很快就找到了解决方案,但这只是因为数组的最后三个元素和为11。相反,当arr的元素被洗牌时,要生成arr1,需要相当长的时间来执行

为了了解发生了什么,让我们包含一些puts语句

def subset_sum((first, *rest), target)
  puts "first = #{first}, rest = #{rest}, target = #{target}"
  if rest.empty?
    puts "  rest is empty, return #{first == target ? [first] : nil}"        
    return first == target ? [first] : nil
  end 
  rv = subset_sum(rest, target)
  puts "  rv #{first} not used"
  puts "  return #{rv}" unless rv.nil?
  return rv unless rv.nil?
  return nil if first > target
  return [first] if first == target
  rv = subset_sum(rest, target-first)
  rv.nil? ? nil : [first, *rv]
end
总的来说,我不相信这会比使用Arraycombination快很多,但是可以做一些基准测试

使用动态规划可以获得进一步的改进。状态变量是数组前n个元素中使用的变量之和。例如,如果前四个元素是

a4 = [1,2,3,4]
那么1到4个元素的所有组合将是:

all = (1..4).each_with_object([]) do |n,a|
  a4.combination(n).each { |aa| a << aa.sum }
end
  #=> [1, 2, 3, 4, 3, 4, 5, 5, 6, 7, 6, 7, 8, 9, 10]
如果已知数组的值都是非负的,如果目标值是,比如说,6,那么状态变量的可能值将限制为以下值

all.uniq.select { |a| a <= 5 }
  #=> [1, 2, 3, 4, 5]
这将是有效的,a4的计算是根据a3的结果得出的,依此类推


如果需要,我可以演示如何编写动态编程解决方案。

将选择替换为检测或查找。当找到一个时,它会停止搜索。此外,.inject:+可以写为.sum,但不要期望加速。已知给定数组的元素是否为非负?Cary,非常感谢您的决定。这很有效。。。你的问题的答案是“是”。我正在葡萄树上构建一个简单的ATM,所以数组的所有元素都是非负的。我的解决方案很简单,但我以前从未使用过葡萄。这是我的“尝试”:我不想把你的时间浪费在我的愚蠢上。但是,如果您有几分钟的空闲时间,我将非常感谢您对代码的任何评论和选项。谢谢你,卡里斯沃夫兰,请看一看。该方法返回nil:subset_sum[1,1,1,1,2,2,5,5,5,5,5,5,10],6=>nil谢谢,这非常有效!
a4 = [1,2,3,4]
all = (1..4).each_with_object([]) do |n,a|
  a4.combination(n).each { |aa| a << aa.sum }
end
  #=> [1, 2, 3, 4, 3, 4, 5, 5, 6, 7, 6, 7, 8, 9, 10]
all_uniq = all.uniq
  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
all.uniq.select { |a| a <= 5 }
  #=> [1, 2, 3, 4, 5]