Ruby 最佳选择是在数组b中的偏移量0和数组c中的偏移量3处选择散列,总(最大)值为42。我已经在表格中列出了这个选择

Ruby 最佳选择是在数组b中的偏移量0和数组c中的偏移量3处选择散列,总(最大)值为42。我已经在表格中列出了这个选择,ruby,algorithm,optimization,knapsack-problem,Ruby,Algorithm,Optimization,Knapsack Problem,同样的结果适用于18-23范围内的任何剩余预算。将对其他每个可用预算范围进行类似的计算。当可用预算为24时,您将看到一个平局 现在我们可以转到数组a。(我们快完成了。) 我没有包括偏移量0处的散列,因为它主要由偏移量2处的散列控制 阵列a、b和c的可用预算为30,因此我们不必以此为条件(即第一个阵列)。我们必须考虑以下三种可能性: 选择偏移量1处的散列值以获得20,这将为数组b和c留下30-9=>21的剩余预算,这(从数组b的表中)为最后两个数组生成42的最佳值,对于总值20+42=>62

同样的结果适用于
18-23范围内的任何剩余预算。将对其他每个可用预算范围进行类似的计算。当可用预算为
24
时,您将看到一个平局

现在我们可以转到数组
a
。(我们快完成了。)

我没有包括偏移量
0
处的散列,因为它主要由偏移量
2
处的散列控制

阵列
a
b
c
的可用预算为
30
,因此我们不必以此为条件(即第一个阵列)。我们必须考虑以下三种可能性:

  • 选择偏移量
    1
    处的散列值以获得
    20
    ,这将为数组
    b
    c
    留下
    30-9=>21
    的剩余预算,这(从数组
    b
    的表中)为最后两个数组生成
    42
    的最佳值,对于总值
    20+42=>62

  • 选择偏移量
    2
    处的散列值以获得
    22
    ,这将为数组
    b
    c
    留下
    30-10=>20
    的剩余预算,这(从数组
    b
    的表中)为最后两个数组生成
    42
    的最佳值,对于总值
    22+42=>64

  • 选择偏移量
    3
    处的散列值以获得
    10
    ,这将为数组
    b
    c
    留下
    30-2=>28的剩余预算,这(从数组
    b
    的表中)为最后两个数组生成
    44
    的最佳值,对于总值
    10+44=>54

因此,我们得出结论,通过选择数组
a
中偏移量
2
处的散列、数组
b
中偏移量
0
处的散列和数组
c
中偏移量
2
处的散列,可以实现
64
的最大值

代码

def knapsack(all_costs_and_values, total_budget)
  costs_and_values = remove_dominated_choices(all_costs_and_values)
  budget = remaining_budget(costs_and_values, total_budget)
  solution = optimize(costs_and_values, budget)
  display_solution(costs_and_values, solution)
end

示例

def knapsack(all_costs_and_values, total_budget)
  costs_and_values = remove_dominated_choices(all_costs_and_values)
  budget = remaining_budget(costs_and_values, total_budget)
  solution = optimize(costs_and_values, budget)
  display_solution(costs_and_values, solution)
end
我将
所有成本和值的数据结构更改为哈希数组,其中键是成本/值对的标签(例如,
'12'
指OP数组中行偏移量
1
、列偏移量
2
处的哈希)。我希望标签被更有意义的字符串替换

all_costs_and_values =
  [{ '00' => { cost: 10, value: 20 }, '01' => { cost:  8, value: 17 },
     '02' => { cost: 10, value: 20 }, '03' => { cost: 12, value: 24 } },
   { '10' => { cost:  6, value: 16 }, '11' => { cost:  4, value: 14},
     '12' => { cost:  6, value: 17 }, '13' => { cost:  5, value: 13 } },
   { '20' => { cost:  8, value: 16 }, '21' => { cost: 14, value: 30 },
     '22' => { cost: 16, value: 32 }, '23' => { cost: 14, value: 32 } },
   { '30' => { cost:  2, value:  4 }, '31' => { cost:  5, value:  9 },
     '32' => { cost: 10, value: 16 }, '33' => { cost:  6, value:  8 } }]

total_budget = 30

knapsack(all_costs_and_values, total_budget)
  #=> Optimal value: 70
  #     Best choice for hash 0: 01
  #     Best choice for hash 1: 12
  #     Best choice for hash 2: 23
  #     Best choice for hash 3: 30
