PY动态规划笔记

1、动态规划指的是从小问题的最优解从而一步步求出大问题,它与递归的区别是动态规划在求解的过程中会把解记录下来,无需再求,递归是将大问题无限递归分成小问题,通过返回小问题的解解决大问题,这是与动态规划是不同的,而且递归过程不会记录解,会有重复做工。
2、动态规划适用于以下特征:
(1)无后效性,各种解之间不会影响
(2)有重叠子问题
(3)可以找出状态转移方程,即将大问题转成求小问题
(4)边界问题,小问题的的解
3、三要素:最优子结构(当前最优状态可以通过前某个阶段最优状态得到)、状态转移方程、边界。

补充
需要将最优子集保存,就要通过numpy建立数组保存,数组与列表看起来相似,但数组容量已经固定了,这与列表是不同的。还可以通过镶嵌列表储存。

# numpy模块
import numpy as np
arr = np.zeros((3, 3))     # 建立元素为0的数组3*3
#赋值 arr[0, 0] = 1

# 镶嵌列表
x = [[0 for i in range(0,3)]for i in range(0,3)]
print(x)
x[2][2] = 3
print(x)
x.append([1,2,3,4])
print(x)
# 结果 
#[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
#[[0, 0, 0], [0, 0, 0], [0, 0, 3]]
#[[0, 0, 0], [0, 0, 0], [0, 0, 3], [1, 2, 3, 4]] 

练习
选择一组数组中不相邻的数求和,所得的结果要求最大:
1,2,4,1,7,8,3

分析:从最后一个数3开始,有两种选择:选和不选。如果选了3,则不能选择8,最大值由3和7及之前的数组成;如果没有选3,则最大值为8及之前的数组成。。。总之,找出口,i为0和1,找递归

def rec_opt(arr, i):
# arr是要选择的数组 i是索引值
# 递归出口
    if i == 0:
        return arr[0]
    elif i == 1:
        return max(arr[0], arr[1])
# 递归
    else:
        A = rec_opt(arr, i - 1)
        B = rec_opt(arr, i - 2) + arr[i]
        return max(A, B)
arr = [1, 2, 4, 1, 7, 8, 3]
print(rec_opt(arr, 6))
# 15

# 动态规划
def dp_opt(arr, i):
    opt = [0 for i in range(len(arr))]
# 边界
    opt[0] = arr[0]
    opt[1] = max(arr[0], arr[1])
# 状态转移公式,遍历,从小到大保存最优子
    for i in range(2, len(arr)):
# 选择该数
        A = arr[i] + opt[i - 2]
# 不选择该数
        B = opt[i - 1]
# 取最大
        opt[i] = max(A, B)
    return opt[i]

arr = [1, 2, 4, 1, 7, 8, 3]
print(dp_opt(arr, 6))
# 15

背包问题``
求有限重量内的最大价值

# wb为背包可承受重量,,w为物品重量,v为物品价值,n,i为纵坐标,j为横坐标,横着填数据
def price(wb, n, w, v):
# 创建一个容器保存数据
    opt = [[0 for j in range(wb)] for i in range(len(v) + 1)]
# 边间先存进去
    for j in range(wb):
        opt[0][j] = 0
# 状态转移
    for i in range(1, n + 1):
        for j in range(wb):
# 装的下,有两种结果,选和不选
            if j + 1 >= w[i - 1]:     # 背包重量大于物体重量
                A = opt[i - 1][j + 1 - w[i - 1]] + v[i - 1]    #该物品+剩余空间的价值
                B = opt[i - 1][j]          # 上一件物品价值
                opt[i][j] = max(A, B)      # 取最大值
            else:                    # 装不下
                opt[i][j] = opt[i - 1][j]    # 取之前物品最大价值

    return opt
    


wb = 4
n = 3
w = [1, 3, 4]
v = [1500, 2000, 3000]
print(price(wb, n, w, v))
# [[0, 0, 0, 0], [1500, 1500, 1500, 1500], [1500, 1500, 3500, 3500], [1500, 1500, 3500, 4500]]
# 这里犯了一个错误,没有考虑到背包剩余重量为0的情况,一开始想的是只要不放东西了价值就是0,后面检查写的时候发现有情况是当物体正好可以放进背包,所以剩余的空间为0了,而处理过程中我没有设立0这一列,新手智障错误,下面重新处理:

