信息论与编码_哈夫曼编码
哈夫曼樹
哈夫曼樹(Huffman Tree)也是一種特殊的二叉樹,這種樹的所有葉子結(jié)點(diǎn)都帶有權(quán)值,從中構(gòu)造出帶權(quán)路徑長度最短的二叉樹,即哈夫曼樹。哈夫曼樹的定義
? 設(shè)二叉樹具有n個(gè)帶權(quán)值的葉子結(jié)點(diǎn),那么從根結(jié)點(diǎn)到各個(gè)葉子結(jié)點(diǎn)的路徑長度與相應(yīng)結(jié)點(diǎn)權(quán)值的乘積的和,叫做二叉樹的帶權(quán)路徑長度,記作:
其中,為第i個(gè)葉子結(jié)點(diǎn)的權(quán)值,l為第i個(gè)葉子結(jié)點(diǎn)的路徑長度。如圖6.19所示的二叉樹, 它的帶權(quán)路徑長度值WPL=1×3+3×3+2×2+4×1=20
如果給定一組具有確定權(quán)值的葉子結(jié)點(diǎn)· 可以構(gòu)造出不同的帶權(quán)二叉樹, 它們的帶權(quán)路徑長度并不相同· 我們把其中具有最小帶權(quán)路徑長度的二叉樹稱為哈夫曼樹·.
哈夫曼樹的構(gòu)造
哈夫曼編碼
? 哈夫曼編碼具有廣泛的應(yīng)用, 利用哈夫曼樹構(gòu)造的用于通信的二進(jìn)制編碼稱為哈夫曼編碼。例如: 有一段電文“ CAST囗TAT囗A囗SA "( 其中,“ 囗” 表示一個(gè)空格) 。統(tǒng)計(jì)電文中字母的頻度 f('C')=1,f('S')=2,f('T')=3,f('囗')=3,f('A')=4 。
用頻度{ 1 , 2 , 3 , 3 , 4 } 為權(quán)值生成哈夫曼樹. 并在每個(gè)葉子上注明對應(yīng)的字符。樹中從根到每個(gè)葉子都有一條路徑, 對路徑上的各分枝約定指向左子樹根的分枝表示“ 0 ” 碼, 指向右子樹的分枝表示“ 1 ” 碼, 取每條路徑上的“ 0 ” 或“ 1 ” 的序列作為和各個(gè)葉子對應(yīng)的字符的編碼, 這就是哈夫曼編碼。對應(yīng)圖6- 22 的哈夫曼樹,上述字符編碼為:
編碼過程:
信源熵
在信息論中,熵(英語:entropy)是接收的每條消息中包含的信息的平均量,又被稱為信息熵、信源熵、平均自信息量。這里,“消息”代表來自分布或數(shù)據(jù)流中的事件、樣本或特征。(熵最好理解為不確定性的量度而不是確定性的量度,因?yàn)樵诫S機(jī)的信源的熵越大。)來自信源的另一個(gè)特征是樣本的概率分布。這里的想法是,比較不可能發(fā)生的事情,當(dāng)它發(fā)生了,會提供更多的信息。由于一些其他的原因,把信息(熵)定義為概率分布的對數(shù)的相反數(shù)是有道理的。事件的概率分布和每個(gè)事件的信息量構(gòu)成了一個(gè)隨機(jī)變量,這個(gè)隨機(jī)變量的均值(即期望)就是這個(gè)分布產(chǎn)生的信息量的平均值(即熵)。熵的單位通常為比特,但也用Sh、nat、Hart計(jì)量,取決于定義用到對數(shù)的底。
采用概率分布的對數(shù)作為信息的量度的原因是其可加性。例如,投擲一次硬幣提供了1 Sh的信息,而擲m次就為m位。更一般地,你需要用log2(n)位來表示一個(gè)可以取n個(gè)值的變量。
在1948年,克勞德·艾爾伍德·香農(nóng)將熱力學(xué)的熵,引入到信息論,因此它又被稱為香農(nóng)熵。
熵的計(jì)算
當(dāng)取自有限的樣本時(shí),熵的公式可以表示為:
在這里b是對數(shù)所使用的底,通常是2,自然常數(shù)e,或是10。當(dāng)b = 2,熵的單位是bit;當(dāng)b = e,熵的單位是nat;而當(dāng)b = 10,熵的單位是Hart。
程序?qū)崿F(xiàn):
文件結(jié)構(gòu)
. ├── code │ ├── huffmanClass.h │ └── huffmanCode.cpp //主程序 ├── data │ ├── 1.txt //待壓縮文本文件 │ ├── pic.bmp //待壓縮圖像文件 │ ├── 1.txt.data //壓縮后的文本文件 │ ├── pic.bmp.data //壓縮后的圖像文件 │ ├── 1.txt.txt //解碼后恢復(fù)的文本文件 │ └── pic.bmp.bmp //解碼后恢復(fù)的圖像文件 │ ├── huffmanEncode //vs2019工程文件夾 ├── huffmanEncode.sln //點(diǎn)擊打開工程 └── 哈夫曼編碼.md核心代碼介紹
字頻統(tǒng)計(jì)code
對每個(gè)出現(xiàn)的字節(jié)進(jìn)行統(tǒng)計(jì)void Huffman::count() {ifstream readfile;readfile.open(fileAddress, ios::in | ios::binary);unsigned char *now = new unsigned char; //存儲當(dāng)前讀取到的字符CountVector* temp = new CountVector; for (int i = 0; i < 256; i++) {temp->value = i;temp->frequency=0;charCountFrequency.push_back(*temp);}while (!readfile.eof()){readfile.read((char*)now, sizeof(unsigned char));charCountFrequency[*now].frequency++;//只需要在對應(yīng)的位置上將字頻增加NumOfChar++;}charCountFrequency[*now].frequency--;readfile.close();}構(gòu)建哈夫曼樹code
根據(jù)字頻表,每次選取頻率最小的兩個(gè)組成一組,然后權(quán)值相加放入字頻表,再選字頻最小的兩個(gè)。void Huffman::CreateHuffmanTree(vector<CountVector> charFrequency) {vector<CountVector> buildtree;//HuffmanNode newNode;HuffmanNode* rootnode = new HuffmanNode;buildtree = charFrequency;sort(buildtree.begin(), buildtree.end(), mysortfunction);vector<CountVector>::iterator last = buildtree.begin();for (int i = 0; i < buildtree.size(); i++) {if (buildtree[i].frequency != 0) {if (last != buildtree.begin()) {buildtree.erase(buildtree.begin(), last);}break;}last++;}int treedepth = 0;while (buildtree.size() > 1){HuffmanNode* nodeLeft = new HuffmanNode,* nodeRight = new HuffmanNode,* newNode = new HuffmanNode;CountVector insertnew;if (buildtree[0].nodeAddress != NULL){ //如果是葉子節(jié)點(diǎn)的話 左右子樹的地址都為NULLnodeLeft->Lchild = buildtree[0].nodeAddress->Lchild;nodeLeft->Rchild = buildtree[0].nodeAddress->Rchild;}else{nodeLeft->Lchild = NULL;nodeLeft->Rchild = NULL;}if (buildtree[1].nodeAddress != NULL){nodeRight->Lchild = buildtree[1].nodeAddress->Lchild;nodeRight->Rchild = buildtree[1].nodeAddress->Rchild;}else{nodeRight->Lchild = NULL;nodeRight->Rchild = NULL;}nodeLeft->frequency = buildtree[0].frequency;nodeLeft->value = buildtree[0].value;nodeRight->frequency = buildtree[1].frequency;nodeRight->value = buildtree[1].value;newNode->frequency = nodeRight->frequency + nodeLeft->frequency;newNode->Lchild = nodeLeft;newNode->Rchild = nodeRight;insertnew.frequency = newNode->frequency;insertnew.value = 0;insertnew.nodeAddress = newNode;buildtree.erase(buildtree.begin());buildtree.erase(buildtree.begin());buildtree.insert(buildtree.begin(), insertnew);sort(buildtree.begin(), buildtree.end(), mysortfunction); //每次更新完要排序rootnode = newNode;treedepth++;}root = rootnode; }生成哈夫曼碼表
遞歸的方法找到每個(gè)葉子的路徑,走左邊為0,走右邊為1.void Huffman::GetHuffmanCode(HuffmanNode* root, int depth) {static char code[512];//判斷左兒子if (root->Lchild != NULL){code[depth] = '0';code[depth + 1] = '0';GetHuffmanCode(root->Lchild, depth + 1);}if (root->Rchild != NULL){code[depth] = '1';code[depth + 1] = '0';GetHuffmanCode(root->Rchild, depth + 1);}else{int codelength = 0;for (int j = 0; code[j] != '0'; j++){codelength++;}HuffmanCodeTable[root->value].codelen = codelength;HuffmanCodeTable[root->value].code = (string)code;//直接把編碼放到對應(yīng)value位置上}}總結(jié)
在實(shí)現(xiàn)字頻查找和編碼表的生成,都采用直接建表,在對應(yīng)value位置上賦值,提高了壓縮速度。[3]中生成字頻代碼如下,需要對每個(gè)讀取的字符與已有的字頻表進(jìn)行匹配,時(shí)間復(fù)雜度很高。優(yōu)化以后,效率提高了近十倍。void Huffman::count() {ifstream readfile;readfile.open(fileAddress, ios::in | ios::binary);unsigned char *now = new unsigned char; //存儲當(dāng)前讀取到的字符while (!readfile.eof()){CountVector *presentChar = new CountVector; //讀取到的字符信息readfile.read((char*)now, sizeof(unsigned char));int flag = 0; //標(biāo)志是否是新出現(xiàn)的字符for (int i = 0; i < charCountFrequency.size(); i++){if (*now == charCountFrequency[i].value){charCountFrequency[i].frequency++;NumOfChar++;flag = 1;}}if (flag == 0){presentChar->value = *now;presentChar->frequency++;NumOfChar++;charCountFrequency.push_back(*presentChar);}}readfile.close(); }
結(jié)果顯示:
- 對一個(gè)文本文件進(jìn)行編碼壓縮和解碼,如下:
運(yùn)行結(jié)果:
生成文件:(1.txt.data是編碼后的輸出文件,1.txt.txt是解碼文件)
- 輸入一張bmp圖片。
編碼壓縮后:
大小是原來的48%。- 選取了三張圖片,對比一下理想信源熵和平均碼長
可以看到哈夫曼編碼很接近香農(nóng)熵。
github:https://github.com/zldz/huffmanEncode
參考:
[1]數(shù)據(jù)結(jié)構(gòu)與算法教程-李春葆-125頁[2]https://github.com/FLHonker/HffmanCompress
[3]https://github.com/PiggyGaGa/Information-Theory-Source-Coding
[4]Matlab霍夫曼編碼器
總結(jié)
以上是生活随笔為你收集整理的信息论与编码_哈夫曼编码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python csv性能_性能:Pyth
- 下一篇: eclipse远程连接hadoop_Ha