在计算最佳值时,将构造散列
解决方案
数组:

solution
  #=> [{  30=>{:name=>"01", :value_onward=>70}},
  #                   "01" => { cost:  8, value: 17 } leave 30-8 = 22
  #=> {14-15=>{:name=>"11", :value_onward=>34},
  #=>     16=>{:name=>"12", :value_onward=>37},
  #=>  17-18=>{:name=>"11", :value_onward=>39},
  #=>     19=>{:name=>"12", :value_onward=>42},
  #=>  20-21=>{:name=>"11", :value_onward=>50},
  #=>     22=>{:name=>"12", :value_onward=>53}},
  #                   "12" => { cost:  6, value: 17 } leave 22-6 = 16
  #=> {10-12=>{:name=>"20", :value_onward=>20},
  #=>  13-15=>{:name=>"20", :value_onward=>25},
  #=>  16-18=>{:name=>"23", :value_onward=>36},
  #                   "23" => { cost: 14, value: 32 } leave 16-14 = 2
  #=>   {2-4=>{:name=>"30", :value_onward=>4},
  #                   "30" => { cost:  2, value:  4 } leave 2-2 = 0
  #=>    5-9=>{:name=>"31", :value_onward=>9},
  #=>     10=>{:name=>"32", :value_onward=>16}}]

多少是“多个阵列”?有一些简单的方法可以解决这个问题,当在大型数据集上使用时,这些方法的性能可能会很差。8个数组,可能每个数组最多50个哈希。性能不是一个大问题,因为这只需要每天运行一次。这是包装问题的一个变种。你应该把这当作一个算法问题来问,或者在数学网站上问,你会得到更多的答案。嗯,我正在准备一个采用动态规划的解决方案,它可以有效地用于任意数量的哈希数组(即,不只是三个)。不过,我今晚没时间完成。卡里,那太好了。也可以说,如果使用一个对象,从所有哈希的结果集中删除所有相同的对象,这样每个使用的对象都是彼此唯一的吗?感谢您的回复,也许您可以进一步帮助我。。。我的数组实际上是对象的数组,我只是以散列为例,让它更容易理解。当我尝试运行上面的代码时,我得到了错误“ObjectName没有隐式转换为哈希”。是否可以将其修改为适用于对象数组?很抱歉第一次漏掉了这个细节。嗯,这改变了很多。我试着优化迭代,这样所有的和都可以通过merge一次性计算出来;不要用OBEJCT。看看@MarkThomas answer,它应该更接近你想要的;我们正在寻找。@BroiSatse到目前为止给出的两个答案都是暴力解决方案。有可能用非暴力的方式来做吗?它是NP不完全的吗?这和BroiSatse的区别是先进行排序还是先进行过滤,我认为先进行过滤比使用BroiSatse更有效。在你的回答中,你是在对那些会被丢弃的元素进行排序。就我个人而言,我认为这是可读性上的差异。我想让商业规则更清晰。顺序可以改变;我没有考虑效率。而且,这没有@MajorMajor不想要的哈希依赖。我唯一试图避免的是迭代两次来计算总和。不幸的是,我同意它的可读性要差得多。您仍然可以通过更改
sort\u by
select
的顺序来加快解决方案的速度。我更改了顺序,还注意到另一个改进:我将
sort\u替换为。最后一次
最大值
a.product(b,c)
 .select{ |arr| arr.reduce(0) { |sum,h| sum + h[:cost] } < 30 }
 .max_by{ |arr| arr.reduce(0) { |sum,h| sum + h[:value] } } 
[{budget: 3,  value: 10, hash_offset: 3 }, 
 {budget: 4,  value: 10, hash_offset: 3 },
 ...
 {budget: 8,  value: 10, hash_offset: 3 },
 {budget: 9,  value: 20, hash_offset: 1 },
 {budget: 10, value: 22, hash_offset: 2 },
 ...
 {budget: 10, value: 22, hash_offset: 2 }]
def knapsack(all_costs_and_values, total_budget)
  costs_and_values = remove_dominated_choices(all_costs_and_values)
  budget = remaining_budget(costs_and_values, total_budget)
  solution = optimize(costs_and_values, budget)
  display_solution(costs_and_values, solution)
end
private

