简体   繁体   中英

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). 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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