简体   繁体   English

Python背包分支和绑定

[英]Python Knapsack Branch and Bound

I have spent a week working on this branch and bound code for the knapsack problem, and I have looked at numerous articles and books on the subject.我花了一个星期的时间研究这个用于背包问题的分支和绑定代码,并且我查看了许多关于这个主题的文章和书籍。 However, when I am running my code I don't get the result I expect.但是,当我运行我的代码时,我没有得到我期望的结果。 Input is received from a text file, such as this:从文本文件接收输入,例如:

12
4 1
1 1
3 2
2 3

where the first line is the capacity, and each subsequent line are value/weight pairs.其中第一行是容量,随后的每一行都是值/权重对。 The result I get from using this file is '8' instead of '10' (unless I am mistaken and all the items won't fit in the knapsack).我使用这个文件得到的结果是“8”而不是“10”(除非我弄错了,所有的东西都装不下背包)。 Here is my code:这是我的代码:

#!/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)

Are my indices off?我的指数关闭了吗? Am I not traversing the tree or calculating the bounds correctly?我没有正确地遍历树或计算边界吗?

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)

I think you only want to calculate the bound if you're not taking the item.我认为如果你不拿这个项目,你只想计算界限。 If you take the item, that means that your bound is still attainable.如果您拿走该物品,则意味着您的绑定仍然可以实现。 If you don't, you have to readjust your expectations.如果你不这样做,你必须重新调整你的期望。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM