1、基本概念
動態規劃過程是:每次決策依賴於當前狀態,又隨即引發狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,因此,這種多階段最優化決策解決問題的過程就稱爲動態規劃。
2、基本思想與策略
基本思想與分治法相似,也是將待求解的問題分解爲若干個子問題(階段),按順序求解子階段,前一子問題的解,爲後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各類可能的局部解,經過決策保留那些有可能達到最優的局部解,丟棄其餘局部解。依次解決各子問題,最後一個子問題就是初始問題的解。
因爲動態規劃解決的問題多數有重疊子問題這個特色,爲減小重複計算,對每個子問題只解一次,將其不一樣階段的不一樣狀態保存在一個二維數組中。
與分治法最大的差異是:適合於用動態規劃法求解的問題,經分解後獲得的子問題每每不是互相獨立的(即下一個子階段的求解是創建在上一個子階段的解的基礎上,進行進一步的求解)。
3、適用的狀況
能採用動態規劃求解的問題的通常要具備3個性質:
(1) 最優化原理:若是問題的最優解所包含的子問題的解也是最優的,就稱該問題具備最優子結構,即知足最優化原理。
(2) 無後效性:即某階段狀態一旦肯定,就不受這個狀態之後決策的影響。也就是說,某狀態之後的過程不會影響之前的狀態,只與當前狀態有關。
(3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被屢次使用到。(該性質並非動態規劃適用的必要條件,可是若是沒有這條性質,動態規劃算法同其餘算法相比就不具有優點)
4、求解的基本步驟
動態規劃所處理的問題是一個多階段決策問題,通常由初始狀態開始,經過對中間階段決策的選擇,達到結束狀態。這些決策造成了一個決策序列,同時肯定了完成整個過程的一條活動路線(一般是求最優的活動路線)。如圖所示。動態規劃的設計都有着必定的模式,通常要經歷如下幾個步驟。
初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態
圖1 動態規劃決策過程示意圖
(1)劃分階段:按照問題的時間或空間特徵,把問題分爲若干個階段。在劃分階段時,注意劃分後的階段必定要是有序的或者是可排序的,不然問題就沒法求解。
(2)肯定狀態和狀態變量:將問題發展到各個階段時所處於的各類客觀狀況用不一樣的狀態表示出來。固然,狀態的選擇要知足無後效性。
(3)肯定決策並寫出狀態轉移方程:由於決策和狀態轉移有着自然的聯繫,狀態轉移就是根據上一階段的狀態和決策來導出本階段的狀態。因此若是肯定了決策,狀態轉移方程也就可寫出。但事實上經常是反過來作,根據相鄰兩個階段的狀態之間的關係來肯定決策方法和狀態轉移方程。
(4)尋找邊界條件:給出的狀態轉移方程是一個遞推式,須要一個遞推的終止條件或邊界條件。
通常,只要解決問題的階段、狀態和狀態轉移決策肯定了,就能夠寫出狀態轉移方程(包括邊界條件)。
實際應用中能夠按如下幾個簡化的步驟進行設計:
(1)分析最優解的性質,並刻畫其結構特徵。
(2)遞歸的定義最優解。
(3)以自底向上或自頂向下的記憶化方式(備忘錄法)計算出最優值
(4)根據計算最優值時獲得的信息,構造問題的最優解
5、算法實現的說明
動態規劃的主要難點在於理論上的設計,也就是上面4個步驟的肯定,一旦設計完成,實現部分就會很是簡單。
使用動態規劃求解問題,最重要的就是肯定動態規劃三要素:
(1)問題的階段 (2)每一個階段的狀態
(3)從前一個階段轉化到後一個階段之間的遞推關係。
遞推關係必須是從次小的問題開始到較大的問題之間的轉化,從這個角度來講,動態規劃每每能夠用遞歸程序來實現,不過由於遞推能夠充分利用前面保存的子問題的解來減小重複計算,因此對於大規模問題來講,有遞歸不可比擬的優點,這也是動態規劃算法的核心之處。
肯定了動態規劃的這三要素,整個求解過程就能夠用一個最優決策表來描述,最優決策表是一個二維表,其中行表示決策的階段,列表示問題狀態,表格須要填寫的數據通常對應此問題的在某個階段某個狀態下的最優值(如最短路徑,最長公共子序列,最大價值等),填表的過程就是根據遞推關係,從1行1列開始,以行或者列優先的順序,依次填寫表格,最後根據整個表格的數據經過簡單的取捨或者運算求得問題的最優解。
f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}
6、動態規劃算法基本框架
代碼
1 for(j=1; j<=m; j=j+1) // 第一個階段
2 xn[j] = 初始值;
3
4 for(i=n-1; i>=1; i=i-1)// 其餘n-1個階段
5 for(j=1; j>=f(i); j=j+1)//f(i)與i有關的表達式
6 xi[j]=j=max(或min){g(xi-1[j1:j2]), ......, g(xi-1[jk:jk+1])};
8
9 t = g(x1[j1:j2]); // 由子問題的最優解求解整個問題的最優解的方案
10
11 print(x1[j1]);
12
13 for(i=2; i<=n-1; i=i+1)
15 {
17 t = t-xi-1[ji];
18
19 for(j=1; j>=f(i); j=j+1)
21 if(t=xi[ji])
23 break;
25 }
貪心算法
1、基本概念:
所謂貪心算法是指,在對問題求解時,老是作出在當前看來是最好的選擇。
也就是說,不從總體最優上加以考慮,他所作出的僅是在某種意義上的局部最優解。
貪心算法沒有固定的算法框架,算法設計的關鍵是貪心策略的選擇。必須注意的是,貪心算法不是對全部問題都能獲得總體最優解,選擇的貪心策略必須具有無後效性,即某個狀態之後的過程不會影響之前的狀態,只與當前狀態有關。
因此對所採用的貪心策略必定要仔細分析其是否知足無後效性。
2、貪心算法的基本思路:
1.創建數學模型來描述問題。
2.把求解的問題分紅若干個子問題。
3.對每一子問題求解,獲得子問題的局部最優解。
4.把子問題的解局部最優解合成原來解問題的一個解。
3、貪心算法適用的問題
貪心策略適用的前提是:局部最優策略能致使產生全局最優解。
實際上,貪心算法適用的狀況不多。通常,對一個問題分析是否適用於貪心算法,能夠先選擇該問題下的幾個實際數據進行分析,就可作出判斷。
4、貪心算法的實現框架
從問題的某一初始解出發;
while (能朝給定總目標前進一步)
{
利用可行的決策,求出可行解的一個解元素;
}
由全部解元素組合成問題的一個可行解;
5、貪心策略的選擇
由於用貪心算法只能經過解局部最優解的策略來達到全局最優解,所以,必定要注意判斷問題是否適合採用貪心算法策略,找到的解是否必定是問題的最優解。
6、例題分析
下面是一個能夠試用貪心算法解的題目,貪心解的確不錯,惋惜不是最優解。
[揹包問題]有一個揹包,揹包容量是M=150。有7個物品,物品能夠分割成任意大小。
要求儘量讓裝入揹包中的物品總價值最大,但不能超過總容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
價值 10 40 30 50 35 40 30
分析:
目標函數: ∑pi最大
約束條件是裝入的物品總重量不超過揹包容量:∑wi<=M( M=150)
(1)根據貪心的策略,每次挑選價值最大的物品裝入揹包,獲得的結果是否最優?
(2)每次挑選所佔重量最小的物品裝入是否能獲得最優解?
(3)每次選取單位重量價值最大的物品,成爲解本題的策略。
值得注意的是,貪心算法並非徹底不可使用,貪心策略一旦通過證實成立後,它就是一種高效的算法。
貪心算法仍是很常見的算法之一,這是因爲它簡單易行,構造貪心策略不是很困難。
惋惜的是,它須要證實後才能真正運用到題目的算法中。
通常來講,貪心算法的證實圍繞着:整個問題的最優解必定由在貪心策略中存在的子問題的最優解得來的。
對於例題中的3種貪心策略,都是沒法成立(沒法被證實)的,解釋以下:
(1)貪心策略:選取價值最大者。反例:
W=30
物品:A B C
重量:28 12 12
價值:30 20 20
根據策略,首先選取物品A,接下來就沒法再選取了,但是,選取B、C則更好。
(2)貪心策略:選取重量最小。它的反例與第一種策略的反例差很少。
(3)貪心策略:選取單位重量價值最大的物品。反例:
W=30
物品:A B C
重量:28 20 10
價值:28 20 10
根據策略,三種物品單位重量價值同樣,程序沒法依據現有策略做出判斷,若是選擇A,則答案錯誤。
一、概念
回溯算法實際上一個相似枚舉的搜索嘗試過程,主要是在搜索嘗試過程當中尋找問題的解,當發現已不知足求解條件時,就「回溯」返回,嘗試別的路徑。
回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步從新選擇,這種走不通就退回再走的技術爲回溯法,而知足回溯條件的某個狀態的點稱爲「回溯點」。
許多複雜的,規模較大的問題均可以使用回溯法,有「通用解題方法」的美稱。
二、基本思想
在包含問題的全部解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,若是包含,就從該結點出發繼續探索下去,若是該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)。
若用回溯法求問題的全部解時,要回溯到根,且根結點的全部可行的子樹都要已被搜索遍才結束。
而若使用回溯法求任一個解時,只要搜索到問題的一個解就能夠結束。
三、用回溯法解題的通常步驟:
(1)針對所給問題,肯定問題的解空間:
首先應明肯定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。
(2)肯定結點的擴展搜索規則
(3)以深度優先方式搜索解空間,並在搜索過程當中用剪枝函數避免無效搜索。
四、算法框架
(1)問題框架
設問題的解是一個n維向量(a1,a2,………,an),約束條件是ai(i=1,2,3,…..,n)之間知足某種條件,記爲f(ai)。
(2)非遞歸回溯框架
1: int a[n],i;
2: 初始化數組a[];
3: i = 1;
4: while (i>0(有路可走) and (未達到目標)) // 還未回溯到頭
5: {
6: if(i > n) // 搜索到葉結點
7: {
8: 搜索到一個解,輸出;
9: }
10: else // 處理第i個元素
11: {
12: a[i]第一個可能的值;
13: while(a[i]在不知足約束條件且在搜索空間內)
14: {
15: a[i]下一個可能的值;
16: }
17: if(a[i]在搜索空間內)
18: {
19: 標識佔用的資源;
20: i = i+1; // 擴展下一個結點
21: }
22: else
23: {
24: 清理所佔的狀態空間; // 回溯
25: i = i –1;
26: }
27: }
(3)遞歸的算法框架
回溯法是對解空間的深度優先搜索,在通常狀況下使用遞歸函數來實現回溯法比較簡單,其中i爲搜索的深度,框架以下:
1: int a[n];
2: try(int i)
3: {
4: if(i>n)
5: 輸出結果;
6: else
7: {
8: for(j = 下界; j <= 上界; j=j+1) // 枚舉i全部可能的路徑
9: {
10: if(fun(j)) // 知足限界函數和約束條件
11: {
12: a[i] = j;
13: ... // 其餘操做
14: try(i+1);
15: 回溯前的清理工做(如a[i]置空值等);
16: }
17: }
18: }
19: }
分支限界法
1、基本描述
相似於回溯法,也是一種在問題的解空間樹T上搜索問題解的算法。但在通常狀況下,分支限界法與回溯法的求解目標不一樣。回溯法的求解目標是找出T中知足約束條件的全部解,而分支限界法的求解目標則是找出知足約束條件的一個解,或是在知足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。
(1)分支搜索算法
所謂「分支」就是採用廣度優先的策略,依次搜索E-結點的全部分支,也就是全部相鄰結點,拋棄不知足約束條件的結點,其他結點加入活結點表。而後從表中選擇一個結點做爲下一個E-結點,繼續搜索。
選擇下一個E-結點的方式不一樣,則會有幾種不一樣的分支搜索方式。
1)FIFO搜索
2)LIFO搜索
3)優先隊列式搜索
(2)分支限界搜索算法
2、分支限界法的通常過程
因爲求解目標不一樣,致使分支限界法與回溯法在解空間樹T上的搜索方式也不相同。回溯法以深度優先的方式搜索解空間樹T,而分支限界法則以廣度優先或以最小耗費優先的方式搜索解空間樹T。
分支限界法的搜索策略是:在擴展結點處,先生成其全部的兒子結點(分支),而後再從當前的活結點表中選擇下一個擴展對點。爲了有效地選擇下一擴展結點,以加速搜索的進程,在每一活結點處,計算一個函數值(限界),並根據這些已計算出的函數值,從當前活結點表中選擇一個最有利的結點做爲擴展結點,使搜索朝着解空間樹上有最優解的分支推動,以便儘快地找出一個最優解。
分支限界法常以廣度優先或以最小耗費(最大效益)優先的方式搜索問題的解空間樹。問題的解空間樹是表示問題解空間的一棵有序樹,常見的有子集樹和排列樹。在搜索問題的解空間樹時,分支限界法與回溯法對當前擴展結點所使用的擴展方式不一樣。在分支限界法中,每個活結點只有一次機會成爲擴展結點。活結點一旦成爲擴展結點,就一次性產生其全部兒子結點。在這些兒子結點中,那些致使不可行解或致使非最優解的兒子結點被捨棄,其他兒子結點被子加入活結點表中。此後,從活結點表中取下一結點成爲當前擴展結點,並重覆上述結點擴展過程。這個過程一直持續到找到所求的解或活結點表爲空時爲止。
3、回溯法和分支限界法的一些區別
有一些問題其實不管用回溯法仍是分支限界法均可以獲得很好的解決,可是另一些則否則。也許咱們須要具體一些的分析——到底什麼時候使用分支限界而什麼時候使用回溯呢?
回溯法和分支限界法的一些區別:
方法對解空間樹的搜索方式 存儲結點的經常使用數據結構 結點存儲特性經常使用應用
回溯法深度優先搜索堆棧活結點的全部可行子結點被遍歷後才被從棧中彈出找出知足約束條件的全部解
分支限界法廣度優先或最小消耗優先搜索隊列、優先隊列每一個結點只有一次成爲活結點的機會找出知足約束條件的一個解或特定意義下的最優解