动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划python
动态规划算法一般基于一个递推公式和n个初始状态。当前子问题的解将由前子问题的解推出。使用动态规划来解题只须要多项式时间复杂度。算法
动态规划算法一般用于求解具备某种最优性质的问题。在这类问题中,可能会有许多可行解。每个解都对应于一个值,咱们但愿找到具备最优值的解。与分治法相似,基本思想也是将待求解问题分解成若干个子问题,先求解子问题,而后从这些子问题的解获得原问题的解。数组
与分治法不一样的是,适合于用动态规划求解的问题,经分解获得子问题每每不是互相独立的。若用分治法来解这类问题,则分解获得的子问题数目太多,有些子问题被重复计算了不少次。若是咱们可以保存已解决的子问题的答案,而在须要时再找出已求得的答案,这样就能够避免大量的重复计算。能够用一个表来记录全部已解的子问题的答案。无论该子问题之后是否被用到,只要它被计算过,就将结果记录下来,方便以后使用。这就是动态规划法的基本思路。markdown
动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。不像搜索或数值计算那样,具备一个标准的数学表达式和明确清晰的解题方法。动态规划程序设计每每是针对一种最优化问题,因为各类问题的性质不一样,肯定最优解的条件也互不相同,于是动态规划的设计方法对不一样的问题,有各具特点的解题方法,而不存在一种万能的动态规划算法,能够解决各种最优化问题。数据结构
最优化原理可这样阐述:一个最优化策略具备这样的性质,不论过去状态和决策如何,对前面的决策所造成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略老是最优的。一个问题知足最优化原理又称其具备最优子结构性质。优化
将各阶段按照必定的次序排列好以后,对于某个给定的阶段状态,它之前各阶段的状态没法直接影响它将来的决策,而只能经过当前的这个状态。换句话说,每一个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。spa
动态规划将原来具备指数级时间复杂度的搜索算法改进成了具备多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程当中,不得不存储产生过程当中的各类状态,因此它的空间复杂度要大于其它的算法。设计
有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够x元?code
咱们最早想到的算法通常是贪婪算法,每次减去可以减掉的最大数值,最后得出结果。递归
固然这在上面问题这种问题上是成立的,由于有面值是基数1元,这显然不能表明广泛性。当提供的面值是二、三、5时,要求x=11,咱们发现直接粗暴地套用贪婪原理并不奏效,x-5-5=1
,此时没法进行下一步,须要提供回溯。
因此说,在有些时候这类问题能够经过贪婪算法解决,可是大部分状况,咱们没法直接得到想要的结果。
很明显,这个问题能够分解为:
求解凑齐
x-1
数值的硬币枚数+1求解凑齐
x-3
数值的硬币枚数+1求解凑齐
x-5
数值的硬币枚数+1
最终的结果就是这三种状况的最小值,而这三种状况又能够继续分别分解。解决的路径跟递归很类似,可是至关容易套圈~
因此咱们开始尝试顺人类认知的从小数值递推到x.
如今咱们在开始看上一部分DP适用条件
最优化原理
无后效性
子问题的重叠性
第一个条件显而易见的符合,第二个条件,当咱们求出x-1
、x-3
、x-5
这些结果以后,这些步骤的最终结果只是为了可以获得x的最优解,求解以后跟x+1
、x+3
后续再无瓜葛,这就符合了无后效性。第三个条件,从我开始分解这道问题的时候就可以得出结论。
按照DP的知识,有下面几个重要的术语:
阶段:把所给求解问题的过程恰当地分红若干个相互联系的阶段,以便于求解,过程不一样,阶段数就可能不一样.描述阶段的变量称为阶段变量。在多数状况下,阶段变量是离散的,用k表示。此外,也有阶段变量是连续的情形。若是过程能够在任什么时候刻做出决策,且在任意两个不一样的时刻之间容许有无穷多个决策时,阶段变量就是连续的
状态:状态表示每一个阶段开始面临的天然情况或客观条件,它不以人们的主观意志为转移,也称为不可控因素。在上面的例子中状态就是某阶段的出发位置,它既是该阶段某路的起点,同时又是前一阶段某支路的终点。
决策:一个阶段的状态给定之后,从该状态演变到下一阶段某个状态的一种选择(行动)称为决策。在最优控制中,也称为控制。在许多问题中,决策能够天然而然地表示为一个数或一组数。不一样的决策对应着不一样的数值。描述决策的变量称决策变量,因状态知足无后效性,故在每一个阶段选择决策时只需考虑当前的状态而无须考虑过程的历史。决策变量的范围称为容许决策集合
策略:由每一个阶段的决策组成的序列称为策略。对于每个实际的多阶段决策过程,可供选取的策略有必定的范围限制,这个范围称为容许策略集合。容许策略集合中达到最优效果的策略称为最优策略。
状态转移方程:给定k阶段状态变量x(k)的值后,若是这一阶段的决策变量一经肯定,第k+1阶段的状态变量x(k+1)也就彻底肯定,即x(k+1)的值随x(k)和第k阶段的决策u(k)的值变化而变化,那么能够把这一关系当作(x(k),u(k))与x(k+1)肯定的对应关系,用x(k+1)=Tk(x(k),u(k))表示。这是从k阶段到k+1阶段的状态转移规律,也就是状态转移方程。
上面在Encyclopedia上提到的解读可能比较抽象,状态就是问题分解获得子问题的当前解。
具体到这个例子,就是当x=[0、一、二、三、四、五、六、七、八、九、...x-一、x]
这些问题的解。
咱们经过基础的数据结构数组S表示子问题解,也就是原来问题的状态。
s[0]
求解须要多少枚硬币能够凑齐0元,显然,s[0]=0
s[1]
须要多少枚硬币能够凑齐1元,s[1]=1,须要1枚1元硬币
s[2]
须要多少枚硬币能够凑齐2元,s[2]=s[2-1]+1=s[1]+1=2。须要上面子问题状态再加上1枚1元硬币
s[3]
须要多少枚硬币能够凑齐3元,此时的状况就比较复杂了。咱们有两种选择,固然是基于原来的状态求出。
以s[2]的状态基础在加一枚1元硬币。s[3]=s[3-1]+1
以s[0]的状态再加一枚3元硬币。s[3]=s[3-3]+1
如今的问题就是如何肯定全部选择。从前面的疾苦==记录能够看出,我都是有意识的在下标上作文章,故意将s[0]===s[3-3]
,这就是关键。每次咱们获取当前状态,不是要从紧邻的上一个状态递推,而是根据提供给咱们的硬币数值获得可能凑到当前数值的状况。
2元再加一枚1元硬币是如此,0元再加一枚3元硬币也是如此。当要求解s[5]的时候状况更加多变。候选变成了[s[4]+1,s[2]+1,s[0]+1]
。每一个子问题的求解都要尝试可能的求解路径状态。
4+1是一种状况,2+三、0+5是另外的状况。
那么s[3]的结果是哪个呢,根据问题须要求解最小的硬币枚数,显然结果是Min(s[2]+1,s[0]+1)
,是s[3]=1,须要1枚三元硬币
s[4]
须要多少枚硬币能够凑齐4元,s[4]=Min(s[3]+1,s[1]+1)
,s[4]=2,须要一枚3元硬币的基础上再加一枚1元硬币。
上面在s[3]的求解过程当中,实际上已经脱出了该问题的状态转移方程。
s[x]=Min(s[x-j]+1)
,其中j是提供的硬币面值。
x=input('plz input the value of x')
s=np.arange(int(x))
x_coins=(1,3,5)
for item in range(1,x-1):
for i in x_coins:
if item>=i and s[item-i]+1<s[item]:
s[item]=s[item-i]+1
print(s)