动态规划与贪心算法的本质区别
1. 共性:把大问题拆分成子问题,利用子问题的最优决策找到全局最优决策。
2. 区别:
先分析下两个算法(纯白话):
贪心算法:制定一个评判优劣的标准对所有可选项排序,每次取最优(即对每个可选项只做一次决策),所以只有当评判标准存在(即可选项有序)时,贪心算法才能一步步找到全局最优解。否则,如果排序错误,按贪心原则每次取当下最优,不一定找到全局最优解,因为当下最优的决策并不一定是全局最优的一部分,决策失败不可更改。
动态规划:遍历全局目标下所有可能空间(比如(1...N)),在每个可能空间里对所有可选项做决策并记录, 每个可选项有N次决策,随着目标的改变而改变,不用保证每个可选项的每次决策必须是全局最优的一部分,因为站在全局角度看,对于每个可选项只需在它所有的决策集里找到符合目前最优的就可以了,所以,动态规划的实质是穷举所有可能策略只在最后一步做出选择,这样一定可以找到全局最优策略。理论上可以用贪心算法求解的都可以用动态规划求解。
所以两者的本质区别:站在全局角度来看,对于每个可选项,贪心算法是只保留一种决策(要么最优,要么非最优) ,动态规划保留了它的一个决策集,从中取最优。
当然,算法有优势就有劣势,贪心算法虽然适用条件苛刻,但复杂度低,当可选集太大时,动态规划这种穷举策略会比较耗时,而贪心算法虽然并不一定能找到最优解,但也可以找到一个接近最优解的解。
3.分析下面两个问题,为什么部分背包问题可以用贪心算法,而0-1背包问题只能用动态规划解决?
部分背包问题:
有一个背包,背包容量是 M =80kg,有 3个物品,物品可以分割成任意大小,要求尽可能让装入背包中的物品总价值最大,但不能超过总容量
物品 | A | B | C |
---|---|---|---|
重量 | 35kg | 10kg | 60kg |
价值 | 35 | 40 | 120 |
解决方案
计算每个物品的单位价值 :
物品 | A | B | C |
---|---|---|---|
单位价值 | 1 | 3 | 2 |
可知B>C>A, 按总是选取当前单位价值最大原则放入背包,首先放B , 剩余(A,C),再选C放入,此时M=70<80,再取10kg的A放入背包,此时背包装满,价值达到最大。
0_1背包问题:
在部分背包问题上增加一个限制条件,物品不可分割。
为什么0_1背包不可以用贪心算法?
分析题目发现,当物品可分割时,所有物品就可以制定一个统一度量标准,作为每步选取最优解的依据。
但是当物品不可分割后,每个物品要么取,要么不取,这样每个物品就变成了一个独立的整体,所以就无法制定一个统一度量标准去评判所有物品,那就无法对物品进行一个价值高低排序,而贪心的原则就是每一步找当下最好的,无法排序就无法确定哪个是最好的,所以这个情况就不适用于贪心了。
所以贪心算法适用的关键在于,是否可以找到一个统一的评判标准对所有选项排序,有序就可以贪心选择。
为什么0_1背包可以用动态规划?
动态规划解决什么样的问题?
动态规划能解决的问题是无后效性的问题(也称马尔科夫性问题)。即某个决策只受当前最优决策子序列的影响,而不受当前决策可能产生的新的最优决策子序列的影响,则可以理解这个最优决策具有无后效性。
也就是说,动态规划每一个阶段选择的决策,不会影响到过去已经做过的决策。只有这种问题,才可以用动态规划算法求解。
动态规划每一步的最优解是依赖于前面一步或若干步的最优解得到的。所以,动态规划,简单说就是制定一个公式,可以利用历史步的最优解,来得到当前步的最优解,这个公式就是状态转移方程。一般是用一维数组或者二维数组来保存历史记录。
在0_1背包问题上, 如果我们可以定义一组变量记录每一步的最优数据,且定义的这个最优数据代表的意义是无后效性(即不对已存在的记录造成影响),且记录之间有依赖关系,那这样就可以用动态规划的思想解题了。
0_1背包问题中,每个物品只有一件,我们要么装入背包,要么不装入。
依据前面对动态规划算法的分析,我们就可以遍历背包容量上限为M 时的所有可能空间,在每个空间里对每个物品比较放与不放两种情况下达到的价值,找最优策略。当所有可能空间下的所有物品都遍历完时,我们就可在目标空间里找到最优策略。
定义一个二维数组,dp[i][j]表示遍历到第i个物品时,在背包重量为j的限制下所得到的最大价值。
对装与不装进行分析:
装:如果我们选择了第i个物品,那么此时的价值为dp[i-1][j-w[i]]+v[i],即遍历到第i-1个物品且当容量为j-w[i]时的最大价值加上第i个物品的价值。j-w[i]的意义:只有当上个状态所占空间为j-w[i]时,第i个物品才能放得下。
不装:此时价值是dp[i-1][j]。
比较这两种情况,取最优即可
状态转移方程如下:
dp[i][j] =max(dp[i-1][j], dp[i-1][ j-w[i]] +v[i])
当我们遍历完目标空间下最后一个物品在给定的背包容量下的最优解时,就找到了问题的答案。
0_1背包问题既能满足无后效性,又可以创建状态转移方程,故可以用动态规划解决。
代码如下:
# -*- coding: utf-8 -*-
def FindMax(num,bagC,w,v):
#定义二维数组,初始置0
dp =[[0 for j in range(bagC + 1)] for i in range(num + 1)]
#寻求每个空间下的最优价值
for i in range(1, num+1):
for j in range(1, bagC+1):
dp[i][j] = dp[i-1][j]
#装入i
if j >= w[i-1]:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]]+v[i-1])
return dp
if __name__ == "__main__":
n = 6 #物品的数量,
bagC = 10 #背包容量
w = [1, 2, 3, 1, 5, 3] #每个物品的重量
v = [2, 3, 1, 5, 4, 3] #每个物品的价值
dp = FindMax(n,bagC,w,v)
for i in dp:
print i
max_v = dp[n][bagC]
#打印装入策略,依据結果倒推策略,在同等容量下,当前i件价值大于前i-1件价值时,则为装入
for i in range(n, 1, -1):
if dp[i][bagC] >dp[i-1][bagC]:
print "装入第", i, "个物品"
#此时容量变为bagC-w[i-1]
bagC = bagC-w[i-1]
print "max_value:",max_v
本次分析到此结束,不对的地方欢迎指正~