分支界限法(Branch and Bound)-问题1: 0/1背包客问题

本范例主要是通过分支界限法解决著名的0/1背包客问题


问题描述:
N=4件商品,记为A,B,C,D,E。每件商品的重量和价值分别为w[i]v[i];
其中w=[1.98,2,5,3.4], v=[100,40,95,50]
现在有一个背包,可以容纳的最大重量为W=10
问该背包可以包含的最大价值是多少?

基本思路:
本问题是一个典型的排列组合问题,只要能满足总重量小于背包的容量的所有组合中,找到那个价值最大的组合;

每一件商品都有2种选择,要么包含该商品,要么不包含该商品
因此总共有2^N种组合方式。当N很大时,如果尝试每一种组合方式,将会使得计算量变得非常大。如下图所示

上图中每一个小圆圈都代表一个节点,对于每一个小节点,都拥有两个分支,两种选择,即包不包含某一个商品,这既是分支界限法种分支的由来。而界限是指我们做每一个选择时,我们先提前预估一下加入做了该选择,可能出现的最优或最差情况,即所谓的上界和下界

比如我们从零开始,我们算一下最大价值是多少?按照贪婪算法思想,我们商品已由单位重量价值进行了从大到小的排列

那么开始最大可能价值就是A,B,C全部包含,D商品部分包含V_{max} =100+40+95+(10-1.98-2-5) *50/3.4 = 250, 由于可以包含部分商品,因此本价值提供了可能最大价值的上限。

假如包含商品A,则上限还是250,假如不包含商品A,则上限计算应是B,C的全部价值加D部分价值,应该是
40+95+(10-2-5)*50/3.4=179.12

分支界限法的思想就是不断选择那个预测值最大的路,像上述包含商品A预测值大于不包含商品A的情况,因此,我们暂时选择包含商品A,下一步就可以考虑是否包含商品B,计算方法类似

更换路线条件:
按照上述方法就为沿着一条路线不断前进且在岔路口选择是否包含某一商品提供了一种判断方法,那么什么情况得更换线路呢?

  1. 重量超标的情况,本例中,假如每一条路线重量超过了10,则该路走不通了,必须切换到当前预测价值最大的那条线路。
  2. 预测值下降严重,已经小于另一条线路上的预测值,这是应更换到那条预测值大的线路上,也就是说时刻走预测值最大的那条线路

结束条件:

  • 每一件商品都考虑到了,如何此时还是最优的情况,那么这条线路就一定是最优的情况,如本例中就是包含商品A,B,C,不包含商品D,最大价值是235


python代码

"""
Created on Mon Jan  7 21:57:42 2019

@author: WAY
"""

from copy import copy,deepcopy
class Item:
    def __init__(self,w,v):
        self.w = w
        self.v = v
        self.r = v/w
    def __lt__(self,other):
        return self.r<other.r

class Node:
    def __init__(self):
        self.layer = -1
        self.info=[]
        self.optimum_value = -1
        self.total_weight=0
    def __lt__(self,other):
        return self.optimum_value<other.optimum_value


class Knapsack:
    def __init__(self,N,maxweight):
        self.N=N
        self.items = []
        self.max_weight = maxweight
        self.max_value = 0
        self.quene = []
    def add_item(self,item):
        self.items.append(item)
    def sort_item(self):
        self.items.sort()
        self.items.reverse()
    def calculate_matrix_value(self,node):
        weight = 0
        value = 0
        for i in node.info:
            weight += i.w
            value += i.v
        if weight>self.max_weight:
            node.optimum_value = -1
            return
        node.total_weight=weight
        for j in range(node.layer+1,self.N):
            if weight + self.items[j].w <= self.max_weight:
                weight += self.items[j].w
                value += self.items[j].v
            else:
                value += self.items[j].r*(self.max_weight-weight)
                break
        node.optimum_value = value
    def knapsack(self):
        node = Node()
        self.calculate_matrix_value(node)
        self.max_value = node.optimum_value
        self.quene.append(node)
        panduan = 0
        while self.quene:
            if panduan == 0:
                node = self.quene.pop()
            panduan=1
            node_L = deepcopy(node)
            node_L.layer += 1
            node_L.info.append(self.items[node_L.layer])
            self.calculate_matrix_value(node_L)
            if node_L.optimum_value>0  and node_L.optimum_value <=self.max_value:
                self.quene.append(node_L)
            
            node_R = deepcopy(node)
            node_R.layer += 1
            self.calculate_matrix_value(node_R)
            if node_R.optimum_value>0  and node_R.optimum_value <=self.max_value:
                self.quene.append(node_R)
            self.quene.sort()
            node = self.quene.pop()
            
            if node.layer == 3:
                break
        return node

def main():
    item1 = Item(2,40)
    item2 = Item(3.14,50)
    item3 = Item(1.98,100)
    item4 = Item(5,95)
    k = Knapsack(4,10)
    k.add_item(item1)
    k.add_item(item2)
    k.add_item(item3)
    k.add_item(item4)
    k.sort_item()
    nn = k.knapsack()
    print(nn.optimum_value)
    print(nn.total_weight)
    for i in nn.info:
        print(i.w)


if __name__ == "__main__":
    main()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容

  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 9,003评论 0 13
  • 目录 1.问题描述1.1 问题描述1.2 问题的数学表示(规划类问题,此种表示可以转换为回溯法)1.3 三种方法的...
    王侦阅读 19,281评论 0 2
  • 选择题部分 1.(),只有在发生短路事故时或者在负荷电流较大时,变流器中才会有足够的二次电流作为继电保护跳闸之用。...
    skystarwuwei阅读 12,943评论 0 7
  • 问题描述: 0-1背包问题:给定n种物品和一背包。物品 i 的重量似乎 wi,其价值为 vi,背包的容量为 c。问...
    我没有三颗心脏阅读 52,783评论 31 154
  • 最近真正的颓废,颓了一个星期啦。每天烦闷,和天气一样,dreary!!! 本周要开始工作,只是不知从何下手。抽了一...
    被地产耽误的columnist阅读 269评论 0 0