Python背包分支和绑定
Posted
技术标签:
【中文标题】Python背包分支和绑定【英文标题】:Python Knapsack Branch and Bound 【发布时间】:2014-03-10 20:20:53 【问题描述】:我已经花了一周的时间来研究这个背包问题的分支和绑定代码,并且我查看了许多关于这个主题的文章和书籍。但是,当我运行我的代码时,我没有得到我期望的结果。从文本文件接收输入,例如:
12
4 1
1 1
3 2
2 3
其中第一行是容量,随后的每一行都是值/权重对。我使用这个文件得到的结果是“8”而不是“10”(除非我弄错了,所有物品都不适合放在背包里)。这是我的代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import Queue
from collections import namedtuple
Item = namedtuple("Item", ['index', 'value', 'weight', 'level', 'bound', 'contains'])
class Node:
def __init__(self, level, value, weight, bound, contains):
self.level = level
self.value = value
self.weight = weight
self.bound = bound
self.contains = contains
def upper_bound(u, k, n, v, w):
if u.weight > k:
return 0
else:
bound = u.value
wt = u.weight
j = u.level + 1
while j < n and wt + w[j] <= k:
bound += v[j]
wt += w[j]
j += 1
# fill knapsack with fraction of a remaining item
if j < n:
bound += (k - wt) * (v[j] / w[j])
return bound
def knapsack(items, capacity):
item_count = len(items)
v = [0]*item_count
w = [0]*item_count
# sort items by value to weight ratio
items = sorted(items, key=lambda k: k.value/k.weight, reverse = True)
for i,item in enumerate(items, 0):
v[i] = int(item.value)
w[i] = int(item.weight)
q = Queue.Queue()
root = Node(0, 0, 0, 0.0, [])
root.bound = upper_bound(root, capacity, item_count, v, w)
q.put(root)
value = 0
taken = [0]*item_count
best = set()
while not q.empty():
c = q.get()
if c.bound > value:
level = c.level+1
# check 'left' node (if item is added to knapsack)
left = Node(c.value + v[level], c.weight + w[level], level, 0.0, c.contains[:])
left.contains.append(level)
if left.weight <= capacity and left.value > value:
value = left.value
best |= set(left.contains)
left.bound = upper_bound(left, capacity, item_count, v, w)
if left.bound > value:
q.put(left)
# check 'right' node (if items is not added to knapsack)
right = Node(c.value, c.weight, level, 0.0, c.contains[:])
right.contains.append(level)
right.bound = upper_bound(right, capacity, item_count, v, w)
if right.bound > value:
q.put(right)
for b in best:
taken[b] = 1
value = sum([i*j for (i,j) in zip(v,taken)])
return str(value)
我的索引关闭了吗?我没有正确遍历树或计算边界吗?
【问题讨论】:
你可能想看看rosettacode.org/wiki/Knapsack_Problem/Python 我正在努力将背包算法应用于包含 10,000 多个项目的数据集。我成功地在较小的集合上实现了 DP 背包,但在某些时候内存成为问题,这就是我切换到分支定界方法的原因。 所以我用bound += v[j]
替换了bound += (k - wt) * (v[j] / w[j])
;显然前者导致算法过早停止。当我初始化左右节点(现在已修复)时,我也有一些向后的变量。现在我被困在标记哪些物品被拿走了。在较大的集合中,我得到[0,1,3,...]
或[0,2,4,...]
的模式。 'contains' 数组的更新/修改位置是否正确?
【参考方案1】:
def upper_bound(u, k, n, v, w):
if u.weight > k:
return 0
else:
bound = u.value
wt = u.weight
j = u.level
while j < n and wt + w[j] <= k:
bound += v[j]
wt += w[j]
j += 1
# fill knapsack with fraction of a remaining item
if j < n:
bound += (k - wt) * float(v[j])/ w[j]
return bound
def knapsack(items, capacity):
item_count = len(items)
v = [0]*item_count
w = [0]*item_count
# sort items by value to weight ratio
items = sorted(items, key=lambda k: float(k.value)/k.weight, reverse = True)
for i,item in enumerate(items, 0):
v[i] = int(item.value)
w[i] = int(item.weight)
q = Queue.Queue()
root = Node(0, 0, 0, 0.0,[])
root.bound = upper_bound(root, capacity, item_count, v, w)
q.put(root)
value = 0
taken = [0]*item_count
best = set()
while not q.empty():
c = q.get()
if c.bound > value:
level = c.level+1
# check 'left' node (if item is added to knapsack)
left = Node(level,c.value + v[level-1], c.weight + w[level-1], 0.0, c.contains[:])
left.bound = upper_bound(left, capacity, item_count, v, w)
left.contains.append(level)
if left.weight <= capacity:
if left.value > value:
value = left.value
best = set(left.contains)
if left.bound > value:
q.put(left)
# check 'right' node (if items is not added to knapsack)
right = Node(level,c.value, c.weight, 0.0, c.contains[:])
right.bound = upper_bound(right, capacity, item_count, v, w)
if right.weight <= capacity:
if right.value > value:
value = right.value
best = set(right.contains)
if right.bound > value:
q.put(right)
for b in best:
taken[b-1] = 1
value = sum([i*j for (i,j) in zip(v,taken)])
return str(value)
【讨论】:
【参考方案2】:我认为您只想在不拿该物品的情况下计算界限。如果您拿走了该物品,则意味着您的界限仍然可以实现。如果你不这样做,你必须重新调整你的期望。
【讨论】:
P.S.很高兴你参加 Coursera 的离散优化课程 :)【参考方案3】:import functools
class solver():
def __init__(self, Items, capacity):
self.sortedItems = list(filter(lambda x: x.value > 0, Items))
self.sortedItems = sorted(self.sortedItems, key=lambda x:float(x.weight)/float(x.value))
self.numItems = len(Items)
self.capacity = capacity
self.bestSolution = solution(0, self.capacity)
def isOptimisitcBetter(self, sol, newItemIdx):
newItem = self.sortedItems[newItemIdx]
rhs = (sol.value + (sol.capacity/newItem.weight)*newItem.value)
return rhs > self.bestSolution.value
def explore(self, sol, itemIndex):
if itemIndex < self.numItems:
if self.isOptimisitcBetter(sol, itemIndex):
self.exploreLeft(sol, itemIndex)
self.exploreRight(sol, itemIndex)
def exploreLeft(self, sol, itemIndex):
newItem = self.sortedItems[itemIndex]
thisSol = sol.copy()
if thisSol.addItem(newItem):
if thisSol.value > self.bestSolution.value:
self.bestSolution = thisSol
self.explore(thisSol, itemIndex+1)
def exploreRight(self, sol, itemIndex):
self.explore(sol, itemIndex+1)
def solveWrapper(self):
self.explore(solution(0, self.capacity), 0)
class solution():
def __init__(self, value, capacity, items=set()):
self.value, self.capacity = value, capacity
self.items = items.copy()
def copy(self):
return solution(self.value, self.capacity, self.items)
def addItem(self, newItem):
remainingCap = self.capacity-newItem.weight
if remainingCap < 0:
return False
self.items.add(newItem)
self.capacity = remainingCap
self.value+=newItem.value
return True
solver = solver(items, capacity)
solver.solveWrapper()
bestSol = solver.bestSolution
【讨论】:
以上是关于Python背包分支和绑定的主要内容,如果未能解决你的问题,请参考以下文章