動態規劃(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)