貪心算法講解

轉載自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2616926.html


貪心算法

 以下參照一張清華PPT課件

1.定義概覽

貪心算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。貪心算法不是對所有問題都能得到整體最優解,但對範圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。

貪心算法在有最優子結構的問題中尤爲有效。最優子結構的意思是局部最優解能決定全局最優解。簡單地說,問題能夠分解成子問題來解決,子問題的最優解能遞推到最終問題的最優解

算法思想:從問題的某一個初始解出發逐步逼近給定的目標,以儘可能快的地求得更好的解。當達到某算法中的某一步不能再繼續前進時,算法停止。

該算法存在問題:

1). 不能保證求得的最後解是最佳的;
2). 不能用來求最大或最小解問題;
3). 只能求滿足某些約束條件的可行解的範圍。

 Dijkstra算法、Prim算法和Kruskal算法都屬於典型的貪心算法

 

2.活動安排問題

設有n個活動的集合E={1,2,…,n},其中每個活動都要求使用同一資源,如演講會場等,而在同一時間內只有一個活動能使用這一資源。每個活動i都有一個要求使用該資源的起始時間si和一個結束時間fi,且si <fi 。如果選擇了活動i,則它在半開時間區間[si, fi)內佔用資源。若區間[si, fi)與區間[sj, fj)不相交,則稱活動i與活動j是相容的。也就是說,當si≥fj或sj≥fi時,活動i與活動j相容。 

 

在下面所給出的解活動安排問題的貪心算法greedySelector :

複製代碼
int greedySelector(int s[MAXNUM] , int f[MAXNUM], bool a[])  //s數組記錄着相應活動開始時間,f數組記錄着相應活動結束時間
{                                                            //各個活動按結束時間的非減序排列 
      int n=MAXNUM-1;
      a[1]=true;                                             //安排第一個活動
      int j=1,count=1;

      for (int i=2;i<=n;i++)
      {
        if (s[i]>=f[j])                                      //檢驗當前最早結束的活動的開始時間是否晚於前一個活動的結束結束時間
        {                                                    //如果晚於,則表示兩個活動相互兼容
            a[i]=true;                                       //標記爲true,表示已經安排
            j=i;                                             //記已經安排活動的個數
            count++;
        }
        else 
            a[i]=false;                                      //與已安排活動不兼容,標記此活動未安排
      }
      return count;
}
複製代碼

     由於輸入的活動以其完成時間的非減序排列,所以算法greedySelector每次總是選擇具有最早完成時間的相容活動加入集合A中。直觀上,按這種方法選擇相容活動爲未安排活動留下儘可能多的時間。也就是說,該算法的貪心選擇的意義是使剩餘的可安排時間段極大化,以便安排儘可能多的相容活動。

      算法greedySelector的效率極高。當輸入的活動已按結束時間的非減序排列,算法只需O(n)的時間安排n個活動,使最多的活動能相容地使用公共資源。如果所給出的活動未按非減序排列,可以用O(nlogn)的時間重排。

例:設待安排的11個活動的開始時間和結束時間按結束時間的非減序排列如下:

 

 

 

 

算法greedySelector 的計算過程如左圖所示。圖中每行相應於算法的一次迭代。陰影長條表示的活動是已選入集合A的活動,而空白長條表示的活動是當前正在檢查相容性的活動。

 

 

 

 

 

 

      若被檢查的活動i的開始時間Si小於最近選擇的活動j的結束時間fi,則不選擇活動i,否則選擇活動i加入集合A中。

      貪心算法並不總能求得問題的整體最優解。但對於活動安排問題,貪心算法greedySelector卻總能求得的整體最優解,即它最終所確定的相容活動集合A的規模最大。這個結論可以用數學歸納法證明

 

 

3.貪心算法基本要素

1).貪心選擇性質

貪心選擇性質是指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。這是貪心算法可行的第一個基本要素,也是貪心算法與動態規劃算法的主要區別。

在動態規劃算法中,每步所作的選擇往往依賴於相關子問題的解。因而只有在解出相關子問題後,才能作出選擇。而在貪心算法中,僅在當前狀態下作出最好選擇,即局部最優選擇。然後再去解作出這個選擇後產生的相應的子問題。貪心算法所作的貪心選擇可以依賴於以往所作過的選擇,但決不依賴於將來所作的選擇,也不依賴於子問題的解。正是由於這種差別,動態規劃算法通常以自底向上的方式解各子問題,而貪心算法則通常以自頂向下的方式進行,以迭代的方式作出相繼的貪心選擇,每作一次貪心選擇就將所求問題簡化爲一個規模更小的子問題。

