我有几个哈希数组(让我们说我有三个)如下:
a = [{ cost: 10, value: 20},
{ cost: 9, value: 20},
{ cost: 10, value: 22},
{ cost: 2, value: 10}
]
b = [{ cost: 4, value: 20},
{ cost: 9, value: 20},
{ cost: 15, value: 22},
{ cost: 12, value: 10}
]
c = [{ cost: 10, value: 21},
{ cost: 9, value: 20},
{ cost: 10, value: 22},
{ cost: 3, value: 10}
]
我需要找出哪个哈希值,每个数组中的一个,给出了:value
的最大总和,将:cost
的总和保持在给定值之下(让我们说{ {1}})。
这个案例的答案可能很容易看到它,但这只是示例数据,实际上我有更多的数组。有人可以帮助我或指出我正确的方向吗?
编辑:我还要提一下,我想要处理这个对象数组。我使用哈希作为一个例子,因为它更容易描述,但我计划使用对象。另外,如果从一个数组中使用了一个Object,我就不喜欢从其他数组中使用相同的选项,因此每个选定的Object都应该是唯一的。
答案 0 :(得分:3)
这是一个单行:
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]} }
分手:
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] } }
答案 1 :(得分:2)
这将是:
def find_max_option(max_cost, *arys)
a = arys.shift
a.product(*arys).map do |c|
[ c, c.inject({}) {|result, hash|
result.merge(hash) {|_,o,n| o + n }
}
]
end.select {|_,v| v[:cost] < max_cost}.max_by {|_,v| v[:value]}.first
end
find_max_option(30, a, b, c) #=> [{:cost=>10, :value=>22}, {:cost=>4, :value=>20}, {:cost=>10, :value=>22}]
<小时/> 由sawa编辑为了便于阅读,稍作重写。
def find_max_option(max_cost, *a)
a.first.product(*a.drop(1))
.group_by{|hs| hs.inject({}){|acc, h| acc.merge(h){|_, v_acc, v_h| v_acc + v_h}}}
.select{|k, _| k[:cost] < max_cost}
.max_by{|k, _| k[:value]}
.last.first
end
答案 2 :(得分:1)
我们可以使用动态编程来解决这个&#34;背包问题&#34;有效地用于任何数量的数组(散列)。解决方案时间大致与数组的数量和平均数组大小的平方成正比。
我的第一个想法是对此进行编码,但随后决定这将花费太长时间,并且可能不是解释算法如何工作的最佳方式。相反,我决定展示如何针对问题中给出的示例计算最优解。应该明白如何为任意数量的阵列实现该算法。
[编辑:我决定对此进行编码。我在最后添加了 tidE]
首先考虑(&#34;最后&#34;)数组c
。这是来自Excel电子表格的相关信息的屏幕截图。
请注意,我未在偏移0
,{ cost: 10, value: 21}
中包含哈希值。这是因为哈希是&#34; domninated&#34;由偏移2
,{ cost: 10, value: 22}
的那个。前者永远不会是首选,因为它们都具有相同的成本,而后者具有更高的价值。
第二个表格显示了给定&#34;剩余预算&#34; (即,在选择a
和b
中的哈希之后)。请注意,可能的剩余预算范围从3
(数组c
中的哈希值中的最低费用)到24
(总预算30
减去选择a
和b
中具有最小费用的哈希:30 - 2 - 4 = 24
)。
最好用一组哈希值来实现它:
[{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 }]
现在让我们转到数组b
。
这里我只包含数组b
中包含的四个哈希中的两个,因为偏移1
的哈希,{ cost: 9, value: 20 }
由偏移{{0}处的哈希支配。 1}},0
和哈希{ cost: 4, value: 20 }
由{ cost: 12, value: 10}
支配。
这里有&#34;可用预算&#34;范围从{ cost: 4, value: 20}
到7
。 27
提供了在7
中选择一个哈希的最低要求(偏移b
,成本为0
),4
中的一个哈希值c
成本3
)。范围的高端3
是选择27
中的哈希值后可以保留的最大值(a
)。
假设剩余预算(对于数组30 - 3
和b
)为c
。如果我们要在偏移18
处选择哈希值,我们会从该哈希值中实现0
的值,并且剩余预算为20
,用于选择数组{{1}中的哈希值}}。从数组18 - 4 => 14
的表中我们看到,我们将选择数组c
中偏移量c
的哈希值,其值为2
。因此,如果可用预算为c
,我们将为22
的数组b
和c
提供(最大)值,并在偏移20 + 22 => 42
处选择哈希值在数组18
。
我们现在必须考虑选择数组0
中偏移量为2的哈希值,这将产生b
的值并留下b
的剩余预算用于选择数组中的哈希值22
。我们从表中看到数组18 - 15 => 3
,它将是偏移3处的散列,值为c
。因此,如果可用预算为c
,我们将为10
的数组b
和c
提供(最大)值,并在偏移22 + 10 => 32
处选择哈希值在数组18
。
如果我们要为数组2
和b
提供18
的可用预算,那么最佳选择是在偏移b
处选择哈希值。数组c
和数组0
中偏移b
的数组3
的总(最大)值。我在表格中概述了这一选择。
同样的结果适用于c
范围内的任何剩余预算。将对每个其他可用预算范围执行类似的计算。当可用预算为42
时,您会发现有一个平局。
我们现在可以转到数组18 - 23
。 (我们差不多完成了。)
我没有在偏移24
处包含散列,因为它由偏移a
处的散列支配。
数组0
,2
和a
的可用预算为b
,因此我们不必考虑(即第一个数组)。我们必须考虑三种可能性中的每一种:
选择偏移c
处的哈希值,以获得30
的值,这会为数组1
和{{1}留下20
的剩余预算},(来自数组30 - 9 => 21
的表)为最后两个数组生成b
的最佳值,总值为c
。
选择偏移b
处的哈希值,以获得42
的值,这会为数组20 + 42 => 62
和{{1}留下2
的剩余预算},(来自数组22
的表)为最后两个数组生成30 - 10 => 20
的最佳值,总值为b
。
选择偏移c
处的哈希值,以获得b
的值,这会为数组42
和{{1}留下22 + 42 => 64
的剩余预算},(来自数组3
的表)为最后两个数组生成10
的最佳值,总值为30 - 2 => 28
。
因此,我们得出结论,通过选择数组b
中偏移c
处的散列,数组{{b
处的散列,可以获得44
的最大值。{ 1}}和数组10 + 44 => 54
中偏移量64
的哈希值。
<强>代码强>
2
a
0
。
b
示例强>
我将2
的数据结构更改为哈希数组,其中键是成本/值对的标签(例如,c
指的是以前是行偏移的哈希{{ 1}},OP的数组中的列偏移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