動態(tài)規(guī)劃是運(yùn)籌學(xué)中用于求解決策過程中的最優(yōu)化數(shù)學(xué)方法。當(dāng)然,我們在這里關(guān)注的是作為一種算法設(shè)計技術(shù),作為一種使用多階段決策過程最優(yōu)的通用方法。
動態(tài)規(guī)劃過程是:每次決策依賴于當(dāng)前狀態(tài),又隨即引起狀態(tài)的轉(zhuǎn)移。一個決策序列就是在變化的狀態(tài)中產(chǎn)生出來的,所以,這種多階段最優(yōu)化決策解決問題的過程就稱為動態(tài)規(guī)劃。
假設(shè)問題是由交疊的子問題所構(gòu)成,我們就能夠用動態(tài)規(guī)劃技術(shù)來解決它。一般來說,這種子問題出自對給定問題求解的遞推關(guān)系中,這個遞推關(guān)系包括了同樣問題的更小子問題的解。動態(tài)規(guī)劃法建議,與其對交疊子問題一次重新的求解,不如把每一個較小子問題僅僅求解一次并把結(jié)果記錄在表中(動態(tài)規(guī)劃也是空間換時間的)。這樣就能夠從表中得到原始問題的解。
動態(tài)規(guī)劃經(jīng)常常使用于解決最優(yōu)化問題,這些問題多表現(xiàn)為多階段決策。
關(guān)于多階段決策:在實(shí)際中,人們經(jīng)常遇到這樣一類決策問題,即因?yàn)檫^程的特殊性,能夠?qū)Q策的全過程根據(jù)時間或空間劃分若干個聯(lián)系的階段。而在各階段中。人們都須要作出方案的選擇。我們稱之為決策。而且當(dāng)一個階段的決策之后,經(jīng)常影響到下一個階段的決策,從而影響整個過程的活動。這樣,各個階段所確定的決策就構(gòu)成一個決策序列,常稱之為策略。因?yàn)楦鱾€階段可供選擇的決策往往不止一個。因而就可能有很多決策以供選擇,這些可供選擇的策略構(gòu)成一個集合,我們稱之為同意策略集合(簡稱策略集合)。每一個策略都對應(yīng)地確定一種活動的效果。我們假定這個效果能夠用數(shù)量來衡量。
因?yàn)椴煌牟呗越?jīng)常導(dǎo)致不同的效果,因此,怎樣在同意策略集合中選擇一個策略,使其在預(yù)定的標(biāo)準(zhǔn)下達(dá)到最好的效果。經(jīng)常是人們所關(guān)心的問題。我們稱這種策略為最優(yōu)策略,這類問題就稱為多階段決策問題。
多階段決策問題舉例:機(jī)器負(fù)荷分配問題
某種機(jī)器能夠在高低兩種不同的負(fù)荷下進(jìn)行生產(chǎn)。在高負(fù)荷下生產(chǎn)時。產(chǎn)品的年產(chǎn)量g和投入生產(chǎn)的機(jī)器數(shù)量x的關(guān)系為g=g(x),這時的年完善率為a,即假設(shè)年初完善機(jī)器數(shù)為x,到年終時完善的機(jī)器數(shù)為a*x(0<a<1);在低負(fù)荷下生產(chǎn)時,產(chǎn)品的年產(chǎn)量h和投入生產(chǎn)的機(jī)器數(shù)量y的關(guān)系為h=h(y)。對應(yīng)的完善率為b(0<b<0)。且a<b。
假定開始生產(chǎn)時完善的機(jī)器熟練度為s1。
要制定一個五年計劃,確定每年投入高、低兩種負(fù)荷生產(chǎn)的完善機(jī)器數(shù)量,使5年內(nèi)產(chǎn)品的總產(chǎn)量達(dá)到最大。
這是一個多階段決策問題。
顯然能夠?qū)⑷^程劃分為5個階段(一年一個階段),每一個階段開始時要確定投入高、低兩種負(fù)荷下生產(chǎn)的完善機(jī)器數(shù),并且上一個階段的決策必定影響到下一個階段的生產(chǎn)狀態(tài)。決策的目標(biāo)是使產(chǎn)品的總產(chǎn)量達(dá)到最大。這個問題常常使用數(shù)學(xué)方法建模,結(jié)合線性規(guī)劃等知識來進(jìn)行解決。
基本思想與分治法類似,也是將待求解的問題分解為若干個子問題(階段),按順序求解子階段,前一子問題的解,為后一子問題的求解提供了實(shí)用的信息。
在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達(dá)到最優(yōu)的局部解,丟棄其它局部解。依次解決各子問題,最后一個子問題就是初始問題的解。
因?yàn)閯討B(tài)規(guī)劃解決的問題多數(shù)有重疊子問題這個特點(diǎn)。為降低反復(fù)計算。對每個子問題僅僅解一次,將其不同階段的不同狀態(tài)保存在一個二維數(shù)組中。
與分治法最大的區(qū)別是:適合于用動態(tài)規(guī)劃法求解的問題,經(jīng)分解后得到的子問題往往不是互相獨(dú)立的(即下一個子階段的求解是建立在上一個子階段的解的基礎(chǔ)上,進(jìn)行進(jìn)一步的求解)。
能采用動態(tài)規(guī)劃求解的問題的一般要具有3個性質(zhì):
(1)最優(yōu)化原理:假設(shè)問題的最優(yōu)解所包括的子問題的解也是最優(yōu)的,就稱該問題具有最優(yōu)子結(jié)構(gòu),即滿足最優(yōu)化原理。
(2)無后效性:即某階段狀態(tài)一旦確定。就不受這個狀態(tài)以后決策的影響。也就是說,某狀態(tài)以后的過程不會影響曾經(jīng)的狀態(tài)。僅僅與當(dāng)前狀態(tài)有關(guān);
(3)有重疊子問題:即子問題之間是不獨(dú)立的,一個子問題在下一階段決策中可能被多次使用到(該性質(zhì)并非動態(tài)規(guī)劃適用的必要條件,可是假設(shè)沒有這條性質(zhì)。動態(tài)規(guī)劃算法同其它算法相比就不具備優(yōu)勢)。
四、求解的基本步驟
動態(tài)規(guī)劃所處理的問題是一個多階段決策問題,一般由初始狀態(tài)開始,通過對中間階段決策的選擇,達(dá)到結(jié)束狀態(tài)。這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線(通常是求最優(yōu)的活動路線)。如圖所示。動態(tài)規(guī)劃的設(shè)計都有著一定的模式,一般要經(jīng)歷以下幾個步驟。
初始狀態(tài)→│決策1│→│決策2│→…→│決策n│→結(jié)束狀態(tài)
圖1 動態(tài)規(guī)劃決策過程示意圖
(1)劃分階段:按照問題的時間或空間特征,把問題分為若干個階段。在劃分階段時,注意劃分后的階段一定要是有序的或者是可排序的,否則問題就無法求解。
(2)確定狀態(tài)和狀態(tài)變量:將問題發(fā)展到各個階段時所處于的各種客觀情況用不同的狀態(tài)表示出來。當(dāng)然,狀態(tài)的選擇要滿足無后效性。
(3)確定決策并寫出狀態(tài)轉(zhuǎn)移方程:因?yàn)闆Q策和狀態(tài)轉(zhuǎn)移有著天然的聯(lián)系,狀態(tài)轉(zhuǎn)移就是根據(jù)上一階段的狀態(tài)和決策來導(dǎo)出本階段的狀態(tài)。所以如果確定了決策,狀態(tài)轉(zhuǎn)移方程也就可寫出。但事實(shí)上常常是反過來做,根據(jù)相鄰兩個階段的狀態(tài)之間的關(guān)系來確定決策方法和狀態(tài)轉(zhuǎn)移方程。
(4)尋找邊界條件:給出的狀態(tài)轉(zhuǎn)移方程是一個遞推式,需要一個遞推的終止條件或邊界條件。
一般,只要解決問題的階段、狀態(tài)和狀態(tài)轉(zhuǎn)移決策確定了,就可以寫出狀態(tài)轉(zhuǎn)移方程(包括邊界條件)。
實(shí)際應(yīng)用中可以按以下幾個簡化的步驟進(jìn)行設(shè)計:
(1)分析最優(yōu)解的性質(zhì),并刻畫其結(jié)構(gòu)特征。
(2)遞歸的定義最優(yōu)解。
(3)以自底向上或自頂向下的記憶化方式(備忘錄法)計算出最優(yōu)值。
(4)根據(jù)計算優(yōu)值時得到的信息,構(gòu)造問題的最優(yōu)解。
有數(shù)組penny,penny中所有的值都為正數(shù)且不重復(fù)。每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定一個整數(shù)aim(小于等于1000)代表要找的錢數(shù),求換錢有多少種方法。
給定數(shù)組penny及它的大小(小于等于50),同時給定一個整數(shù)aim,請返回有多少種方法可以湊成aim。
測試樣例:
[1,2,4],3,3
返回:2
解析:設(shè)dp[n][m]為使用前n中貨幣湊成的m的種數(shù),那么就會有兩種情況:
使用第n種貨幣:dp[n-1][m]+dp[n-1][m-peney[n]]
不用第n種貨幣:dp[n-1][m],為什么不使用第n種貨幣呢,因?yàn)閜enney[n]>m。
這樣就可以求出當(dāng)m>=penney[n]時 dp[n][m] = dp[n-1][m]+dp[n][m-peney[n]],否則,dp[n][m] = dp[n-1][m]
代碼如下:
有一個矩陣map,它每個格子有一個權(quán)值。從左上角的格子開始每次只能向右或者向下走,最后到達(dá)右下角的位置,路徑上所有的數(shù)字累加起來就是路徑和,返回所有的路徑中最小的路徑和。
給定一個矩陣map及它的行數(shù)n和列數(shù)m,請返回最小路徑和。保證行列數(shù)均小于等于100.
測試樣例:
[[1,2,3],[1,1,1]],2,3
返回:4
解析:設(shè)dp[n][m]為走到n*m位置的路徑長度,那么顯而易見dp[n][m] = min(dp[n-1][m],dp[n][m-1]);
代碼如下:
有n級臺階,一個人每次上一級或者兩級,問有多少種走完n級臺階的方法。為了防止溢出,請將結(jié)果Mod 1000000007
給定一個正整數(shù)int n,請返回一個數(shù),代表上樓的方式數(shù)。保證n小于等于100000。
測試樣例:
1
返回:1
解析:這是一個非常經(jīng)典的為題,設(shè)f(n)為上n級臺階的方法,要上到n級臺階的最后一步有兩種方式:從n-1級臺階走一步;從n-1級臺階走兩步,于是就有了這個公式f(n) = f(n-1)+f(n-2);
代碼如下:
給定兩個字符串A和B,返回兩個字符串的最長公共子序列的長度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最長公共子序列。
給定兩個字符串A和B,同時給定兩個串的長度n和m,請返回最長公共子序列的長度。保證兩串長度均小于等于300。
測試樣例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
解析:設(shè)dp[n][m] ,為A的前n個字符與B的前m個字符的公共序列長度,則當(dāng)A[n]==B[m]的時候,dp[i][j] = max(dp[i-1][j-1]+1,dp[i-1][j],dp[i][j-1]),否則,dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
代碼如下: