背包问题描述
有n个物品,它们有各自的体积和价值,现有给定容量m的背包,如何让背包里装入的物品具有最大的价值总和?
思考
这是一个经典问题,在博客里搜一抓一大把,对于算法小白来说,有没有不用复杂的算法,取巧来解决这个问题的方法呢?试着分析一下背包问题本身:
- 背包里尽量装重量最轻、价值反而最高的物品(即”最优物品“,意味着需要重排数据);
- 用先进后出的方法,先将最优物品放进包中,接近满重。这里先放入包里的,大多是重量较轻者;
- 在用m减去目前重量获得差值,如果已在包中的物品价值 + 差值,比未入包包的物品重,则比较包中物品和未入包物品的价值,替换或者不替换。
- 完成
代码实现
- 用了两个库:
from operator import itemgetter
import numpy as np
- 先自定义一批数字和背包重量,本来想做input的,为了展示方便,自定义了
weight = [1, 4, 6, 2, 3, 2, 5, 3, 2, 4, 6]
value = [200, 300, 300, 250, 240, 140, 150, 200, 110, 220, 160]
# m = input('请输入背包重量(kg):')
m = 20
- 将物品的重量与价值联系起来为thing_cp列表
thing_cp = []
for i in range(len(value)):
thing_cp.append([weight[i], value[i]])
- 给物品的价值做重新排列,巧加入一个评估参数形成[ 重量,价值,评估参数 ],评估参数越大则说明其重量和价值的搭配最优;利用sort方法排序:按照价值参数、将物品进行降序排列。
def rearrange():
# 寻找重量最轻、价值最高的参数valu_sort_index
for i in thing_cp:
valu_sort_index = (i[1] / np.max(value)) / (i[0] / np.max(weight))
i.insert(2, valu_sort_index)
# thing_cp现在为[重量,价值,价值参数]
# 按照价值参数降序排列
thing_cp.sort(key = itemgetter(2), reverse = True)
return thing_cp
thing_cp = rearrange()
# 排列后为:[[1, 200, 4.0], [2, 250, 2.5000000000000004], [3, 240, 1.6], [4, 300, 1.5], [2, 140, 1.4000000000000001], [3, 200, 1.3333333333333333],
# [2, 110, 1.1], [4, 220, 1.1], [6, 300, 1.0], [5, 150, 0.6], [6, 160, 0.5333333333333333]]
- 开始装包
ready_in = [] # 已经在背包里的数组
def fullbag():
# 初始化背包重量、放入物品数、放入物品价值
global m
bagm = 0
items = 0
values = 0
# 定义将物品按照上述顺序放入背包到快满
for i in thing_cp:
bagm += i[0]
items += 1
values += i[1]
ready_in.append(i)
if bagm < m:
continue
if bagm > m: # 如果迭代到背包重量过大,则退回这次迭代
bagm -= i[0]
items -= 1
values -= i[1]
ready_in.remove(i)
return ready_in, bagm, items, values
ready_in, bagm, items, values= fullbag()
for j in ready_in:
thing_cp.remove(j) # 重置thing_cp列表
- 现在包里几乎已经满了,开始做拿出、换入
def adjust():
global ready_in, thing_cp, bagm, items, values
diff = m - bagm # 有空缺重量diff
for i in ready_in:
for j in thing_cp:
if i[0] + diff >= j[0] and i[1] < j[1]:
# ready_in里面某物品i 重量加上差值大于thing_cp里的某个物品 j,
# 但是i的价值小于j,于是替换
ready_in.remove(i)
ready_in.append(j)
thing_cp.remove(j)
thing_cp.append(i)
bagm = bagm - i[0] + j[0]
items = items
values = values - i[1] + j[1]
diff = m - bagm # 更新diff差值
return ready_in, thing_cp, bagm, items, values
ready_in, thing_cp, bagm, items, values = adjust()
print('背包里的物品有:')
print(ready_in)
print('剩余备选列表:')
print(thing_cp)
print('背包目前重量:{}kg,装入物品{}个,总价值{}元'.format(bagm, items, values))
最后得到的结果:
背包里的物品有:
[[2, 250, 2.5000000000000004], [3, 240, 1.6], [4, 300, 1.5], [3, 200, 1.3333333333333333], [4, 220,
1.1], [1, 200, 4.0], [2, 140, 1.4000000000000001]]
剩余备选列表:
[[6, 300, 1.0], [5, 150, 0.6], [6, 160, 0.5333333333333333], [2, 110, 1.1]]
背包目前重量:19kg,装入物品7个,总价值1550元
总结
这里利用了重量价值参数做排序,有两个好处:
1. 将重量更低、价值更高的作为优选放入包,减少了后续的更换计算
2. 正因为第1点,在后续替换的时候,不可能出现在ready_in当中两物品价值的相加会少于任何还在thing_cp当中的物品,减少了替换条件。