下面是痛改前非后的结果:

# wb为背包可承受重量,,w为物品重量,v为物品价值,n,i为纵坐标,j为横坐标,横着填数据
def price(wb, n, w, v):
# 创建一个容器保存数据
    opt = [[0 for j in range(wb + 1)] for i in range(n + 1)]
# 边界先存进去
    for j in range(wb + 1):
        opt[0][j] = 0
    for i in range(n + 1):
        opt[i][0] = 0
# 状态转移
    for i in range(1, n + 1):
        for j in range(1, wb + 1):
# 包能装的下,有两种结果,选和不选 
            if j >= w[i - 1]:     # 背包重量大于物体重量
                A = opt[i - 1][j - w[i - 1]] + v[i - 1]    #该物品+剩余空间的价值
                B = opt[i - 1][j]          # 上一件物品价值
                opt[i][j] = max(A, B)      # 取最大值
            else:                    # 装不下
                opt[i][j] = opt[i - 1][j]    # 取之前物品最大价值
    return opt
'''
看到有的书这样写的,也行
			if j >= w[i - 1] and opt[i - 1][j - w[i - 1]] + v[i - 1] > opt[i - 1][j]:
				opt[i][j] = opt[i - 1][j - w[i - 1]] + v[i - 1]
			else:
				opt[i][j] = opt[i - 1][j]
'''
    
# 找出装了哪些物品   
def get_w(opt, w, wb, n):
# 创建一个空列表记录装的物品
    w_list = []
    i = n                       # 从最后一个格子开始
    j = wb
    while i > 0:                # 停止
        if opt[i][j] > opt[i - 1][j]:           # 当前大于之前
            w_list.append(w_name[i - 1])
            i -= 1 
            j -= w[i - 1] 
        else:                                   # 这个就没有装,然后看之前的物品
            i -= 1 
    return w_list
    
if __name__ == '__main__':
    n = 3
    wb = 4
    w_name = ['guitar', 'laptop', 'sound']
    w = [1, 3, 4]
    v = [1500, 2000, 3000]
    opt = price(wb, n, w, v)
    print(opt[n][wb])
    print(get_w(opt, w, wb, n))
    
# 3500
#['laptop', 'guitar']

贪婪算法背包问题,每次取能装的最大的,求最大价值。

def rec_bag(w, v, n, wb):  # n为要装的物品个数, wb为背包空余重量 v为价值 w为物体重量
# 出口(东西没有了或者没有空间了)
    list_w = w   # 保护原始数据
    list_v = v
    if n == 0 or wb == 0:
            return 0
    else:      # 取最大,能是否装下
        Max = max(v)
        i = v.index(Max)   # 找到最大值物品的位置
        if w[i] <= wb:       # 如果装得下
            value = list_v.pop(i)
            weight = list_w.pop(i)
       # 还可以拿的物品数目少一件
            return value + rec_bag(list_w, list_v, n - 1, wb - weight)
        else:                   # 如果装不下,排除这件,能拿的少了一件
            value = list_v.pop(i)
            weight = list_w.pop(i)
            return rec_bag(list_w, list_v, n - 1, wb)

        
if __name__ == '__main__':
    w_list = ['guitar', 'laptop', 'sound']
    w = [1, 3, 4, 1]
    v = [1500, 2000, 3000, 5000]
    n = 4
    wb = 4
    print(rec_bag(w, v, n, wb))

# 7000

递归求背包问题。。。。。回溯还想不到,学艺不精,唉,期待大佬指教。

def rec_bag(i, j):  # i为要装的前i物品, j为背包空余重量
# 出口
    if i == -1 or j == -1:
            return 0
    else:
        if j >= w[i]:
            return max(rec_bag(i - 1, j - w[i]) + v[i], rec_bag(i - 1, j))
        else:
            return rec_bag(i - 1, j)

    
if __name__ == '__main__':
    w_list = ['guitar', 'laptop', 'sound']
    w = [1, 5, 4, 1]           #物品重量
    v = [1500, 2000, 3000, 5000]  #物品价值
    n = 4   # n个物品
    j = 4   # 背包容量j
    print(rec_bag(n - 1, j)) # n - 1 为最后一个物品的位置
# out 6500
# 前n个 = 前n-1个+第n个取与不取的最大价值,前n-1个 = 前n-2个+第n-1个取与不取的最大价值,一直打递归基准条件
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值