對於一個具體問題,要確定它是否具有貪心選擇性質,我們必須證明每一步所作的貪心選擇最終導致問題的一個整體最優解。通常可以用我們在證明活動安排問題的貪心選擇性質時所採用的方法來證明。首先考察問題的一個整體最優解,並證明可修改這個最優解,使其以貪心選擇開始。而且作了貪心選擇後,原問題簡化爲一個規模更小的類似子問題。然後,用數學歸納法證明,通過每一步作貪心選擇,最終可得到問題的一個整體最優解。其中,證明貪心選擇後的問題簡化爲規模更小的類似子問題的關鍵在於利用該問題的最優子結構性質。

 

2).最優子結構性質

當一個問題的最優解包含着它的子問題的最優解時,稱此問題具有最優子結構性質。問題所具有的這個性質是該問題可用動態規劃算法或貪心算法求解的一個關鍵特徵。在活動安排問題中,其最優子結構性質表現爲:若a是對於正的活動安排問題包含活動1的一個最優解,則相容活動集合a’=a—{1}是對於e’={i∈e:si≥f1}的活動安排問題的一個最優解。

 

3).貪心算法與動態規劃算法的差異

貪心算法和動態規劃算法都要求問題具有最優子結構性質,這是兩類算法的一個共同點。大多數時候,能用貪心算法求解的問題,都可以用動態規劃算法求解。但是能用動態規劃求解的,不一定能用貪心算法進行求解。

 

4.0-1揹包問題和揹包問題

 

1).兩個問題的描述

  • 0-1揹包問題:

      給定n種物品和一個揹包。物品i的重量是Wi,其價值爲Vi,揹包的容量爲C。應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?

     在選擇裝入揹包的物品時,對每種物品i只有2種選擇,即裝入揹包或不裝入揹包。不能將物品i裝入揹包多次,也不能只裝入部分的物品i。

  • 揹包問題:

      與0-1揹包問題類似,所不同的是在選擇物品i裝入揹包時,可以選擇物品i的一部分,而不一定要全部裝入揹包,1≤i≤n。

這2類問題都具有最優子結構性質,極爲相似,但揹包問題可以用貪心算法求解,而0-1揹包問題卻不能用貪心算法求解。

 

2).用貪心算法解揹包問題的基本步驟:

      首先計算每種物品單位重量的價值Vi/Wi,然後,依貪心選擇策略,將儘可能多的單位重量價值最高的物品裝入揹包。若將這種物品全部裝入揹包後,揹包內的物品總重量未超過C,則選擇單位重量價值次高的物品並儘可能多地裝入揹包。依此策略一直地進行下去,直到揹包裝滿爲止。

代碼實現:

複製代碼
float knapsack(float c,float w[MAXNUM], float v[MAXNUM],float con[MAXNUM])
{
      int n=MAXNUM;
      float d[n],hascon=0,remain=c;

      int i;
      for (i = 0; i < n; i++) 
          d[i] = v[i]/w[i];                      //算出每個物品的平均價值

      sort(d,w,v,n);                             //按照平均價值對w,v進行排列,詳細代碼略

      for (i=0;i<n;i++)
          con[i]=0;                              //con記錄第i個物品完整度,如果爲0,未裝入,如果爲1整體裝入 0,1之間裝入部分

      for (i=0;i<n;i++) 
      {
          if (w[i]>remain )                      //如果第i個物品無法整體裝進保 則跳出循環
                break;
          con[i]=1;
          hascon+=v[i]                           //累加裝進包的物品總價值
          remain-=w[i];                          
      }
      if (i<n)
      {
           con[i]=remain/w[i];                   //計算包中剩餘部分還能容納多少價值 
           hascon+=con[i]*v[i];
       }

      return hascon;                             
}
複製代碼

 

     算法knapsack的主要計算時間在於將各種物品依其單位重量的價值從大到小排序。因此,算法的計算時間上界爲O(nlogn)。當然,爲了證明算法的正確性,還必須證明揹包問題具有貪心選擇性質。

      對於0-1揹包問題,貪心選擇之所以不能得到最優解是因爲在這種情況下,它無法保證最終能將揹包裝滿部分閒置的揹包空間使每公斤揹包空間的價值降低了。事實上,在考慮0-1揹包問題時,應比較選擇該物品和不選擇該物品所導致的最終方案,然後再作出最好選擇。由此就導出許多互相重疊的子問題。這正是該問題可用動態規劃算法求解的另一重要特徵。

     實際上也是如此,動態規劃算法的確可以有效地解0-1揹包問題

 

5.貪心算法的適用範圍:

貪心算法並不能總求得問題的整體最優解。但對於某些問題,卻總能求得整體最優解,這要看問題時什麼了。只要能滿足貪心算法的兩個性質:貪心選擇性質和最優子結構性質,貪心算法就可以出色地求出問題的整體最優解。即使某些問題,貪心算法不能求得整體的最優解,貪心算法也能求出大概的整體最優解。如果你的要求不是太高,貪心算法是一個很好的選擇。最優子結構性質是比較容易看出來的,但是貪心選擇性質就沒那麼容易了,這個時候需要證明。證明往往使用數學歸納法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章