一、實驗原理
1. huffman編碼演算法
(1)將檔案以ascii字元流的形式讀入,統計每個符號的發生頻率;
(2)將所有檔案中出現過的字元按照頻率從小到大的順序排列;
(3)每一次選出最小的兩個值,作為二叉樹的兩個葉子節點,將和作為它們的根節點,這兩個葉子節點不再參與比較,新的根節點參與比較;
(4)重複3,直到最後得到和為1的根節點;
(5)將形成的二叉樹的左節點標0,右節點標1,把從最上面的根節點到最下面的葉子節點途中遇到的0, 1序列串起來,得到了各個字元的編碼表示。
2. huffman編碼的資料結構設計
在程式實現中使用一種叫做二叉樹的資料結構實現huffman編碼。
(1)霍夫曼節點結構
typedef
struct huffman_node_tag //節點資料型別
; unsigned
char symbol; //如果是葉節點,那麼它表示某個信源符號,這裡用乙個位元組的8位二進位制數表示
};
} huffman_node;
(2)霍夫曼碼結構
typedef
struct huffman_code_tag //碼字資料型別
huffman_code;
3霍夫曼編碼流程
該程式包括兩個工程,「huff_run」為主工程,其中包括了「huffcode.c」檔案
「huff_code」為庫工程,其中包括了「huffman.c」檔案
下面按照實驗流程來進行分析
3.1 讀入待編碼的原始檔
如下**屬於」huffcode.c」檔案
main(int argc, char** argv)
} //如果輸入檔案給定,那麼讀取
if(file_in)
} //如果輸出檔案給定,那麼建立
if(file_out)
} //by yzhang for huffman statistics
if(file_out_table)
} //end by yzhang
if(memory) //對記憶體資料進行編碼或解碼操作
//對檔案進行編碼或解碼操作
if(compress) //change by yzhang
huffman_encode_file(in, out,outtable);//step1:changed by yzhang from huffman_encode_file(in, out) to huffman_encode_file(in, out,outtable)
else
huffman_decode_file(in, out);
if(in)
fclose(in);
if(out)
fclose(out);
if(outtable)
fclose(outtable);
return
0;
}
3.2 huffman.c第一次掃瞄,統計信源字元發生頻率
因為是8位元,共256個信源符號,所以建立乙個256個元素的指標陣列,用以儲存256個信源符號的頻率。其下標對應相應字元的ascii碼。需要注意的是,原始檔中不一定所有的信源符號都會出現,因此陣列中存在空元素,而非空元素為待編碼檔案中實際出現的信源符號。
#define max_symbols 256
//typedef
huffman_node* symbolfrequencies[max_symbols]; //信源符號陣列
//typedef
huffman_code* symbolencoder[max_symbols]; //編碼後的符號陣列
/* 第一次掃瞄,統計信源字元發生頻率 */
static
unsigned
int
get_symbol_frequencies(symbolfrequencies *psf, file *in)
return total_count;
}
(1) 將頻率從小到大排序並建立huffman樹
(2) 遞迴遍歷huffman樹,對存在的每個字元計算碼字
如下**屬於」huffman.c」檔案
static huffman_node*
read_code_table(file* in, unsigned int *pdatabytes)
count = ntohl(count);
//讀取此編碼表示的資料位元組數
if(fread(pdatabytes, sizeof(*pdatabytes), 1, in) != 1)
*pdatabytes = ntohl(*pdatabytes);
while(count-- > 0) //讀取碼表,由符號、碼長、碼字三部分組成
symbol = (unsigned char)c;
if((c = fgetc(in)) == eof)
numbits = (unsigned char)c;
numbytes = (unsigned char)numbytes_from_numbits(numbits);//計算儲存乙個碼長所需要的位元組數
bytes = (unsigned char*)malloc(numbytes); // 為讀取碼字分配相應的空間
if(fread(bytes, 1, numbytes, in) != numbytes) // 讀取碼字
//根據碼表,進行huffman樹的重建,由根節點至葉節點
for(curbit = 0; curbit < numbits; ++curbit)
p = p->one;
} else
//若當前讀取位為'0'
p = p->zero;
} }
free(bytes);
} return root; // 返回huffman樹的根結點,才可以進行之後的遍歷
}
4.2 讀取huffman碼字,並根據huffman樹進行解碼輸出
如下**屬於」huffman.c」檔案
「` int
huffman_decode_file(file *in, file *out)
} } free_huffman_tree(root); // 所有碼字均已解碼輸出,檔案解碼完畢
return 0;}
二、實驗結果
選擇9種不同格式型別的檔案,使用huffman編碼器進行壓縮得到輸出的壓縮位元流檔案,並對各種不同格式的檔案進行壓縮效率的分析
分析結果如下:
1. **形式表示的實驗結果
2. 各樣本檔案的概率分布圖
3. 實驗結果的分析
可以從**和概率分布圖看出,huffman編碼的平均碼長小於且很接近信源熵,信源熵一定小於8bit/sym
當檔案分布概率越不均勻,編碼效率越高(如yuv檔案);當檔案分布概率比較均勻時,壓縮效果並不理想
霍夫曼編碼
一 八卦 在 演算法為什麼這麼難?這篇部落格裡,劉未鵬講了乙個八卦 根據wikipedia的介紹,霍夫曼同學 當年還在讀ph.d,所以的確是 同學 而這個問題是坑爹的導師robert m.fano 給他們作為大作業的 fano自己和shannon合作給出了乙個suboptimal的編碼方案,為得不到...
霍夫曼編碼
給定乙個文字中出現的一組字元c,每個字元有其出現頻率freq,想構造字元的最優二進位制表示,即用來編碼整個文字的二進位制位最少 定長編碼 每個字元用相同長度的二進位制位數進行編碼,則每個字元的長度n必須滿足,2 n c 變長編碼 思想是賦予高頻字元短碼字,賦予低頻字元長碼字 編碼過程相對簡單,將表示...
霍夫曼編碼
霍夫曼編碼,或者也可以說哈夫曼編碼。它是一種編碼方式,是可變長編碼 vlc 的一種。準確來說,它是一種方法,什麼方法呢?這種方法,它完全依據字元出現的概率來構造異字頭的平均長度最短的碼字。哈夫曼編碼使用變成編碼表對源字元進行編碼,而這個變長編碼表是通過估算源字元出現的概率得到的。它有個特點,就是出現...