貪婪算法的基本思想:通過一系列步驟來構造問題的解,每一步都是對已構造的部分解的一個擴展,直到獲得問題的完整解。
貪婪算法中,每一步“貪婪地” 選擇最好的部分解,但不顧及這樣選擇對整體的影響(局部最優),因此得到的全局解不一定最好的解,但對許多問題它能產生整體最優解。
具體算法描述:
1: void Knapsack(int n,float M, float v[], float w[], float x[])
2: {
3: Sort(n, v, w);
4: int i;
5: for(i = 1; i < n; i++)
6: x[i] = 0;
7: float c = M;
8: for(i = 1; i < n; i++)
9: {
10: if(w[i] > c)
11: break;
12: x[i] = 1;
13: c -= w[i];
14: }
15: if(i <=n)
16: x[i] = c/w[i];
17:
貪婪算法每一步需要滿足3個條件:
1.可行性:即必須滿足問題的約束。
2.局部最優:它是當前步驟中所有可行選擇中最佳的局部選擇。
3.不可取消:選擇一旦做出,在后面的步驟中就無法改變。
貪心算法的基本要素:
1.貪心選擇性質:指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。
2.最優子結構性質:指一個問題的最優解包含其子問題的最優解。
貪心算法與動態規划算法的異同:
相同點:都具有最優子結構性質。
不同點:動態規划算法通常以自底向上的方式解各子問題;而貪心算法則通常以自頂向下的方式進行;
下面研究2個經典的組合優化例題,並以此說明貪心算法與動態規划算法的主要差別。
0-1背包問題:
給定n種物品和一個背包。物品i的重量是Wi,其價值為Vi,背包的容量為C。應如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大?
背包問題:
與0-1背包問題類似,所不同的是在選擇物品i裝入背包時,可以選擇物品i的一部分,而不一定要全部裝入背包,1≤i≤n。
這2類問題都具有最優子結構性質,極為相似;但背包問題可以用貪心算法求解;而0-1背包問題卻不能用貪心算法求解。
對於0-1背包問題:
例:n=3 , w={10,20,30} ,v={60,100,120} ,c=30
什么是最好的部分解? ——不求單位價值。
按貪心法:選擇價值最大的放入 : 全部放入第3個物品,價值120。
但這並不是最好的, 若1,2 物品的放入,總價值160。
對於背包問題:
例:n=3 w={10,20,30} v={60,100,120} c=50
單位價值:v/w={6,5,4}
因此,第一次挑一號物品全部裝入, r=40,pv=60
第二次挑2號,全部裝入r=20,pv=160
第三次挑3號,部分裝入r=0,pv=160+80=240
相信大家也已經看出問題的所在了。事實上,在考慮0-1背包問題時,應比較選擇該物品和不選擇該物品所導致的最終方案,然后再作出最好選擇。所以對於0-1背包問題,適合采用動態規划來求解。
動態規划解0-1背包問題:
例:n=3 ,w={30,20,10} ,v={120, 100,60} ,c=50
m(i,j)是背包容量為j,可選擇物品為1, 2,…,i時的最優值
Max=220,放入第一種,第二種