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个取与不取的最大价值,一直打递归基准条件