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背包分支和绑定的主要内容,如果未能解决你的问题,请参考以下文章

分支定界法改良背包

背包分支定界错误结果

C# 分支定界法 01背包问题

算法---分支限定0/1背包--蚁群算法

分支定界背包 Java

0-1背包问题之分支界限法