貪心演算法精講

2021-06-20 18:57:00 字數 4802 閱讀 6923

一.貪心演算法的基本概念當乙個問題具有最優子結構性質時,我們會想到用動態規劃法去解它。但有時會有更簡單有效的演算法。我們來看乙個找硬幣的例子。假設有四種硬幣,它們的面值分別為二角五分、一角、五分和一分。現在要找給某顧客六角三分錢。這時,我們會不假思索地拿出2個二角五分的硬幣,1個一角的硬幣和3個一分的硬幣交給顧客。這種找硬幣方法與其他的找法相比,所拿出的硬幣個數是最少的。這裡,我們下意識地使用了這樣的找硬幣演算法:首先選出乙個面值不超過六角三分的最大硬幣,即二角五分;然後從六角三分中減去二角五分,剩下三角八分;再選出乙個面值不超過三角八分的最大硬幣,即又乙個二角五分,如此一直做下去。這個找硬幣的方法實際上就是貪心演算法。顧名思義,貪心演算法總是作出在當前看來是最好的選擇。也就是說貪心演算法並不從整體最優上加以考慮,它所作出的選擇只是在某種意義上的區域性最優選擇。當然,我們希望貪心演算法得到的最終結果也是整體最優的。上面所說的找硬幣演算法得到的結果就是乙個整體最優解。找硬幣問題本身具有最優子結構性質,它可以用動態規劃演算法來解。但我們看到,用貪心演算法更簡單,更直接且解題效率更高。這利用了問題本身的一些特性。例如,上述找硬幣的演算法利用了硬幣面值的特殊性。如果硬幣的面值改為一分、五分和一角一分3種,而要找給顧客的是一角五分錢。還用貪心演算法,我們將找給顧客1個一角一分的硬幣和4個一分的硬幣。然而3個五分的硬幣顯然是最好的找法。雖然貪心演算法不是對所有問題都能得到整體最優解,但對範圍相當廣的許多問題它能產生整體最優解。如圖的單源最短路徑問題,最小生成樹問題等。在一些情況下,即使貪心演算法不能得到整體最優解,但其最終結果卻是最優解的很好的近似解。

二.求解活動安排問題演算法

活動安排問題是可以用貪心演算法有效求解的乙個很好的例子。該問題要求高效地安排一系列爭用某一公共資源的活動。貪心演算法提供了乙個簡單、漂亮的方法使得盡可能多的活動能相容地使用公共資源。

設有n個活動的集合e=,其中每個活動都要求使用同一資源,如演講會場等,而在同一時間內只有乙個活動能使用這一資源。每個活動i都有乙個要求使用該資源的起始時間si和乙個結束時間fi,且si

i。如果選擇了活動i,則它在半開時間區間[si,fi]內占用資源。若區間[si,fi]與區間[sj,fj]不相交,則稱活動i與活動j是相容的。也就是說,當si≥fi或sj≥fj時,活動i與活動j相容。活動安排問題就是要在所給的活動集合中選出最大的相容活動子集合。

在下面所給出的解活動安排問題的貪心演算法gpeedyselector中,各活動的起始時間和結束時間儲存於陣列s和f為所給的活動集合。由於正中活動按結束時間的非減序排列,故活動1具有最早的完成時間。首先我們要證明活動安排問題有乙個最優解以貪心選擇開始,即該最優解中包含活動1。設是所給的活動安排問題的乙個最優解,且a中活動也按結束時間非減序排列,a中的第乙個活動是活動k。若k=1,則a就是乙個以貪心選擇開始的最優解。若k>1,則我們設。由於f1≤fk,且a中活動是互為相容的,故b中的活動也是互為相容的。又由於b中活動個數與a中活動個數相同,且a是最優的,故b也是最優的。也就是說b是乙個以貪心選擇活動1開始的最優活動安排。因此,我們證明了總存在乙個以貪心選擇開始的最優活動安排方案。

