動態規劃算法
一、 概念:
每次決策依賴於當前狀態,又隨即引起狀態的轉移。
一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化策略解決問題的過程就成爲動態規劃。
二、 特性:
能採用動態規劃求解的問題的一般具有3個性質:
(1) 最優化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最優化原理。
(2) 無後效性:即某階段狀態一旦確定,就不受這個狀態以後決策的影響。也就是說,某狀態以後的過程不會影響以前的狀態,只與當前的狀態有關。
(3) 有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被多次使用到。
三、 基本思想:
動態規劃是一種將問題實例分解爲更小的、相似的子問題,並存儲子問題的解而避免計算重複的子問題,以解決最優化問題的算法策略。
該方法主要應用於最優化問題,這類問題會有多種可能的解,每個解都有一個值,而動態規劃找出其中最優(最大或最小)值的解。
若存在若干個取最優值的解的話,它只取其中一個。
在求解過程中,該方法也是通過求解局部子問題的解達到全局最優解,動態規劃允許這些子問題不獨立,也允許其通過自身子問題的解做出選擇,該方法對每個子問題只解一次,並將結果保存起來,避免每次碰到時都要重複計算。
四、 基本步驟:
動態規劃所處理的問題是一個多階段決策問題,一般由初始狀態開始,通過對中間階段決策的選擇,達到結束狀態。
這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線。
初始狀態à|決策1|à|決策2|à…à|決策n|à結束狀態
(1) 劃分階段:按照問題的時間或空間特徵,把問題分爲若干個階段。在劃分階段時,注意劃分後的階段一定要是有序的或者是可排序的,否則問題就無法求解。
(2) 確定狀態和狀態變量:將問題發展到各個階段時所處於的各種客觀情況用不同的狀態表示出來。狀態的選擇要滿足無後效性。
(3) 確定決策並寫出狀態轉移方程:因爲決策和狀態的轉移有着天然的聯繫,狀態轉移就是根據上一階段的狀態和決策來導出本階段的狀態。所以,如果確定了決策,狀態轉移方程也就可以寫出。但事實上常常是反過來做,根據相鄰的兩個階段的狀態之間的關係來確定決策方法和狀態轉移方程。
如:給定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))表示。
(4) 尋找邊界條件:給出的狀態轉移方程是一個遞推式,需要一個遞推的終止條件或邊界條件。
實際中,可以簡化步驟來進行設計:
(1) 分析最優解的性質,並刻畫其結構特徵。
(2) 遞歸的定義最優解。
(3) 以子底向上或自頂向下的記憶化方式(備忘錄法)計算出最優值。
(4) 根據計算最優值時得到的信息,構造問題的最優解。
五、 算法說明:
確定動態規劃的三要素:
(1) 問題的階段;
(2) 每個階段的狀態;
(3) 從前一個階段轉化到後一個階段之間的遞推關係。
遞推關係必須是從次小的問題開始到較大的問題之間的轉化,往往可以通過遞歸程序來實現。
確定了動態規劃的三要素,整個求解過程就可以用一個最優決策表來描述,最優決策表是一個二維表,其中行表示決策的階段,列表示問題狀態,表格需要填寫的數據一般對應此問題的在某個階段某個狀態下的最優值。
f(n,m) = max{f(n-1,m),f(n-1,m-w[n])+P(n,m)}
六、 複雜度:
動態規劃算法一般是n步疊代計算局部最優解,每一步疊代需要計算m個子項,那麼時間複雜度就是O(m*n)。如果只保存一步疊代的結果,空間複雜度就是O(m);
如果需要保存k步疊代結果,空間複雜度就是O(m*k)。
七、 應用領域:
常用軟件:MATLAB、LINGO
作用:在編程中常用解決最長公共子序列問題、矩陣連乘問題、凸多邊形最優三角剖分問題、電路佈線等問題。
一般來說,只要問題可以劃分成規模更小的子問題,並且原問題的最優解中包含了子問題的最優解,則可以考慮用動態規劃解決。
動態規劃問世以來,在工程技術、經濟管理等社會各個領域有着廣泛的應用,並且獲得了顯著的效果。
在經濟管理方面,動態規劃可以用來解決最優路徑問題、資源分配問題、生產調度問題、庫存管理問題、排序問題、設備更新問題以及生產過程最優控制問題等,是經濟管理中一種重要的決策技術。許多規劃問題用動態規劃的方法來處理,常比線性規劃或非線性規劃更有效。特別是對於離散的問題,由於解析數學無法發揮作用,動態規劃便成了一種非常有效的工具。
動態規劃可以按照決策過程的演變是否確定分爲確定性動態規劃和隨機性動態規劃;也可以按照決策變量的取值是否連續分爲連續性動態規劃和離散性動態規劃。雖然動態規劃主要用於求解以時間劃分階段的動態過程的優化問題,但是一些與時間無關的靜態規劃(如線性規劃、非線性規劃)。只要人爲的引進時間因素,把它視爲多階段決策過程,也可以用動態規劃方法方便的求解。
最經典的動態規劃算法案例
揹包問題(Knapsack Problem):
假設有一個揹包的負重最多可達8公斤,而希望在揹包中裝入負重範圍內可得之總價物品,假設是水果,水果編號、單價與重量如下所示:
編號 名稱 重量 單價
0 李子 4KG NT$4500
1 蘋果 5KG NT$5700
2 橘子 2KG NT$2250
3 草莓 1KG NT$1100
4 甜瓜 6KG NT$6700
解法:揹包問題是關於最佳化的問題,可以採用動態規劃進行解答,從空集合開始,每增加一個元素就先求出該階段的最佳解,直到所有的元素加入至集合中,最後得到的就是最佳解。
步驟:使用兩個陣列value和item,分別表示目前的最佳解所得之總價和最後一個放至揹包的水果,假設有負重量1~8的揹包8個,並對每個揹包求其最佳解。
逐步將水果放入揹包中,並求該階段的最佳解:
(1)放入李子:
揹包負重 1 2 3 4 5 6 7 8
value 0 0 0 450 450 450 450 900
item — — — 0 0 0 0 0
(2)放入蘋果:
揹包負重 1 2 3 4 5 6 7 8
value 0 0 0 450 570 570 570 900
item — — — 0 1 1 1 0
(3)放入橘子:
揹包負重 1 2 3 4 5 6 7 8
value 0 225 225 450 570 675 795 900
item — 2 2 0 1 2 2 0
(4)放入草莓:
揹包負重 1 2 3 4 5 6 7 8
value 110 225 335 450 570 680 795 905
item 3 2 3 0 1 3 2 3
(5)放入甜瓜:
揹包負重 1 2 3 4 5 6 7 8
value 110 225 335 450 570 680 795 905
item 3 2 3 0 1 3 2 3
由最後一個表格可以看出,在揹包負重8公斤時,最多可以裝入9050元的水果,而最後一個裝入的水果是3號,也就是草莓,裝入了草莓,揹包只能再放入7公斤的水果,所以必須看揹包負重7公斤的最佳解,最後一個放入的是2號,也就是橘子,現在揹包剩下負重量5公斤,所以看負重5公斤的最佳解,最後讓入的是1號,也就是蘋果,此時揹包負重量剩下0公斤,無法再放入水果,所以求出最佳解爲放入草莓、橘子與蘋果,而總價爲9050。
代碼實現:
#include <stdio.h>
#include <stdlib.h>
#define LIMIT 8 // 重量限制
#define N 5 // 物品種類
#define MIN 1 // 最小重量
struct body
{
char name[20];
int size;
int price;
};
typedef struct body object;
int main(void)
{
int item[LIMIT+1] = {0};
int value[LIMIT+1] = {0};
int newvalue, i, s, p;
object a[] = {{“李子”, 4, 4500},
{“蘋果”, 5, 5700},
{“橘子”, 2, 2250},
{“草莓”, 1, 1100},
{“甜瓜”, 6, 6700}};
for(i = 0; i < N; i++)
{
for(s = a[i].size; s <= LIMIT; s++)
{
p = s – a[i].size;
newvalue = value[p] + a[i].price;
if(newvalue > value[s]) // 找到階段最佳解
{
value[s] = newvalue;
item[s] = i;
}
}
}
for(i = LIMIT; i >= MIN; i = i – a[item[i]].size)
{
printf(“%s\t%d\n”,a[item[i]].name, a[item[i]].price);
}
printf(“合計\t%d\n”, value[LIMIT]);
return 0;
}