SICP 第一章 1.2.2 树形递归中,有这么一问题:给了半美元,四分之中的一个美元。10美分,5美分和1美分的硬币。将1美元换成零钱,一共同拥有多少种不同方式?更一般的问题是,给定了随意数量的现金,我们能写一个程序,计算出全部换零钱方式的种数吗?
因此各个阶段决策的选取不能随意确定,它依赖于当前面临的状态。又影响以后的发展。当各个阶段决策确定后,就组成一个决策序列,从而也就确定了整个过程的一条活动路线。这样的把一个问题看做是一个前后关联具有链状结构的多阶段过程称为多阶段决策过程。
动态规划著名的应用实例有:求解最短路径问题,背包问题,项目管理,网络流优化等。动态规划的基本模型例如以下:
通俗理解就是子问题的局部最优将导致整个问题的全局最优,即问题具有最优子结构的性质,也就是说一个问题的最优解仅仅取决于其子问题的最优解,非最优解对问题的求解没有影响。
详细地说,假设一个问题被划分各个阶段之后,阶段 I 中的状态仅仅能由阶段 I+1 中的状态通过状态转移方程得来,与其它状态没有关系,特别是与未发生的状态没有关系,这就是无后效性。从图论的角度去考虑。假设把这个问题中的状态定义成图中的顶点,两个状态之间的转移定义为边,转移过程中的权值增量定义为边的权值,则构成一个有向无环加权图,因此,这个图能够进行“拓扑排序”,至少能够按他们拓扑排序的顺序去划分阶段。
所以假设确定了决策,状态转移方程也就可写出。但其实经常是反过来做。依据相邻两段各状态之间的关系来确定决策。
动态规划的正向思维法。正是从已知最优值的初始状态或边界状态開始,依照一定的次序遍历整个状态空间,递推出每一个状态所相应问题的最优值。
在正向思维法中。不再区分原问题和子问题,将动态规划的过程看成是从状态到状态的转移。将全部的状态构造出一个状态空间,并在状态空间中设想一个状态网络。若对两个状态i,j,存在决策变量di使t(i,di)=j。则向状态网络加入有向边。
给定己知最优值的初始状态或边界状态,能够沿著有向边推广到未知最优值的新状态。利用状态转移方程得到新状态的状态变量的最优值。
我们能够用这样的方式遍历整个状态空间,得到每一个状态的状态变量的最优值。
动态规划的正向推导步骤:
动态规划须要按阶段遍历整个状态空间。因此动态规划的效率取决于状态空间的大小和计算每一个状态最优值的开销:假设状态空间的大小是多项式的,那么应用动态规划的算法就是多项式时间的;假设状态空间的大小是指数的,那么应用动态规划的算法也是指数时间的。因此,找一个好的状态划分对动态规划的效率是至关重要的。
还能够正向推导。打表记录已经计算出的值。
Python实现
NUM = 0 def count_change(amount, money, kinds): ''' 树形递归存在冗余''' global NUM if amount == 0: NUM+=1 return 1 if amount < 0 or kinds == 0: NUM+=1 return 0 NUM+=1 return count_change(amount, money, kinds - 1) + count_change(amount - money[kinds - 1], money, kinds) def count_dy(amount,money,kinds): '''动态规划,打表记录已经计算的值''' table = [[0 for col in range(kinds)] for row in range(amount+1)] table[0] = [1]*kinds for i in range(1,amount+1): for j in range(kinds): # 包括 money[j] x = table[i - money[j]][j] if i-money[j] >= 0 else 0 # 不包括 money[j] y = table[i][j-1] if j>=1 else 0 table[i][j] = x+y return table[amount][kinds-1] if __name__ == '__main__': money = [1, 5, 10, 25, 50] print(count_change(100, money, len(money)),'time:',NUM) print(count_dy(100, money, len(money)),'time:',100*len(money)) ''' 292 time: 15499 292 time: 500 '''
【地址:http://blog.csdn.net/thisinnocence/article/details/41073275】