动态规划之背包问题的Python实现

本文介绍了如何使用动态规划的网格法解决背包问题,详细阐述了问题描述、动态规划的原理以及Python代码实现,通过实例展示了如何找到背包问题的最优解,并给出了运行结果。

目录

1.问题描述

2.动态规划之网格法

3.python实现


1.问题描述

题目来源于《算法图解》第9章练习题9.2,如下图所示。

对于背包问题,通常的做法有列举法、贪婪算法和动态规划

(1)列举法:列举出所有的可能情况,再选择最优解,但当情况很多时,这种算法复杂度很高

(2)贪婪算法:在容量允许范围内,每次都拿剩余物品中价值最高的 ,贪婪算法能够快速解决复杂度很高的问题,但通常得到的是次优解,但就对这个题目而言,恰好可以得到最优解的

(3)动态规划:对于解决背包问题,最好的算法是动态规划,可以得到最优解。动态规划和贪婪算法相似,都是将问题分解为若干小问题,自底向上解决问题,但贪婪算法目标是取得局部最优,而动态规划要取得全局最优。

2.动态规划之网格法

《算法图解》一书中对于求解动态规划问题,使用了网格法。

对于背包问题,如下表格,网格的列表示背包容量的划分,具体的划分粒度取决于物品的最小重量,本题中,物品最小重量为1,所以划分的最小单位就是1。中间每个网格对应的是一个子问题,网格里填的数字就是我们要优化的值。换句话说,以(水,1)这个单元格为例,所对应的问题就是将水放到容量为1磅的背包中,能够获得的最大价值是多少?

 123456
      
      
食物      
夹克      
相机      

建立网格后,一个关键的问题就是更新网格,更新规则为 当前单元格的价值=max{上一个单元格的价值,当前物品的价值+剩余空间的价值}。这个规则中,上一个单元格的价值,当前物品的价值都很容易得到,关键是剩余空间的价值。剩余空间的价值需要对剩余物品进行组合,然后找出组合中的最大值吗?答案是否定的,这个网格中有一个重要的性质是:更新第n行时,n-1行的单元格的值是目前情况下的最大值。因为网格依次向下更新,保存的永远是最大值。例如,对于6磅的背包,我当前装了1磅的物品,还有5磅的剩余空间,那我直接到前一行找到5磅这列的值,就是当前5磅背包所得到的最大价值。

因此,我们不断向下更新,直到最后一行,便可以得到各种容量背包所能得到的最大价值,这其中当然也包括问题的解——6磅背包的最大价值。此外,在更新过程中,还要记录单元格数值对应的物品,这样才能知道要拿哪些东西。不然,就会出现最后算出了最大价值,但不知道该拿哪些东西的尴尬情况。

3.python实现

在python编程实现这个算法,由于采用了网格,因此采用pandas库能够比较直观的表现出这个过程,同时可以便捷的扩展到其他类似问题。代码如下所示

"""
算法图解第9章,9.2题动态规划解决背包问题
"""
import pandas as pd
import numpy as np


def dynamic_plan():
    need_goods=[['0']* (row*column)] #该列表存放每个单元格对应的物品,仅用一个下标表示
    last_line=pd.DataFrame(np.zeros((1,column)))#这个pandas表格用于存放上一行的值,初始化为0,便于处理更新第一行的情况
    last_line.rename(columns={0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, inplace=True)
    count=0
    for key,dict in goods.items():#迭代更新每一行
        weight=dict['weight']
        value=dict['value']
        for i in divide_capacity: #依次和每列的重量比较
            if weight<i:
                remain_weight=i-weight
                loc=divide_capacity.index(remain_weight)#找到前一行的该重量的位置
                new_value=last_line.at[0,divide_capacity[loc]]+value#当前价值+剩余价值
                if new_value>last_line.at[0,i]:
                    net.at[key,i]=new_value
                    if count>5:
                        need_goods[0][count]=need_goods[0][count-column-(count%column-loc)]+','+key #前面的物品加上当前物品
                    else:
                        need_goods[0][count] = key  # 正好是当前物品,count<5说明更新第一行
                else:
                    net.at[key,i]=last_line.at[0,i]
                    need_goods[0][count] = need_goods[0][count - column]  # 正好是前一行的同列
            elif weight==i:
                if value > last_line.at[0,i]:
                    net.at[key, i] = value
                    need_goods[0][count]=key # 正好是当前物品
                else:
                    net.at[key, i] = last_line.at[0,i]
                    need_goods[0][count] = need_goods[0][count - column]  # 正好是前一行的同列
            else:
                net.at[key, i] = last_line.at[0,i]
                if count>5:
                    need_goods[0][count]=need_goods[0][count-column]#正好是前一行的同列
                else:
                    need_goods[0][count]='0'
            count += 1
        print(net)
        last_line.loc[0]=net.loc[key]
    print('need goods is:')
    print(need_goods[0][count-1])
        # tmp.rename(index={key:0},inplace=True)
        # last_line[0]=tmp


if __name__=="__main__":
    row=5#网格行数
    column=6#网格列数
    capacity=6#背包总容量
    net_array=np.zeros((row,column))
    net=pd.DataFrame(net_array) #创建pandas数据
    items=['water','book','food','jack','camera']
    divide_capacity=[i+1 for i in range(column)]
    net.index=items #修改pandas表格的行名
    net.rename(columns={0:1,1:2,2:3,3:4,4:5,5:6},inplace=True)#修改pandas表格的列名

    goods={'water':{'weight':3,'value':10},'book':{'weight':1,'value':3},
           'food':{'weight':2,'value':9},
           'jack':{'weight':2,'value':5},
           'camera':{'weight':1,'value':6}} #建立物品字典,每样物品都有重量和价值属性

    dynamic_plan()#进行动态规划求解

运行结果如下图所示

更新第一行 water

更新第二行 book

更新第三行food

更新第四行jack

更新第五行camera,得到最终的结果,6磅背包对应的最大价值是25

 选择装入的物品

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值