貪心演算法(又稱貪婪演算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。貪心演算法不是對所有問題都能得到整體最優解,但對範圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。 ...
一.貪心演算法
1.貪心演算法概念
貪婪演算法(Greedy algorithm)是一種對某些求最優解問題的更簡單、更迅速的設計技術。用貪婪法設計演算法的特點是一步一步地進行,常以當前情況為基礎根據某個優化測度作最優選擇,而不考慮各種可能的整體情況,它省去了為找最優解要窮盡所有可能而必須耗費的大量時間,它採用自頂向下,以迭代的方法做出相繼的貪心選擇,每做一次貪心選擇就將所求問題簡化為一個規模更小的子問題,通過每一步貪心選擇,可得到問題的一個最優解,雖然每一步上都要保證能獲得局部最優解,但由此產生的全局解有時不一定是最優的,所以貪婪法不要回溯。
貪婪演算法是一種改進了的分級處理方法。其核心是根據題意選取一種量度標準。然後將這多個輸入排成這種量度標準所要求的順序,按這種順序一次輸入一個量。如果這個輸入和當前已構成在這種量度意義下的部分最佳解加在一起不能產生一個可行解,則不把此輸入加到這部分解中。這種能夠得到某種量度意義下最優解的分級處理方法稱為貪婪演算法。
2.貪心演算法求解思路
基本思路:①建立數學模型來描述問題。②把求解的問題分成若幹個子問題。③對每一子問題求解,得到子問題的局部最優解。④把子問題的解局部最優解合成原來解問題的一個解。
實現該演算法的過程:從問題的某一初始解出發;while 能朝給定總目標前進一步 do;求出可行解的一個解元素;由所有解元素組合成問題的一個可行解。
3.利用貪婪策略,需要解決的兩個問題
確定問題是否能用貪心策略求解;一般來說,適用於貪心策略求解的問題具有以下特點:貪心選擇性質和最優子結構性質。
① 貪心選擇性質:可通過局部的貪心選擇來達到問題的全局最優解。運用貪心策略解題,一般來說需要一步步的進行多次的貪心選擇。在經過一次貪心選擇之後,原問題將變成一個相似的,但規模更小的問題,而後的每一步都是當前看似最佳的選擇,且每一個選擇都僅做一次。
② 最優子結構性質:原問題的最優解包含子問題的最優解,即問題具有最優子結構的性質。在背包問題中,第一次選擇單位質量最大的貨物,它是第一個子問題的最優解,第二次選擇剩下的貨物中單位重量價值最大的貨物,同樣是第二個子問題的最優解,依次類推。
二.背包問題
背包問題(Knapsack problem)是一種組合優化的NP完全問題。問題可以描述為:給定一組物品,每種物品都有自己的重量和價格,在限定的總重量內,我們如何選擇,才能使得物品的總價格最高。問題的名稱來源於如何選擇最合適的物品放置於給定背包中。相似問題經常出現在商業、組合數學,計算複雜性理論、密碼學和應用數學等領域中。也可以將背包問題描述為決定性問題,即在總重量不超過W的前提下,總價值是否能達到V?它是在1978年由Merkel和Hellman提出的。
題目:有N件物品和一個容量為V的背包。第i件物品的重量是w[i],價值是v[i]。求解將哪些物品裝入背包可使這些物品的重量總和不超過背包容量,且價值總和最大。
三.貪心演算法求解背包問題
1 import java.util.Arrays; 2 3 /** 4 * 貪心演算法--->背包問題 5 * 假設有一個背包最大可以裝 150kg 物品 6 * 某物品 重量 價值(元) 性價比(單位重量的價格) 7 * a 35kg 10 10/35 8 * b 30kg 40 40/30 9 * c 60kg 30 30/60 10 * d 50kg 50 50/50 11 * e 40kg 35 35/40 12 * f 10kg 40 40/10 13 * g 25kg 30 30/25 14 * 15 * 求:背包可以裝入的最大價值? 16 * @author Administrator 17 * 18 */ 19 public class Knapsack { 20 /** 21 * 背包最大容量 22 */ 23 private static int KNAPSACK_MAX_CAPACITY=150; 24 /** 25 * 保存各個物品的重量 26 */ 27 private static int [] goods_weights=new int [] {35,30,60,50,40,10,25}; 28 /** 29 * 保存各個物品的價值 30 */ 31 private static int [] goods_values=new int[] {10,40,30,50,35,40,30}; 32 33 /** 34 * 貪婪演算法實現背包問題求解 35 * @param capacity 背包容量 36 * @param weights 各個物品的重量 37 * @param values 各個物品的價值 38 */ 39 private void knapsackGreedy(int capacity,int weights[],int values[]) { 40 int n=weights.length; //物品的數量 41 42 Double[] r=new Double[n]; //保存性價比的數組 43 int [] index=new int[n]; //保存按性價比排序的物品的下標 44 //計算得到各個物品的性價比 45 for (int i = 0; i < n; i++) { 46 r[i]=(double)values[i]/weights[i]; 47 index[i]=i; //初始化各個物品的預設性價比排序 48 } 49 50 //對各個物品的性價比進行排序 51 for(int i=0;i<r.length-1;i++) { 52 for(int j=i+1;j<r.length;j++) { 53 if(r[i]<r[j]) { 54 double temp=r[i]; 55 r[i]=r[j]; 56 r[j]=temp; 57 58 //將排序後性價比的下標更新為性價比排序後的位置 59 int x=index[i]; 60 index[i]=index[j]; 61 index[j]=x; 62 } 63 } 64 } 65 66 //將排序好的重量和價值分別保存到數組 67 int [] w1=new int[n]; 68 int [] v1=new int[n]; 69 for(int i=0;i<n;i++) { 70 w1[i]=weights[index[i]]; 71 v1[i]=values[index[i]]; 72 } 73 74 //將物品裝入背包 75 //記錄哪些物品已經被裝入背包 0 沒有裝入背包 1 代表已經裝入背包 76 int [] x=new int[n]; 77 int maxValue=0; 78 for(int i=0;i<n;i++) { 79 if(w1[i]<=capacity) { 80 //還可以裝的下 81 x[i]=1; //表示將該物品裝入背包 82 System.out.println("物品:"+w1[i]+" 被放進了"); 83 maxValue+=v1[i]; 84 capacity-=w1[i]; 85 } 86 } 87 System.out.println("總共放下的物品的數量為:"+Arrays.toString(x)); 88 System.out.println("最大價值為:"+maxValue); 89 } 90 91 92 public static void main(String[] args) { 93 Knapsack k=new Knapsack(); 94 k.knapsackGreedy(KNAPSACK_MAX_CAPACITY, goods_weights, 95 goods_values); 96 } 97 }
四.測試結果
物品:10 被放進了 物品:30 被放進了 物品:25 被放進了 物品:50 被放進了 物品:35 被放進了 總共放下的物品的數量為:[1, 1, 1, 1, 0, 0, 1] 最大價值為:170