本篇將介紹 哈夫曼壓縮演算法(Huffman compression)
眾所周知,計算機存儲數據時,實際上存儲的是一堆0和1(二進制)。
如果我們存儲一段字元:ABRACADABRA!
那麼計算機會把它們逐一翻譯成二進制,如A:01000001;B: 01000010; !: 00001010.
每個字元佔8個bits, 這一整段字元則至少佔12*8=96 bits。
但如果我們用一些特殊的值來代表這些字元,如:
圖中,0代表A; 1111代表B;等等。此時,存儲這段字元只需30bits,比96bits小多了,達到了壓縮的目的。
我們需要這么一個表格來把原數據翻譯成特別的、占空間較少的數據。同時,我們也可以用這個表格,把特別的數據還原成原數據。
首先,為了避免翻譯歧義,這個表格需滿足一個條件: 任何一個字元用的值都不能是其它字元的前綴 。
我們舉個反例:A: 0; B: 01;這里,A的值是B的值的前綴。如果壓縮後的數據為01xxxxxx,x為0或者1,那麼這個數據應該翻譯成A1xxxxxx, 還是Bxxxxxxx?這樣就會造成歧義。
然後,不同的表格會有不同的壓縮效果,如:
這個表格的壓縮效果更好。
那麼我們如何找到 最好的表格 呢?這個我們稍後再講。
為了方便閱讀,這個表格是可以寫成一棵樹的:
這棵樹的節點左邊是0,右邊是1。任何含有字元的節點都沒有非空子節點。(即上文提及的前綴問題。)
這棵樹是在壓縮的過程中建成的,這個表格是在樹形成後建成的。用這個表格,我們可以很簡單地把一段字元變成壓縮後的數據,如:
原數據:ABRACADABRA!
表格如上圖。
令壓縮後的數據為S;
第一個字元是A,根據表格,A:11,故S=11;
第二個字元是B,根據表格,B:00,故S=1100;
第三個字元是R,根據表格,R:011,故S=1100011;
如此類推,讀完所有字元為止。
壓縮搞定了,那解壓呢?很簡單,跟著這棵樹讀就行了:
壓縮後的數據S=11000111101011100110001111101
記住,讀到1時,往右走,讀到0時,往左走。
令解壓後的字元串為D;
從根節點出發,第一個數是1,往右走:
第二個數是1,往右走:
讀到有字元的節點,返回此字元,加到字元串D里。D:A;
返回根節點,繼續讀。
第三個數是0,往左走:
第四個數是0,往左走:
讀到有字元的節點,返回此字元,加到字元串D里。D:AB;
返回根節點,繼續讀。
第五個數是0,往左走:
第六個數是1,往右走:
第七個數是1,往右走:
讀到有字元的節點,返回此字元,加到字元串D里。D:ABR;
返回根節點,繼續讀。
如此類推,直到讀完所有壓縮後的數據S為止。
壓縮與解壓都搞定了之後 我們需要先把原數據讀一遍,並把每個字元出現的次數記錄下來。如:
ABRACADABRA!中,A出現了5次;B出現了2次;C出現了1次;D出現了1次;R出現了2次;!出現了1次。
理論上,出現頻率越高的字元,我們給它一個佔用空間越小的值,這樣,我們就可以有最佳的壓縮率
由於哈夫曼壓縮演算法這塊涉及內容較多 ,文章篇幅很長;全文全方面講解了Compose布局的各方面知識。更多Android前言技術進階,我自薦一套《 完整的Android的資料,以及一些視頻課講解 》 現在私信發送「進階」或者「筆記」即可免費獲取
最後我想說:
對於程序員來說,要學習的知識內容、技術有太多太多,要想不被環境淘汰就只有不斷提升自己,從來都是我們去適應環境,而不是環境來適應我們
技術是無止境的,你需要對自己提交的每一行代碼、使用的每一個工具負責,不斷挖掘其底層原理,才能使自己的技術升華到更高的層面
Android 架構師之路還很漫長,與君共勉
Ⅱ 哈夫曼編碼怎麼算
哈夫曼編碼的演算法就是把兩個最小的概率相加。
哈夫曼編碼,又稱霍夫曼編碼,是一種編碼方式,哈夫曼編碼是可變字長編碼的一種。
Huffman於1952年提出一種編碼方法,該方法完全依據字元出現概率來構造異字頭的平均長度最短的碼字,有時稱之為最佳編碼,一般就叫做Huffman編碼。
演算法:先按出現的概率大小排隊,把兩個最小的概率相加,作為新的概率和剩餘的概率重新排隊,再把最小的兩個概率相加,再重新排隊,直到最後變成1。每次相加時都將0和1賦與相加的兩個概率,讀出時由該符號開始一直走到最後的1,將路線上所遇到的0和1按最低位到最高位的順序排好,就是該符號的赫夫曼編碼。
動態哈夫曼編碼
Faller等人提出了動態哈夫曼編碼方法,它對數據編碼的依據是動態變化的哈夫曼樹,也就是說,對第t+1個字元編碼是根據原始數據中前t個字元得到的哈夫曼樹來進行的。
壓縮和解壓子程序具有相同的初始化樹,每處理完一個字元,壓縮和解壓方使用相同的演算法修改哈夫曼樹,因而該方法不需要為解壓而保存樹的有關信息。壓縮和解壓一個字元所需的時間與該字元的編碼長度成正比,因而該過程可以實時進行。
第一步我們把前t個字元的哈夫曼樹轉換成它的另一種形式,在該樹中只需在第二步中簡單地把由根到葉結點alol路徑上的所有結點重量加1,就可以變成前t+1個字元的哈夫曼樹。
以上內容參考:網路—哈夫曼編碼
Ⅲ 壓縮演算法原理
哈夫曼
哈夫曼編碼是無損壓縮當中最好的方法。它使用預先二進制描述來替換每個符號,長度由特殊符號出現的頻率決定。常見的符號需要很少的位來表示,而不常見的符號需要很多為來表示。
哈夫曼演算法在改變任何符號二進制編碼引起少量密集表現方面是最佳的。然而,它並不處理符號的順序和重復或序號的序列。
2.1 原理
我不打算探究哈夫曼編碼的所有實際的細節,但基本的原理是為每個符號找到新的二進製表示,從而通常符號使用很少的位,不常見的符號使用較多的位。
簡短的說,這個問題的解決方案是為了查找每個符號的通用程度,我們建立一個未壓縮數據的柱狀圖;通過遞歸拆分這個柱狀圖為兩部分來創建一個二叉樹,每個遞歸的一半應該和另一半具有同樣的權(權是 ∑ N K =1 符號數 k , N 是分之中符號的數量,符號數 k 是符號 k出現的次數 )
這棵樹有兩個目的:
1. 編碼器使用這棵樹來找到每個符號最優的表示方法
2. 解碼器使用這棵樹唯一的標識在壓縮流中每個編碼的開始和結束,其通過在讀壓縮數據位的時候自頂向底的遍歷樹,選擇基於數據流中的每個獨立位的分支,一旦一個到達葉子節點,解碼器知道一個完整的編碼已經讀出來了。
壓縮後的數據流是 24 位(三個位元組),原來是 80 位( 10 個位元組)。當然,我應該存儲哈夫曼樹,這樣解碼器就能夠解碼出對應的壓縮流了,這就使得該例子中的真正數據流比輸入的流數據量大。這是相對較短的數據上的副作用。對於大數據量來說,上面的哈夫曼樹就不佔太多比例了。
解碼的時候,從上到下遍歷樹,為壓縮的流選擇從左 / 右分支,每次碰到一個葉子節點的時候,就可以將對應的位元組寫到解壓輸出流中,然後再從根開始遍歷。
2.2 實現
哈夫曼編碼器可以在基本壓縮庫中找到,其是非常直接的實現。
這個實現的基本缺陷是:
1. 慢位流實現
2. 相當慢的解碼(比編碼慢)
3. 最大的樹深度是 32 (編碼器在任何超過 32 位大小的時候退出)。如果我不是搞錯的話,這是不可能的,除非輸出的數據大於 2 32位元組。
另一方面,這個實現有幾個優點:
1. 哈夫曼樹以一個緊密的形式每個符號要求 12 位(對於 8 位的符號)的方式存儲,這意味著最大的頭為 384 。
2. 編碼相當容易理解
哈夫曼編碼在數據有噪音的情況(不是有規律的,例如 RLE )下非常好,這中情況下大多數基於字典方式的編碼器都有問題。
Ⅳ 無損壓縮演算法是什麼樣的
WinRAR是採用它自己的獨創的壓縮演算法。
【希望你能看看最優二叉樹(哈夫曼樹),理解哈夫曼編碼的原理,對你的這個壓縮演算法會有很明晰的指導和解惑作用】WinRAR是採用它自己的獨創的壓縮演算法。
壓縮處理都是以二進制的方式進行的。這和你的編碼有關。只要是處理後的結果比原文檔文件小,而且是可逆的還原,就是無壓縮。
壓縮率的大小和你的編碼方式有關。
無損壓縮是指重構壓縮數據(還原,解壓縮),而重構數據與原來數據完全相同。該方法用於那些要求重構信號與原始信號完全一致的場合,如文本數據、程序和特殊應用場合的圖像數據(如指紋圖像、醫學圖像等)的壓縮。這類演算法壓縮率較低,一般為1/2~1/5。典型的無損壓縮演算法有:Shanno-Fano編碼、Huffman(哈夫曼)編碼、算術編碼、遊程編碼、LZW編碼等。
基於哈夫曼編碼原理的壓縮演算法:
哈夫曼演算法的過程為:統計原始數據中各字元出現的頻率;所有字元按頻率降序排列;
比如有一個字元串:aaaaaaaaaabbbbbbcccd
原文件大小存儲需要20個位元組。如果按頻率出現的次數高低,給予字元串中的每個字元不同的編碼長度,就可以達到壓縮的目的。
如
a編碼為01(佔用2個bit)
b編碼為00(佔用2個bit)
c編碼為000,(佔用3個bit)
c編碼為001,(佔用3個bit)
那就壓縮後的總長為(2*10+2*6+3*3+1*3)/8 =5.5個位元組。
另外在解碼的時候,要告之對方你的編碼方式,需要把編碼的規則傳遞過去。
如果對於以上字元串,你也可以按aaaaaaaaaa編碼成一個1,bbbbbb為2,ccc為3,d為4。這樣壓縮後的內容為最小,但是要注意一點,這時你的編碼規則為最大,你要把你的編碼規則發給對方的時候,有可能編編解碼規則文件可能會比壓縮後的內容還要大。最終結果為造成壓縮後的文件比原文件還要大。