貪心法和動態規劃法的區別

鴨脖發表於2012-06-10
動態規劃和貪心演算法的區別
動態規劃和貪心演算法都是一種遞推演算法  均用區域性最優解來推導全域性最優解 

不同點: 
貪心演算法: 
1.貪心演算法中,作出的每步貪心決策都無法改變,因為貪心策略是由上一步的最優解推導下一步的最優解,而上一部之前的最優解則不作保留。 
2.由(1)中的介紹,可以知道貪心法正確的條件是:每一步的最優解一定包含上一步的最優解。 

動態規劃演算法: 
1.全域性最優解中一定包含某個區域性最優解,但不一定包含前一個區域性最優解,因此需要記錄之前的所有最優解 
2.動態規劃的關鍵是狀態轉移方程,即如何由以求出的區域性最優解來推導全域性最優解 
3.邊界條件:即最簡單的,可以直接得出的區域性最優解
==============================================================================
貪心演算法與動態規劃 
貪心法的基本思路:   
    
從問題的某一個初始解出發逐步逼近給定的目標,以儘可能快的地求得更好的解。當達到某演算法中的某一步不能再繼續前進時,演算法停止。   
該演算法存在問題:   
1.   不能保證求得的最後解是最佳的;   
2.   不能用來求最大或最小解問題;   
3.   只能求滿足某些約束條件的可行解的範圍。實現該演算法的過程:   
從問題的某一初始解出發;
   
while   能朝給定總目標前進一步   do   
求出可行解的一個解元素;   
由所有解元素組合成問題的一個可行解 

貪心演算法最經典的例子,給錢問題。   
比如中國的貨幣,只看元,有1元2元5元10元20、50、100   
    
如果我要16元,可以拿16個1元,8個2元,但是怎麼最少呢?   
如果用貪心算,就是我每一次拿那張可能拿的最大的。   
比如16,我第一次拿20拿不起,拿10元,OK,剩下6元,再拿個5元,剩下1元   
也就是3張   10、5、1。   
    
每次拿能拿的最大的,就是貪心。   
    
但是一定注意,貪心得到的並不是最優解,也就是說用貪心不一定是拿的最少的張數   
貪心只能得到一個比較好的解,而且貪心演算法很好想得到。   
再注意,為什麼我們的錢可以用貪心呢?因為我們國家的錢的大小設計,正好可以使得貪心演算法算出來的是最優解(一般是個國家的錢幣都應該這麼設計)。如果設計成別的樣子情況就不同了   
比如某國的錢幣分為   1元3元4元   
如果要拿6元錢   怎麼拿?貪心的話   先拿4   再拿兩個1     一共3張錢   
實際最優呢?   兩張3元就夠了   



求最優解的問題,從根本上說是一種對解空間的遍歷。最直接的暴力分析容易得到,最優解的解空間通常都是以指數階增長,因此暴力窮舉都是不可行的。
最優解問題大部分都可以拆分成一個個的子問題,把解空間的遍歷視作對子問題樹的遍歷,則以某種形式對樹整個的遍歷一遍就可以求出最優解,如上面的分析,這是不可行的。
貪心和動態規劃本質上是對子問題樹的一種修剪。兩種演算法要求問題都具有的一個性質就是“子問題最優性”。即,組成最優解的每一個子問題的解,對於這個子問題本身肯定也是最優的。如果以自頂向下的方向看問題樹(原問題作根),則,我們每次只需要向下遍歷代表最優解的子樹就可以保證會得到整體的最優解。形象一點說,可以簡單的用一個值(最優值)代表整個子樹,而不用去求出這個子樹所可能代表的所有值。
動態規劃方法代表了這一類問題的一般解法。我們自底向上(從葉子向根)構造子問題的解,對每一個子樹的根,求出下面每一個葉子的值,並且以其中的最優值作為自身的值,其它的值捨棄。動態規劃的代價就取決於可選擇的數目(樹的叉數)和子問題的的數目(樹的節點數,或者是樹的高度?)。
貪心演算法是動態規劃方法的一個特例。貪心特在,可以證明,每一個子樹的根的值不取決於下面葉子的值,而只取決於當前問題的狀況。換句話說,不需要知道一個節點所有子樹的情況,就可以求出這個節點的值。通常這個值都是對於當前的問題情況下,顯而易見的“最優”情況。因此用“貪心”來描述這個演算法的本質。由於貪心演算法的這個特性,它對解空間樹的遍歷不需要自底向上,而只需要自根開始,選擇最優的路,一直走到底就可以了。這樣,與動態規劃相比,它的代價只取決於子問題的數目,而選擇數目總為1。
------------------------------

找錢問題,可以很大程度上幫助我們理解動態規劃法語貪心演算法的區別

二、問題

      現只有面額為 11元、5元、1元的三種人民幣。

      給定一個 數目為 money 的人民幣,如何用這三種面額的人民幣 找開它,且用的人民幣張數最少

      如:給定 10元,我們可以有以下找法:

            2張  5元面額

            1張  5元面額  + 5 張  1元面額

            10張 1元面額

      我們 選擇第一種找法。只用兩張人民幣。

