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