码龄0.3年的python成长之路(四):背包问题取巧之解

背包问题描述

有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当中的物品,减少了替换条件。

完毕

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊@RoyalzoneTCM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值