[0-1背包问题]有一个背包,背包容量是M=150kg。有7个物品,物品不可以分割成任意大小。(这句很重要)
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 A B C D E F G
重量 35kg 30kg 6kg 50kg 40kg 10kg 25kg
价值 10 40 30 50 35 40 30
要想得到最优的结果,最容易想到的就是穷举法,那么首先用穷举法来处理这个问题。
首先就是要看穷举一共有多少种拿法,$2^7$种拿法,也就是说一共有128种方法,这里面一定有最优的,我们只要计算出128种拿法的价值,就一定能选出最优的拿法,当然要排除掉超过总容量的集合。
为了专业一点,首先要定义一个类Item来将问题转化为计算机能够理解的问题。
# item have three attribute: name,weight,value
class Item(object):
def __init__(self,n,v,w):
self.name=n
self.weight=float(w)
self.value=float(v)
def getName(self):
return self.name
def getValue(self):
return self.value
def getWeight(self):
return self.weight
def __str__(self):
result = ' < '+self.name+' , '+str(self.value)+' , '+str(self.weight) + '>'
return result
下面的这段算法提供了整个解空间,她的返回值就是整个$2^7$个集合
1 | #get the binary representation of a num n |
这段代码需要详细的解释一下。getBinaryRep
就是获取一个数的指定位数的二进制表示。不足的位数补0,运行这个函数的结果如下所示:
1 | print getBinaryRep(7,7) |
genPowerSet
这个函数可以获取指定输入集合的所有子集合的表示。
原理如下:
我们可以把输入集合当做一个元素的列表,任意一个子集合看成是一个只包含了0,1的字符串,0表示不包含指定元素,1表示包含指定元素,那么全0就表示空集,全1就表示包括所有元素的集合(输入集合)。因此0—$2^7$所有的数的2进制就代表了输入集合的所有子集,也就是找到了问题的全部子空间。
函数的运行结果示例:
1 | L = ['A','B','C'] |
准备工作做完了,下面来利用穷举法解决一下0/1背包问题
1 | from Item import Item |
1 | start1 = time.clock() |
从实验结果上看,还是很快的,穷举法很完美啊。但是穷举的复杂度可是指数级的,当数据量比较大时就要歇了,举个例子,比如说集合的元素数目为20的时候,仅仅函数genPowerSet
就要花费14.1231262126s,这就是指数的可怕,如果是26的话,就呵呵了(反正我是死机了)。
所以穷举法对于数据量小的情形是很好用的,但是数据量稍微大一些,就无路可走了。