貪心算法通常是用來解決優化問題的。它們通常由一個試圖找到局部最優解的迭代過程組成。貪心算法是在不考慮未來的情況下,在少量計算的基礎上做出正確的猜測。它一步一步地構建解決方案。每一步都增加了局部解的大小,並基於局部優化。所做的選擇是在保持可行性的同時產生最大的即時收益。這些算法通常是有效的。最難的部分是證明算法確實解決了這個問題。
對於揹包問題,貪心策略:對每一項計算yi=vi/si。將物品按比例遞減排序,並儘可能地從第一件物品,然後是第二件物品,以此類推。特點:該算法由一個簡單的迭代過程組成,在保持可行性的同時,選擇產生最大即時收益的項目。
貪心算法的基本步驟:
solution=f
while not finish(solution)
x=select(A)
if (feasible(solution,x))
solution=solution+x
end if
end while
return solution
通過局部最優(貪婪)選擇,可以得到全局最優解。
當我們考慮要做哪個選擇時,我們先做出當前問題中看起來最好的選擇,然後在做出選擇後解決所產生的子問題。到目前爲止,選擇可能取決於選擇,但它不能取決於任何未來的選擇或子問題的解決方案。它通常以自頂向下的方式進行,做出一個又一個貪婪的選擇,將每個給定的問題實例減少到一個更小的實例。
在動態規劃中,我們在每一步都做一個選擇,但這個選擇通常取決於子問題的解決方案。因此,我們以自底向上的方式解決動態規劃問題,從較小的子問題進展到較大的子問題。
但我們必須證明,每一步的貪婪選擇都會產生全局最優解。
如果問題的最優解包含子問題的最優解,則該問題具有最優子結構。
在0-1揹包問題中,當我們考慮要將一個項目包含在揹包中時,我們必須將包含該項目的子問題的解決方案與排除該項目的子問題的解決方案進行比較,然後才能做出選擇。用這種方式表示的問題會產生許多重疊的子問題——這是動態規劃的一個特點。
最短路徑問題
設G=(V,E)是一個有向圖,其中每條邊都有一個非負長度,且有一個被稱爲源的特異頂點s。然後確定s到V中每個頂點的距離,其中頂點s到頂點x的距離定義爲s到x的最短路徑的長度。
Dijkstra算法概述
1.X←{1};Y←V-{1}
2.對每個v∈Y,如果存在從1到v的邊,則令(v的標記)爲邊的長度:否則令,並設=0.
3.while Y≠{}
4.令y∈Y,使得爲最小
5.將y從Y移到X
6.更新那些在Y中與y相鄰的頂點的標記
7. end while
DIJKSTRA
Input: 含權有向圖 G=(V,E), V={1,...,n}
Output: G中頂點1到其他頂點的距離
X={1}; Y←V-{1}; λ[1]←0
for y←2 to n
if y 相鄰於1 then λ[y]←length[1,y]
else λ[y]←∞
end if
end for
for j←1 to n-1
令 y∈Y 使得 λ[y]最小(第8步)
X←X∪{y} {將頂點 y 加入X}
Y←Y∪{y} {將頂點y 從 Y中刪除}
for 每條邊 (y,w)
if w∈Y and λ[y]+length[y,w]<λ[w] then
λ[w]←λ[y]+length[y,w]
end for
end for
正確性:在算法DIJKSTRA中,當頂點y在第8步中被選中,如果標記是有限的,那麼。
時間複雜性:給出一個邊具有非負權的有向圖G和源頂點s,算法在時間內找到從s到其他每一個頂點距離的長度。
文件壓縮
假設我們有一個文件,它是一個字符串。儘可能地壓縮文件,以便能夠重新構造原始文件。設文件中的字符集爲C={c1,c2,…,cn},
f(ci), 1 ≤i≤n, 字符 ci 在文件中的頻率。由於某些字符的頻率可能比其他字符大得多,所以使用可變長度編碼是合理的。當編碼長度不同時,我們規定一個字符的編碼不能是另一個字符編碼的前綴,這種代碼稱爲前綴代碼。(防止二義性)
考慮Huffman樹找出一個文件的前綴碼,文件由字母a,b,c,d和e組成,頻度爲f(a)=20,f(b)=7,f(c)=10,f(d)=4和f(e)=18,每個葉節點用它相應的字符和該字符在文件中出現的頻度標記,每個內部節點用它子樹中的葉權之和以及它建立的序標記。
從這顆二叉樹可得,a,b,c,d,e的編碼分別爲01,110,10,111,00。假如在壓縮前每一個字符由3爲二進制數字表示,那麼原來文件的大小爲3(20+7+10+4+18)=177比特,而用上面的編碼壓縮的文件大小變成2x20+3x7+2x10+3x4+2x18=129比特,節省了大約27%。
Huffman編碼
Input:n個字符的集合C={c1,...,cn}和頻度{f(c1),...,f(cn)}
Output:C的Huffman樹(V,T)
根據頻度將所有字符插入最小堆H
V←C; T={}
for j←1 to n-1
c←DELETEMIN(H)
c’←DELETEMIN(H)
f(v)←f(c)+f(c’) {v 是一個新節點}
INSERT(H,v)
V=V∪{v} {添加v 到 V}
T=T∪{(v,c),(v,c’)} {使c和c’成爲T的孩子}
end for
時間複雜度爲O(nlogn)。
正確性:
引理—貪婪選擇屬性:令x和y爲C中頻率最低的兩個字符,然後存在一個C的最優前綴代碼,其中x和y的碼字長度相同,僅差最後一位。
該引理表明,通過合併建立最優樹的過程可以從貪婪選擇合併這兩個頻率最低的字符開始。
引理—最優子結構性質:設x和y爲C語言中頻率最小的兩個字符。設C '爲字母C去掉字母x、y,加入(新)字母z。f(z)=f(x)+f(y),設T '是表示C '的最優前綴代碼的任意樹。然後,由T '得到的樹T,用一個內部節點替換z的葉子節點,其中x和y爲子節點,表示C的最優前綴代碼。
定理(正確性):算法產生一個最優的前綴代碼。
擬陣
擬陣是滿足以下條件的有序對M=(S,L):
1.S是有限非空集
2.L 具有繼承性: L 是由 S的子集組成的集合, 稱爲 S的獨立子集 , 如果 B∈L 然後 AB, A∈L,顯然L
3.M 滿足交換性: If A∈L, B∈L 然後 |A|<|B|, x∈B-A,AU{x}∈L。
圖擬陣
給定無向圖G=(V,E),則圖形擬陣定義爲
1.=E, G的邊的集合
2.如果A是E的子集 則 當且僅當A非循環的。一組邊A是獨立的,當且僅當子圖構成一個森林。
定理M1:如果圖G=(V,E)是無向的,則是擬陣
擬陣不變量
定義: 給一個擬陣M=(S,L), 把一個元素 xA稱爲 AL的一個擴張,如果x可以添加到A同時保持獨立。也就是說,如果 A{x}L那麼x 是一個擴展。
定義:如果矩陣M中一個獨立子集A不能進行擴張,則稱其爲最大。
定理:一個擬陣中所有的極大獨立子集大小相同。
例如:考慮一個連通的無向圖G的圖形擬陣,的每個最大獨立子集必須是一個恰好具有|V|-1邊的樹,這些邊連接G的所有頂點,這被稱爲G的生成樹。
加權擬陣
定義: 對於每個元素x,xS,分配一個嚴格的正權值w(x),那麼擬陣M=(S,L)是一個加權擬陣。權值w通過求和擴展到S的子集,對任何。
例:設w(e)爲圖形擬陣MG中邊e的長度,則w(a)爲a中邊的權值之和。
許多由貪心方法提供最優解的問題可以用在加權擬陣中尋找最大權獨立子集的方法來表示。
給出加權擬陣 M=(S,L),我們希望找到一組獨立A∈L使得w(A)是最大的,我們稱這樣一個具有最大可能權值的獨立子集爲擬陣的最優子集。因爲權值是正的,所以最優子集總是一個最大的獨立子集——使A儘可能大總是有幫助的。
最小生成樹
MST:給定一個連通的無向圖G=(V,E)和一個長度函數w,使得w(E)是邊E的長度。找出將所有頂點連接在一起且總長度最小的邊的子集。
解:考慮權函數w ' (e)=w0-w(e)的加權擬陣MG,其中w0大於任意邊的最大長度。每個極大獨立子集A對應一個生成樹,w ' (A)=(|V|-1)w0-w(A)
任何能夠在任意加權擬陣中找到最優子集A的算法都可以求解MST。
定義:令G=(V,E)爲邊有權值的連通無向圖。G的生成樹(V,T)是G的子圖,它是樹。如果G是加權的,且T中所有邊的權值之和是最小的,則(V,T)稱爲最小代價生成樹或簡稱爲最小生成樹。
GENERIC-MST(G,w) A←Ø while A does not form a spanning tree find an edge (u,v) that is safe for A A←A∪{(u,v)} end while return A
接下來,我們將提供一個安全邊的識別規則,這兩種算法都有效的利用了這個規則。
Cut(割)和light edge(輕邊)
定義: 無向圖G=(V,E)的cut(S,V-S)是一個頂點分區,分爲S和V。 一條邊(u,v)∈E 穿過cut(即邊的兩點一個在集合S中,一個集合V-S中) 。
定義:如果邊的權值在所有穿過cut的邊中最小,則邊是穿過cut的輕邊。(cd爲輕邊)
安全邊
定理:給定一個邊有權值的連通無向圖G=(V,E),設A是G的某個MST中包含的E的子集,設(S, v -S)是G的任意cut並不妨礙A,設(u,v)是穿過(S, v -S)的一條輕邊。那麼邊(u,v)對於A是安全的。
推論:給定一個邊有權值的連通無向圖G=(V,E)。設A爲G的某MST中包含的E的子集,設C=(Vc,Ec)爲森林GA=(V,A)中的連通分量(樹)。如果(u,v)是GA中連接C和其他組件的輕邊,則(u,v)對於a是安全的
Kruskal算法:集合A是一個森林。添加到A中的安全邊始終是圖中連接兩個不同組件的最小權值邊。
Algorithm KRUSKAL 輸入:一個有n個頂點的加權連通無向圖G=(V,E) 輸出:G最小生成樹的邊T集合 Sort the edges in E by nondecreasing weight for each vertex vV MAKESET({v}) end for T={} while |T|<n-1 Let (x,y) be the next edge in E if FIND(x)FIND(y) then Add (x,y) to T UNION(x,y) end if end while
Prim算法:集合A形成一個單獨的樹。添加到A中的安全邊始終是連接樹與不在樹中的頂點的最小權值邊。
Algorithm PRIM 輸入:一個加權連通無向圖G=(V,E),其中V={1,2,…,n} 輸出:G最小生成樹的邊T集合 T←{}; X←{1}; Y←V-{1} for y←2 to n if y adjacent to 1 then N[y]←1, C[y]←c[1,y] else C[y]←∞ end for for j←1 to n-1 {find n-1 edges} Let y∈Y be such that C[y] is minimum T←T∈{(y,N[y])} {add edge (y,N[y]) to T} X←X∈{y}, Y←Y-{y} {move vertex y from Y to X} for each vertex wY that is adjacent to y if c[y,w]<C[w] then N[w]←y, C[w]←c[y,w] end for end for
貪心算法之所以能有效地求出MST,是因爲圖的森林集形成了一個圖陣。
依次考慮每個xS單調減少權值 , 如果 A{x} 是獨立的,立即將其添加到集合A積累。
Greedy(M,w) A←Ø {Now A is independent} sort S into monotonically decreasing order by weight w for each x∈S, take in monotonically decreasing order by weight w(x) if A∪{x}∈L then A←A∪{x} {Now A is still independent} end for return A
時間複雜度是O(nlogn+nf(n))。第四步需要檢查是否集合 A∪{x} 獨立, 時間成本爲 O(f(n))。
證明A爲最優子集合(貪心選擇性質和最優子結構性質)
引理:假設x是S的第一個元素,使得{x}是獨立的,如果存在這樣的x的話。如果x存在,那麼存在一個包含x的S的最優子集A。
引理: 對於任何擬陣M=(S,L), 如果 x∈S 是一些S的獨立的子集 A的擴張, 然後x也是的擴張。
推論:令M=(S,L)爲任意擬陣。如果x∈S並且不是的擴展,那麼不是任何獨立的子集A的擴張。
注意:這個推論表明,任何不能立即使用的元素都永遠不會被使用。所以GREEDY從不出錯當跳過S中任何初始元素,其不是的擴張。
引理: 通過GREEDYx∈S被選擇爲第一 個元素在加權擬陣 M=(S,L)中。求包含x的最大權無關子集的剩餘問題可歸結爲求權擬陣M ' =(S ',L ')的最大權無關子集,其中
S’={y∈S|{x,y}∈L}
L’={BS-{x}|B{x}∈L}M '的權函數是M的權函數,僅限於S '(我們稱M '爲M / x的縮寫)
定理:若M=(S,L)是具有權函數w的加權擬陣,則GREEDY(M,w)返回一個最優子集