问题描述
我使用 Branch and Bound 尝试了给定 的实现来解决背包问题。
该解决方案看起来不错,但它没有给出最终选择的项目以达到最佳值。 有没有办法通过最小程度地更改以下代码来实现这一点?
import sys
def bound(vw,v,w,idx):
if idx >= len(vw) or w > limit:
return -1
else:
while idx < len(vw) and w + vw[idx][1] <= limit:
v,idx = v + vw[idx][0],w + vw[idx][1],idx + 1
if idx < len(vw):
v += (limit - w) * vw[idx][0] / (vw[idx][1] * 1.0)
return v
def knapsack(vw,limit,curValue,curWeight,curIndex):
global maxValue
if bound(vw,curIndex) >= maxValue:
if curWeight + vw[curIndex][1] <= limit:
maxValue = max(maxValue,curValue + vw[curIndex][0])
knapsack(vw,curValue + vw[curIndex][0],curWeight + vw[curIndex][1],curIndex + 1)
if curIndex < len(vw) - 1:
knapsack(vw,curIndex + 1)
return maxValue
maxValue = 0
if __name__ == '__main__':
with open(sys.argv[1] if len(sys.argv) > 1 else sys.exit(1)) as f:
n,limit = map(int,f.readline().split())
vw = []
taken = n * [0]
for ln in f.readlines():
vl,wl = map(int,ln.split())
vw.append([vl,wl,vl / (wl * 1.0)])
print(knapsack(sorted(vw,key=lambda x: x[2],reverse=True),0))
print(taken)
4 11
8 4
15 8
4 3
10 5
我期待如下结果
19
0 1 1 0
我编写了自己的 here,它提供了上述所需的输出,但是对于像这样的大问题 implementation
解决方法
我稍微重新组织了提供的代码,然后添加了跟踪最佳选择的逻辑。由于您希望该选择是一个由 0 和 1 组成的列表,因此我为它使用了一个位图(一个大整数),并且每个项目都在该位图中分配了一个位。
这是它的样子:
from collections import namedtuple
Item = namedtuple("Item","value,weight,bit")
def knapsack(items,limit):
maxValue = 0
bestTaken = 0
def bound(value,index):
if index >= len(items) or weight > limit:
return -1
else:
item = items[index]
while weight + item.weight <= limit:
value,index = value + item.value,weight + item.weight,index + 1
if index >= len(items):
return value
item = items[index]
else:
return value + (limit - weight) * item.value / item.weight
def recur(taken,value,index):
nonlocal maxValue,bestTaken
if maxValue < value:
maxValue = value
bestTaken = taken
if index < len(items) and bound(value,index) >= maxValue:
item = items[index]
if weight + item.weight <= limit:
recur(taken | item.bit,value + item.value,index + 1)
recur(taken,index + 1)
# Add bit mask for each item:
items = [Item(*item,1 << index) for index,item in enumerate(items)]
items.sort(key=lambda item: -item.value / item.weight)
recur(0,0)
return maxValue,('{:0{width}b}').format(bestTaken,width=len(items))[::-1]
if __name__ == '__main__':
# Demo input
s = """4 11
8 4
15 8
4 3
10 5"""
lines = s.splitlines(False)
_,limit = map(int,lines.pop(0).split())
items = [tuple(map(int,line.split())) for line in lines]
value,bits = knapsack(items,limit)
print("Maximised value:",value)
print("Item selection:",bits)