动态规划问题首先是分解原始问题为相同形式的子问题,是利用重复的子问题不再重新计算的算法。
钢锯切割是其中一个典型代表,子问题形式为左边不切割,右边的进行切割,首先是朴素的递归形式,时间复杂度是2的n次方,
代码1
#-*- coding: utf8 -*-
import time
'''钢条切割问题的自顶向下版本,直接进行递归
p是长度对应的价格表
n是现在的钢条长度
求解最大利润'''
def CUT_ROD(p, n):
if n is 0:
return 0
q = 0
for i in xrange(1, n+1):
q = max(q, p[i]+CUT_ROD(p, n-i))
return q
p = {0:0,1:1,2:5,3:8,4:9,5:10,6:17,7:17,8:20,9:24,10:30}
def main():
n = 24
max_key = max(p.keys())
if n > max_key:
for i in xrange(max_key+1, n+1):
p[i] = 0 #原来的没有p[i],否则报keyerror
p[i] = CUT_ROD(p, i)
print n, p[n]
if __name__ == '__main__':
begin = time.time()
main()
print 'total run time', time.time()-begin
用时:
>>> 24 70 total run time 35.2200000286
此方式效率看得出是很低的,有两种方式可以优化上面的方案:
加入备忘机制,就是中途将计算的子问题的最优解存储起来
代码2
#-*- coding: utf8 -*- import time ''' “自顶向下”带备忘,是朴素递归带备忘的形式 top-down with memoization ''' def MEMOIZED_CUT_ROD(p, n): r = [] #用来存储长度为n的最大收益,就是所有子问题的解 for i in xrange(0, n+1): # 0 to n 初始化 r.append(-1) return MEMOIZED_CUT_ROD_AUX(p, n, r) def MEMOIZED_CUT_ROD_AUX(p, n, r): if r[n]>=0: return r[n] #这里就是已经存在的长度为n的最大收益,如果存在则直接取出,不再计算 if n is 0: q = 0 else: q = -1 for i in xrange(1, n+1): # 1 to n q = max(q, p[i] + MEMOIZED_CUT_ROD_AUX(p, n-i, r)) r[n]=q return q p = {0:0,1:1,2:5,3:8,4:9,5:10,6:17,7:17,8:20,9:24,10:30} def main(): n = 24 max_key = max(p.keys()) if n > max_key: for i in xrange(max_key+1, n+1): p[i] = 0 #原来的没有p[i],否则报keyerror print MEMOIZED_CUT_ROD(p, n) if __name__ == '__main__': begin = time.time() main() print 'total run time', time.time()-begin
用时:
>>> 70 total run time 0.0120000839233
看出优化是很有效的
第二种方式是自底向上的寻找最优解,更加简单而且执行速度更快:
代码3
#-*- coding: utf8 -*- import time ''' 自底向上法 bottom-up method ''' def BOTTOM_UP_CUT_ROD(p, n): r = [] #用来存储长度为n的最大收益,就是所有子问题的最优解 for i in xrange(0, n+1): # 0 to n 初始化 r.append(-1) r[0] = 0 #长度为0, 收益为0 for j in xrange(1, n+1): q = -1 for i in xrange(1, j+1): q = max(q, p[i]+r[j-i]) r[j] = q return r[n] p = {0:0,1:1,2:5,3:8,4:9,5:10,6:17,7:17,8:20,9:24,10:30} def main(): n = 24 max_key = max(p.keys()) if n > max_key: for i in xrange(max_key+1, n+1): p[i] = 0 #原来的没有p[i],否则报keyerror print BOTTOM_UP_CUT_ROD(p, n) if __name__ == '__main__': begin = time.time() main() print 'total run time', time.time()-begin
用时:
>>> 70 total run time 0.0110001564026
第二种方式更加简洁,且没有递归,因为一程序都有递归调用的最大深度的限制,两者渐进时间一致,但是因为少了递归,第二种方式的系数小一些
稍加修改上面的代码,使之不仅返回最大收益,而且打印出最优解
#-*- coding: utf8 -*- import time ''' 输出最优解的自底向上法 bottom-up method ''' def EXTENDED_BOTTOM_UP_CUT_ROD(p, n): r, s = [], [] # r用来存储长度为n的最大收益,就是所有子问题的最优解 # s存储对长度为n的钢条切割的最优解对应的第一段钢条的切割长度 for i in xrange(0, n+1): # 0 to n 初始化 r.append(-1) s.append(0) r[0] = 0 #长度为0, 收益为0 for j in xrange(1, n+1): q = -1 for i in xrange(1, j+1): if q < p[i]+r[j-i]: q = p[i]+r[j-i] s[j] = i r[j] = q return r, s def PRINT_CUT_ROD_SOLUTION(p, n): r, s = EXTENDED_BOTTOM_UP_CUT_ROD(p, n) print 'max revenue is', r[n] while n>0: print s[n] n = n-s[n] p = {0:0, 1:1, 2:5, 3:8, 4:9, 5:10, 6:17, 7:17, 8:20, 9:24, 10:30} def main(): n = 9 max_key = max(p.keys()) if n > max_key: for i in xrange(max_key+1, n+1): p[i] = 0 #原来的没有p[i],否则报keyerror PRINT_CUT_ROD_SOLUTION(p, n) if __name__ == '__main__': begin = time.time() main() print 'total run time', time.time()-begin
输出:
>>> max revenue is 25 3 6 total run time 0.0220000743866
转载于:https://blog.51cto.com/herculeser/1340213