進一步,在作了貪心選擇,即選擇了活動1後,原問題就簡化為對e中所有與活動1相容的活動進行活動安排的子問題。即若a是原問題的乙個最優解,則a』=a—是活動安排問題的乙個最優解。事實上,如果我們能找到e』的乙個解b』,它包含比a』更多的活動,則將活動1加入到b』中將產生e的乙個解b,它包含比a更多的活動。這與a的最優性矛盾。因此,每一步所作的貪心選擇都將問題簡化為乙個更小的與原問題具有相同形式的子問題。對貪心選擇次數用數學歸納法即知,貪心演算法greedyselector最終產生原問題的乙個最優解。

四.貪心演算法的基本要素

貪心演算法通過一系列的選擇來得到乙個問題的解。它所作的每乙個選擇都是當前狀態下某種意義的最好選擇,即貪心選擇。希望通過每次所作的貪心選擇導致最終結果是問題的乙個最優解。這種啟發式的策略並不總能奏效,然而在許多情況下確能達到預期的目的。解活動安排問題的貪心演算法就是乙個例子。下面我們著重討論可以用貪心演算法求解的問題的一般特徵。

對於乙個具體的問題,我們怎麼知道是否可用貪心演算法來解此問題,以及能否得到問題的乙個最優解呢?這個問題很難給予肯定的回答。但是,從許多可以用貪心演算法求解的問題中

我們看到它們一般具有兩個重要的性質:貪心選擇性質最優子結構性質

1.貪心選擇性質

所謂貪心選擇性質是指所求問題的整體最優解可以通過一系列區域性最優的選擇,即貪心選擇來達到。這是貪心演算法可行的第乙個基本要素,也是貪心演算法與動態規劃演算法的主要區別。在動態規劃演算法中,每步所作的選擇往往依賴於相關子問題的解。因而只有在解出相關子問題後,才能作出選擇。而在貪心演算法中,僅在當前狀態下作出最好選擇,即區域性最優選擇。然後再去解作出這個選擇後產生的相應的子問題。貪心演算法所作的貪心選擇可以依賴於以往所作過的選擇,但決不依賴於將來所作的選擇,也不依賴於子問題的解。正是由於這種差別,動態規劃演算法通常以自底向上的方式解各子問題,而貪心演算法則通常以自頂向下的方式進行,以迭代的方式作出相繼的貪心選擇,每作一次貪心選擇就將所求問題簡化為乙個規模更小的子問題。

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

2.最優子結構性質

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

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

貪心演算法和動態規劃演算法都要求問題具有最優子結構性質,這是兩類演算法的乙個共同點。但是,對於乙個具有最優子結構的問題應該選用貪心演算法還是動態規劃演算法來求解?是不是能用動態規劃演算法求解的問題也能用貪心演算法來求解?下面我們來研究兩個經典的組合優化問題,並以此來說明貪心演算法與動態規劃演算法的主要差別。

五. 0-揹包問題

給定n種物品和乙個揹包。物品i的重量是w,其價值為v,揹包的容量為c.問應如何選擇裝入揹包中的物品,使得裝入揹包中物品的總價值最大? 在選擇裝入揹包的物品時,對每種物品i只有兩種選擇,即裝入揹包或不裝入揹包。不能將物品i裝入揹包多次,也不能只裝入部分的物品i。

此問題的形式化描述是,給定c>0,wi>0,vi>0,1≤i≤n,要求找出乙個n元0—1向

量(xl,x2,…,xn),,使得≤c,而且達到最大。

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

此問題的形式化描述是,給定c>0,wi>0,vi>0,1≤i≤n,要求找出乙個n元向量

(x1,x2,...xn),0≤xi≤1,1≤i≤n 使得≤c,而且達到最大。

