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}}]