三、分析

 利用動態規劃法可以找到最優解。

        利用貪心演算法可以找到最優解(問題滿足貪心選擇性質時。該找錢問題在 11、5、1三種面額的情況下不滿足該性質)

              或者找到近似 最優解(在本題設定的三種面額的情況下 便是如此)

        如果現在要找開 15元錢,則

        1. 根據動態規劃法的解題思想,得到最優解為       3張  5元面額的 ,                                   總共 3張

        2. 根據貪心演算法的解題思想,得到的近似最優解為 1張 11元面額的  加上  4張 1元面額的,     總共 5張

        從這就可以大略的看到 兩個的區別

四、程式碼實現找錢問題的 動態規劃法與貪心演算法 兩種解法,形成對比

view plaincopy to clipboardprint?
01./**********************************************************  
02. *問  題:有最小面額為 11 5 1的三種人民幣,用最少的張數找錢  
03. *描  述:動態規劃與貪心演算法 解決問題 比較  
04. *作  者:JarvisChu  
05. **********************************************************/  
06.#include<stdio.h>   
07.#define N 4      
08.#define VALUE1 11                  //面額為 11元的人民幣 (可以修改)   
09.#define VALUE2 5                   //面額為  5元的人民幣 (可以修改)   
10.#define VALUE3 1                   //面額為  1元的人民幣 (不要修改,不然會有找不開的情況)   
11.#define MAX_MONEY 1000             //能找開的錢的上限   
12.  
13./***************************動態規劃法********************************  
14. *方法:  
15. *     int Num[MAX_MONEY];                  //Num[i]儲存要找開 i 元錢,需要的最小人民幣張數  
16. *     int Num_Value[N][MAX_MONEY];         //Num_Value[i][j] 表示 要找 j元錢,需要面額 VALUEi 的人民幣張數  
17. *  
18. *     Num[i] = i;          0<= i <=4  
19. *     Num[i] = min(Num[i-VALUE1]+1,Num[i-VALUE2]+1,Num[i-VALUE3]+1)  
20. */  
21.  
22.//-------------------------求最小值---------------------------------   
23.int min(int a,int b,int c){   
24.    return a<b ? (a<c ? a:c):(b<c ? b:c);   
25.}   
26.//-------------------------求最優值---------------------------------   
27.int DP_Money(int money,int Num[]){   
28.                                                         //獲得要找開money元錢,需要的人民幣總張數              
29.    int i;   
30.    for(i=0;i<=VALUE2;i++){                               //0~4 全用 1元    
31.        Num[i]=i;   
32.    }   
33.    for(i=VALUE2;i<=money;i++){                           //從5元開始 湊錢   
34.        if(i-VALUE1 >= 0){                                //如果比 11 元大,說明多了一種用11元面額人民幣的可能   
35.                                                         //從用 11元、5元、1元中 選擇一個張數小的   
36.            Num[i] = min(Num[i-VALUE1]+1,Num[i-VALUE2]+1,Num[i-VALUE3]+1);   
37.        }               
38.        else{                                            //從5元、1元中 選擇一個張數小的   
39.            Num[i] = (Num[i-VALUE2]+1) < (Num[i-VALUE3]+1) ? (Num[i-VALUE2]+1):(Num[i-VALUE3]+1);   
40.//          Num[i] = min(Num[i-VALUE2]+2,Num[i-VALUE2]+1,Num[i-VALUE3]+1);    
41.        }   
42.    }   
43.    return Num[money];   
44.}   
45.//-------------------------求最優解---------------------------------   
46.void BestChoice(int money,int Num[],int Num_Value[N][MAX_MONEY]){   
47.                                                           //要找 money 元錢,總人民幣張數放在Num[money]中   
48.                                                           //Num[1~3][money]分別儲存三種面額的張數   
49.    int i;                                                 
50.    for(i=0;i<VALUE2;i++){                            
51.        Num_Value[1][i] = 0;   
52.        Num_Value[2][i] = 0;   
53.        Num_Value[3][i] = i;   
54.    }   
55.    for(i=VALUE2;i<=money;i++){   
56.        if((i>=VALUE1) && (Num[i] == (Num[i-VALUE1]+1))){   //i 是由 i-11+11 構成  即i元是由 i-11元 加上一張面額11元的人民幣構成   
57.            Num_Value[1][i] = Num_Value[1][i-VALUE1]+1;     //多一張 11元面額人民幣   
58.            Num_Value[2][i] = Num_Value[2][i-VALUE1];       // 5元面額人民幣 張數一樣多   
59.            Num_Value[3][i] = Num_Value[3][i-VALUE1];       // 1元面額人民幣 張數一樣多   
60.        }   
61.        else if(Num[i] == (Num[i-VALUE2]+1)){               //i 是由 i-5+5 構成            
62.            Num_Value[1][i] = Num_Value[1][i-VALUE2];       //11元面額人民幣 張數一樣多   
63.            Num_Value[2][i] = Num_Value[2][i-VALUE2]+1;     //多一張 5元面額人民幣   
64.            Num_Value[3][i] = Num_Value[3][i-VALUE2];       // 1元面額人民幣 張數一樣多   
65.        }   
66.        else if(Num[i] == (Num[i-VALUE3]+1)){               //i 是由 i-1+1 構成      
67.            Num_Value[1][i] = Num_Value[1][i-VALUE3];       //11元面額人民幣 張數一樣多   
68.            Num_Value[2][i] = Num_Value[2][i-VALUE3];       // 5元面額人民幣 張數一樣多   
69.            Num_Value[3][i] = Num_Value[3][i-VALUE3]+1;     //多一張 1元面額人民幣   
70.        }   
71.        else{   
72.        }   
73.    }   
74.}   
75.  
76./***************************貪心演算法********************************  
77. *方法:  
78. *     Num_Value[i]表示 面額為VALUEi 的人民幣用的張數  
79. *     能用大面額的人民幣,就儘量用大面額  
80. */  
81.int Greed(int money,int Num_Value[]){   
82.                                                            //要找開 money元人民幣,Num_Value[1~3]儲存 三種面額人民幣的張數   
83.    int total=0;                                            //總張數,返回值也即是總張數。   
84.    Num_Value[1] = 0;   
85.    Num_Value[2] = 0;   
86.    Num_Value[3] = 0;   
87.    for(int i=money;i>=1;){   
88.        if(i >= VALUE1){   
89.            Num_Value[1]++;   
90.            i -= VALUE1;   
91.            total++;   
92.        }   
93.        else if(i >= VALUE2){   
94.            Num_Value[2]++;   
95.            i -= VALUE2;   
96.            total++;   
97.        }   
98.        else if(i >= VALUE3){   
99.            Num_Value[3]++;   
100.            i -= VALUE3;   
101.            total++;   
102.        }   
103.        else{   
104.        }   
105.    }   
106.    return total;   
107.}   
108.void main(){   
109.    //測試 動態規劃法   
110./*  int i;  
111.    int money = 23;  
112.    int Num[MAX_MONEY];                  //Num[i]儲存要找開 i 元錢,需要的最小人民幣張數  
113.    int Num_Value[N][MAX_MONEY];         //Num_Value[i][j] 表示 要找 j元錢,需要面額 VALUEi 的人民幣張數  
114.    printf("%d\n",DP_Money(money,Num));  
115.    printf("-------------------------------------------\n");  
116.    BestChoice(money,Num,Num_Value);  
117.    printf("-------------------------------------------\n");  
118.    for(i=0;i<=money;i++){  
119.        printf("Num[%d]=%4d, %3d, %3d, %3d\n",i,Num[i],Num_Value[1][i],Num_Value[2][i],Num_Value[3][i]);  
120.    }  
121.*/  
122.  
123.    //測試 貪心演算法   
124./*  int i;  
125.    int Num_Value_Greed[4];  
126.    for(i=0;i<=40;i++){                 //從0 元到 40 元的每一個找錢方式  
127.        Greed(i,Num_Value_Greed);  
128.        printf("%d---->>> %d,%d,%d\n",i,Num_Value_Greed[1],Num_Value_Greed[2],Num_Value_Greed[3]);  
129.    }  
130.*/  
131.       
132.    //比較兩個演算法   
133.    int i;   
134.    int dp,grd;                         //分別儲存動態規劃法和貪心演算法得到的人民幣總張數   
135.    int money;                          //要找的錢   
136.    int Num[MAX_MONEY];                 //Num[i]儲存要找i花費的銀幣的數目   
137.    int Num_Value[N][MAX_MONEY];        //Num_Value[i][j] 表示 要找 j 花費的 面值為 VALUEi 的硬幣 的數目   
138.    int Num_Value_Greed[N];             //Num_Value_Greed[i] 表示 面值為VALUEi 的人民幣 數目    
139.    money = 15;                         //可以為任意非負整型值(15 元是一個比較典型的可以區分兩種演算法的值)   
140.    dp = DP_Money(money,Num);          //動態規劃法   
141.    BestChoice(money,Num,Num_Value);   
142.    grd = Greed(money,Num_Value_Greed); //貪心演算法   
143.    printf("要找的錢 為:%d\n\n",money);   
144.    printf("  演算法    張數  11元  5元  1元\n");   
145.    printf("動態規劃  %-4d  %-4d  %-3d  %-3d\n",dp,Num_Value[1][money],Num_Value[2][money],Num_Value[3][money]);   
146.    printf("貪心演算法  %-4d  %-4d  %-3d  %-3d\n",grd,Num_Value_Greed[1],Num_Value_Greed[2],Num_Value_Greed[3]);   
147.} 

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/JarvisChu/archive/2010/12/05/6056963.aspx

相關文章