def remove_dominated_choices(a)    
  a.map do |f|
    # f.invert.invert ensures that no two keys have the same value
    g = f.invert.invert
    g.each_with_object({}) do |(name,v),h|
      (h[name] = v) unless g.any? { |_,p| 
        (p[:cost] <= v[:cost] && p[:value] >  v[:value]) ||
        (p[:cost] <  v[:cost] && p[:value] >= v[:value]) }
    end
  end
end

def remaining_budget(b, tot_budget)
  mc = min_cost_per_hash(b)
  b.map.with_index do |h,i|
    if i.zero?
      (tot_budget..tot_budget)
    else
      (mc[i..-1].reduce(:+)..tot_budget - mc[0..i-1].reduce(:+))
    end
  end
end

def min_cost_per_hash(arr)
  arr.map { |h| h.values.min_by { |h| h[:cost] }[:cost] }
end
def optimize(costs_and_values,remaining_budget)
  solution = Array.new(costs_and_values.size)
  i = costs_and_values.size-1
  g = costs_and_values[i].sort_by { |k,v| -v[:cost] }
  solution[i] = remaining_budget[i].each_with_object({}) do |rb,h|
    name, f = g.find { |_,v| v[:cost] <= rb }  
    h[rb] = { name: name, value_onward: f[:value] }
  end
  while i > 0  
    i -= 1
    g = costs_and_values[i].sort_by { |k,v| v[:cost] }
    min_to_leave = remaining_budget[i+1].first    
    solution[i] = remaining_budget[i].each_with_object({}) do |rb,h|
      best = - Float::INFINITY
      g.each do |name, v|
        leave_for_next = rb - v[:cost]
        break if leave_for_next < min_to_leave
        candidate = v[:value] + solution[i+1][leave_for_next][:value_onward] 
        if candidate > best
          best = candidate
          h[rb] = { name: name, value_onward: candidate }
        end
      end
    end
  end
  solution      
end
def display_solution(costs_and_values, solution)
  rv = solution.first.keys.first
  puts "Optimal value: #{ solution.first[rv][:value_onward] }\n"
  solution.each_with_index do |h,i|
    name =  h[rv][:name]
    puts "  Best choice for hash #{i}: #{name}"
    rv -= costs_and_values[i][name][:cost]
  end
end
all_costs_and_values =
  [{ '00' => { cost: 10, value: 20 }, '01' => { cost:  8, value: 17 },
     '02' => { cost: 10, value: 20 }, '03' => { cost: 12, value: 24 } },
   { '10' => { cost:  6, value: 16 }, '11' => { cost:  4, value: 14},
     '12' => { cost:  6, value: 17 }, '13' => { cost:  5, value: 13 } },
   { '20' => { cost:  8, value: 16 }, '21' => { cost: 14, value: 30 },
     '22' => { cost: 16, value: 32 }, '23' => { cost: 14, value: 32 } },
   { '30' => { cost:  2, value:  4 }, '31' => { cost:  5, value:  9 },
     '32' => { cost: 10, value: 16 }, '33' => { cost:  6, value:  8 } }]

total_budget = 30

knapsack(all_costs_and_values, total_budget)
  #=> Optimal value: 70
  #     Best choice for hash 0: 01
  #     Best choice for hash 1: 12
  #     Best choice for hash 2: 23
  #     Best choice for hash 3: 30
solution
  #=> [{  30=>{:name=>"01", :value_onward=>70}},
  #                   "01" => { cost:  8, value: 17 } leave 30-8 = 22
  #=> {14-15=>{:name=>"11", :value_onward=>34},
  #=>     16=>{:name=>"12", :value_onward=>37},
  #=>  17-18=>{:name=>"11", :value_onward=>39},
  #=>     19=>{:name=>"12", :value_onward=>42},
  #=>  20-21=>{:name=>"11", :value_onward=>50},
  #=>     22=>{:name=>"12", :value_onward=>53}},
  #                   "12" => { cost:  6, value: 17 } leave 22-6 = 16
  #=> {10-12=>{:name=>"20", :value_onward=>20},
  #=>  13-15=>{:name=>"20", :value_onward=>25},
  #=>  16-18=>{:name=>"23", :value_onward=>36},
  #                   "23" => { cost: 14, value: 32 } leave 16-14 = 2
  #=>   {2-4=>{:name=>"30", :value_onward=>4},
  #                   "30" => { cost:  2, value:  4 } leave 2-2 = 0
  #=>    5-9=>{:name=>"31", :value_onward=>9},
  #=>     10=>{:name=>"32", :value_onward=>16}}]