這兩類問題都具有最優子結構性質。對於0—1揹包問題,設a是能夠裝入容量為c的揹包的具有最大價值的物品集合,則aj=a-是n-1個物品1,2,…,j—1,j+1,…,n可裝入容量為c-wi叫的揹包的具有最大價值的物品集合。對於揹包問題,類似地,若它的乙個最優解包含物品j,則從該最優解中拿出所含的物品j的那部分重量wi,剩餘的將是n-1個原重物品1,2,…,j-1,j+1,…,n以及重為wj-wi的物品j中可裝入容量為c-w的揹包且具有最大價值的物品。

雖然這兩個問題極為相似,但揹包問題可以用貪心演算法求解,而0·1揹包問題卻不能用貪心演算法求解。用貪心演算法解揹包問題的基本步驟是,首先計算每種物品單位重量的價值

vj/wi然後,依貪心選擇策略,將盡可能多的單位重量價值最高的物品裝入揹包。若將這種物品全部裝入揹包後,揹包內的物品總重量未超過c,則選擇單位重量價值次高的物品並盡可能多地裝入揹包。依此策略一直進行下去直到揹包裝滿為止。具體演算法可描述如下:

void knapsack(int n, float m, float v[ ], float w[ ], float x[ ] )

sort(n,v,w);

int i;

for(i= 1;i<= n;i++) x[i] = o;

float c = m;

for (i = 1;i < = n;i ++) {

if (w[i] > c) break;

x[i] = 1;

c-= w[i];

if (i < = n) x[i] = c/w[i];

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

這種貪心選擇策略對0—1揹包問題就不適用了。看圖2(a)中的例子,揹包的容量為50千克;物品1重10千克;價值60元;物品2重20千克,價值100元;物品3重30千克;價值120元。因此,物品1每千克價值6元,物品2每千克價值5元,物品3每千克價值4元。若依貪心選擇策略,應首選物品1裝入揹包,然而從圖4—2(b)的各種情況可以看出,最優的選擇方案是選擇物品2和物品3裝入揹包。首選物品1的兩種方案都不是最優的。對於揹包問題,貪心選擇最終可得到最優解,其選擇方案如圖2(c)所示。

對於0—1揹包問題,貪心選擇之所以不能得到最優解是因為它無法保證最終能將揹包裝滿,部分揹包空間的閒置使每千克揹包空間所具有的價值降低了。事實上,在考慮0—1揹包問題的物品選擇時,應比較選擇該物品和不選擇該物品所導致的最終結果,然後再作出最好選擇。由此就匯出許多互相重疊的於問題。這正是該問題可用動態規劃演算法求解的另一重要特徵。動態規劃演算法的確可以有效地解0—1揹包問題。

40講系列7 貪心演算法

貪心 greedy 演算法 在對問題求解時,總是做出在當前看來是最好的選擇。由於貪心演算法每一次操作都需要取最大值或最小值,所以通常需要對陣列排序。適用greedy 的場景 問題能夠分成子問題來解決,子問題的最優解能遞推到最終問題的最優解。如果不能使用貪心演算法,只需要舉出反例即可。貪心選擇性質的證...

經典貪心演算法 貪心演算法概述

貪心演算法具有最優子問題結構,它的特點是 短視 每次選擇對當前局面最有利的決策,來一步步獲得最優解。我個人認為,貪心不是乙個具體的方法,而是一類方法,貪心演算法的關鍵不在於想到,而在於正確性的證明。要證明乙個貪心演算法是正確的,需要證明我們可以把乙個最優解逐步轉化為我們用貪心演算法所得到的解,而解不...

演算法 貪心演算法

把乙個複雜問題分解為一系列較為簡單的區域性最優選擇,每乙個選擇都是對當前解的乙個擴充套件,知道獲得問題的完整解。在解決問題的策略上目光短淺,只根據當前已有的資訊做出選擇,而且一旦做出了選擇,不管將來有什麼結果這個選擇都不會改變。換言之,貪心法並不是從整體最優考慮,它所做出的選擇只是在某種意義上的區域...