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

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

问题描述:

件商品,记为

。每件商品的重量和价值分别为

;

其中

,

现在有一个背包,可以容纳的最大重量为

问该背包可以包含的最大价值是多少?

基本思路:

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

每一件商品都有2种选择,要么包含该商品,要么不包含该商品

因此总共有

种组合方式。当N很大时,如果尝试每一种组合方式,将会使得计算量变得非常大。如下图所示

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

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

那么开始最大可能价值就是A,B,C全部包含,D商品部分包含

, 由于可以包含部分商品,因此本价值提供了可能最大价值的上限。

假如包含商品A,则上限还是250,假如不包含商品A,则上限计算应是B,C的全部价值加D部分价值,应该是

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

更换路线条件:

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

重量超标的情况,本例中,假如每一条路线重量超过了10,则该路走不通了,必须切换到当前预测价值最大的那条线路。

预测值下降严重,已经小于另一条线路上的预测值,这是应更换到那条预测值大的线路上,也就是说时刻走预测值最大的那条线路

结束条件:

每一件商品都考虑到了,如何此时还是最优的情况,那么这条线路就一定是最优的情况,如本例中就是包含商品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

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

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值