算法导论之贪心算法(Huffman编码和拟阵)
貪心算法,在解決最優(yōu)化問題上,通過得到子問題的局部最優(yōu)解來合成問題的一個解,以局部最優(yōu)選擇來輸出一個全局最優(yōu)解。
問題要用貪心算法來求解,需滿足和動態(tài)規(guī)劃一樣的最優(yōu)子結(jié)構(gòu)特征,同時還需要再每個子問題最優(yōu)解選擇上具有貪心性質(zhì)。所謂貪心性質(zhì),就是本來一個問題分成兩個子問題求解,但現(xiàn)在只需要選擇一個來求解,而這個子問題的局部最優(yōu)就是全局最優(yōu)。貪心了,只要解決一個子問題就解決了整個問題。
通過活動選擇問題來說明如何通過動態(tài)規(guī)劃解轉(zhuǎn)化為貪心解。活動選擇問題在現(xiàn)實中有很多實際應(yīng)用場合,記得在大學(xué)時,各類學(xué)生社團(tuán)經(jīng)常要共用某一類公共場合來舉辦活動或會議,這個時候就要協(xié)調(diào)出合理的安排,最大化滿足社團(tuán)對資源的需求。用數(shù)學(xué)模型來描述,就是n個活動集合{a1,a2,…,an}要排斥使用公共資源,如果以活動開始時間和結(jié)束時間作為占用資源的標(biāo)志量,那么就是要求解這個集合中最大子集可以最大化使用這個資源。活動選擇問題就是要選擇出一個由互相兼容的問題組成的最大子集合。
針對活動選擇問題,按照動態(tài)規(guī)劃解決的第一步就是尋找最優(yōu)子結(jié)構(gòu)。假設(shè)Sij為活動的子集,ak為其中一個活動,sk和fk分別為活動的開始時間和結(jié)束時間,數(shù)學(xué)模型描述如下:
這個發(fā)現(xiàn)就是說,最早結(jié)束時間的活動am使兩個子問題求解變成一個子問題求解,是為貪心。
正是因為活動選擇問題中有最早結(jié)束時間的活動使問題可以通過貪心策略來求解。具有貪心性質(zhì)的問題,在子結(jié)構(gòu)求解上,使求解子問題的數(shù)量減少一半,并能采用自頂向下來解決子問題,而不需如動態(tài)規(guī)劃那般自底向上。
看下活動選擇問題在定義最優(yōu)子結(jié)構(gòu)和遞歸解的動態(tài)規(guī)劃法之后如何通過貪心、自頂向下算法求解。
輸入數(shù)組的開始時間s和結(jié)束時間f,以及下標(biāo)i和n,函數(shù)如下:
Fun_recursive_activity_selector(s,f,i,n){//初始i=0
??? m=i+1;
??? while m=<n and sm<fi
??????? do m=m+1;
??? if m=<n
???????? then return { am }UFun_recursive_activity_selector(s,f,m,n)
??? else return 空集
}
自底向上和自頂向下要說區(qū)別,在遞歸上很明顯就體現(xiàn)為,自底向上是先遞歸而后求解,而自頂向下則是先求解而后遞歸。
當(dāng)然貪心算法這個遞歸也是可以轉(zhuǎn)化為迭代來運(yùn)算,不需要通過遞歸,其算法時間性能是線性級。
貪心算法通過一系列的選擇來給出某一問題的最優(yōu)解,雖然這種啟發(fā)式的策略并不是總能找到最優(yōu)解,但卻是解決問題的一個思路。和動態(tài)規(guī)劃通過組合子問題的最優(yōu)解來獲取問題的最優(yōu)解不一樣,貪心算法是通過選擇問題的最優(yōu)解來選擇子問題再進(jìn)行最優(yōu)求解。貪心算法的一般過程是:
第一:定義問題的最優(yōu)子結(jié)構(gòu);
第二:設(shè)計一個遞歸解;
第三:證明在遞歸的任何一個階段,有貪心的最優(yōu)選擇;
第四:證明通過貪心選擇后,所有子問題都為空,除一個外,這個就是貪心選擇后具有最優(yōu)解的子問題;
第五:設(shè)計一個實現(xiàn)貪心策略的遞歸算法;
第六:將遞歸算法轉(zhuǎn)化為迭代算法。
需要在說明的是貪心算法必須具有貪心選擇的性質(zhì),一個全局最優(yōu)解可以通過局部最優(yōu)(貪心)選擇來達(dá)到。在動態(tài)規(guī)劃中,每一步都要依賴子問題的解做選擇,所以用自底向上,從小子問題處理至大子問題。而貪心算法,先選擇看似最佳的子問題,再對子問題進(jìn)行求解,所以采用自頂向下,一個個向下做貪心選擇。或者說,動態(tài)規(guī)劃是先求解而后選擇,而貪心算法是先選擇而后求解,這樣大量縮小子問題數(shù)量,當(dāng)然不見得所做選擇就是最佳。當(dāng)然貪心算法和動態(tài)規(guī)劃一樣,要解決的問題都必須是具有最優(yōu)子結(jié)構(gòu)的特點(diǎn)。對一個問題來說,如果它的一個最優(yōu)解包含了其子問題的最優(yōu)解,則稱該問題具有最優(yōu)子結(jié)構(gòu)。
算法導(dǎo)論中用1-0背包問題和部分背包問題來說明動態(tài)規(guī)劃和貪心算法對問題的適用性。1-0背包問題是1和0的選擇,就是要不帶走物品,要不留下,只能選擇其一,而不能帶走物品的一部分,而部分背包問題是可以把帶走物品的一部分,而不必做出1-0的二分選擇。兩種背包問題都具有子結(jié)構(gòu)性質(zhì),但只有部分背包問題可以用貪心算法來解決。因為部分背后問題中,對每件物品進(jìn)行價值計算,可以選擇最高價值的先拿,在選擇次高價值的物品帶走。
在對貪心算法進(jìn)行基礎(chǔ)的認(rèn)識后,下面看赫夫曼編碼,赫夫曼編碼具有根據(jù)字符出現(xiàn)頻度較小的貪心選擇性質(zhì),也具有二叉樹的最優(yōu)子結(jié)構(gòu)性質(zhì),是貪心算法的典型案例。
赫夫曼編碼是一種非常有效的數(shù)據(jù)壓縮技術(shù)。赫夫曼貪心算法維護(hù)一張字符出現(xiàn)頻度表,據(jù)此構(gòu)造將每個字符表示成二進(jìn)制串的最優(yōu)方式。涉及兩個概念,一個是無前綴編碼(prefix-free code),即編碼是獨(dú)立的,互相不構(gòu)成前綴;另一個是可變長編碼,即對頻度高的字符賦以短編碼,而對頻度對的字符則賦以較長編碼,比固定長度編碼能壓縮空間。
赫夫曼編碼采用二叉樹表示,葉子為字符,每個字符編碼為從根到葉子節(jié)點(diǎn)的路徑,0轉(zhuǎn)左,1轉(zhuǎn)右。最優(yōu)編碼是一棵滿二叉樹,樹種每個非葉節(jié)點(diǎn)都有兩個子節(jié)點(diǎn)。
假定文件采用無前綴編碼的二叉樹T,很容易計算出編碼文件所需的位數(shù)。字母表C中每一個字符為c,設(shè)f(c)表示c在文件中出現(xiàn)的頻度,d(c)表示c的葉子在樹種的深度(也即字符c的編碼長度),可得編碼一個文件所需的位數(shù)就是:
基于上述的說法,通過貪心算法構(gòu)造赫夫曼編碼二叉樹的過程,主要是對最優(yōu)子結(jié)構(gòu)(字符頻度越小,在樹中的深度越高,編碼長度越長)進(jìn)行貪心選擇,貪心選擇就是識別并合并兩個頻度最低的對象,兩個對象合并的結(jié)果是一個新對象,其頻度為被合并兩個對象的頻度之和,用于和其他對象在進(jìn)行頻度比較。
具體算法就是不斷識別最低頻度并合并,導(dǎo)論中還證明了最優(yōu)無前綴編碼的問題具有貪心選擇和最優(yōu)子結(jié)構(gòu)性質(zhì)。證明邏輯,在不失一般性下,構(gòu)造另一棵樹來等同最優(yōu)。
總結(jié)下,通過赫夫曼編碼來進(jìn)行數(shù)據(jù)二進(jìn)制串表示是一種最優(yōu)方式,赫夫曼編碼滿足貪心算法的最優(yōu)子結(jié)構(gòu)和貪心選擇兩個性質(zhì),構(gòu)造出滿二叉樹來表示字符編碼。赫夫曼編碼是一個過程,是根據(jù)字符出現(xiàn)頻度構(gòu)造滿二叉樹的過程,這個構(gòu)造過程符合貪心算法的兩個性質(zhì)。
滿足最優(yōu)子結(jié)構(gòu)和貪心選擇兩個性質(zhì)的問題最優(yōu)求解,可以通過貪心算法來解決,但貪心算法如何才能做出最優(yōu)解從而使貪心選擇是正確的,導(dǎo)論中給出了擬陣作為其理論基礎(chǔ)。
擬陣的定義,及矩陣、圖的擬陣,實際是集合的基礎(chǔ)理論,尋找最大獨(dú)立子集。擬陣具有貪心選擇性質(zhì),具有最優(yōu)子結(jié)構(gòu)性質(zhì)。對于擬陣需要專題學(xué)習(xí)研究,尤其是圖的擬陣。這里先認(rèn)識到貪心算法的理論基礎(chǔ)是擬陣。導(dǎo)論中還給出任務(wù)調(diào)度問題采用貪心算法來解決的思路。總結(jié)
以上是生活随笔為你收集整理的算法导论之贪心算法(Huffman编码和拟阵)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在线实时大数据平台Storm开发之wor
- 下一篇: 在线实时大数据平台Storm版本兼容的问