1. 程式人生 > >【資料壓縮】Huffman編碼

【資料壓縮】Huffman編碼

1. 壓縮編碼概述

資料壓縮在日常生活極為常見,平常所用到jpg、mp3均採用資料壓縮(採用Huffman編碼)以減少佔用空間。編碼\(C\)是指從字元空間\(A\)到碼字表\(X\)的對映。資料壓縮編碼指編碼後資訊的長度較於原始資訊要短。本文試圖探討Huffman編碼是如何保證唯一可譯性、如何壓縮、以及壓縮效率如何?

字首碼

字首碼的任意一碼字均不為其他碼字的字首,此保證了編碼的唯一可譯性。比如碼字表{0, 01, 11, 1}001的字首,111的字首;當遇到字元文字011100,是應分隔為01-11-0-0還是0-11-1-0-0等?若採用字首碼編碼,碼字表為{0, 10, 11},則字元文字011100可即時分隔為0-11-10-0

可譯,所以字首碼亦被稱為即時碼。同時,字首碼保證了編碼的唯一可譯性,即字元空間\(A\)到碼字表\(X\)的對映為一一對映。本文探討的Huffman編碼即為字首碼。

根據碼字長度,編碼分為等長編碼與變長編碼。等長編碼即字母表中所有碼字的長度均相等,最為常見的是字長7位的ASCII碼。變長編碼則是碼字的長度可能存在不相等。

字首碼可表示為葉子節點為碼字的編碼二叉樹,如圖所示。

期望編碼長度

如上圖所示的兩種變長編碼,哪一種編碼壓縮效率比較好?顯然,若資訊編碼之後的長度越小,則編碼的壓縮效率越好。為此,我們引出刻畫量度期望編碼長度

首先我們定義字元空間\(A = \lbrace a_1,a_2, \cdots ,a_n \rbrace\)

,即資訊文字中有n個字元,且字元\(a_i\)的長度為\(l_i\),出現頻率(即概率)為\(p_i\);則期望編碼長度為

\[ L = \sum\limits_{i = 1}^n {p_i*l_i} \]

若要期望編碼長度\(L\)越小,學過數學的都知道,則高概率的碼字字長應不長於低概率的碼字字長,即滿足
\[\forall i,j \ \ \ p_i \ge p_j \Leftrightarrow l_i \le l_j\]

最優編碼

對於二元編碼(01)的字首碼,滿足McMillan-Kraft不等式
\[\sum\limits_{i = 1}^n {{2^{ - l_i}}} \le 1\]

具體的證明參看[3]。McMillan-Kraft不等式從整體上限制編碼長度的下界。

如下圖所示的字首碼即滿足McMillan-Kraft不等式。

最優編碼指期望編碼長度最小的編碼,求解最優編碼等價於數學問題:

\begin{align}
& \min \sum\limits_{i = 1}^n {{p_i}*{l_i}} \cr
& s.t.  \sum {{2^{ - {l_i}}}} \le 1 \label{eq:kraft}
\end{align}

運用拉格朗日乘子法,構造目標函式
\begin{equation}
J = \sum {p_i*l_i + \lambda (\sum {{2^{ - l_i}}} } )
\end{equation}

\(l_i\)求偏導,
\[{{\partial J} \over {\partial l_i}} = p_i - \lambda {2^{ - l_i}}\ln 2\]

令偏導為0,得到
\[{2^{ - l_i}} = {{p_i} \over {\lambda \ln 2}}\]

將其代入McMillan-Kraft不等式\eqref{eq:kraft}中,得到\(\lambda = {1 \over {\ln 2}}\),最優編碼的碼字長度
\begin{equation}
l_i = - \log _{2}p_i
\end{equation}

最優編碼的期望碼字長度即為字元空間的熵:
\begin{equation}
\sum\limits_{i} {p_il_i = - \sum\limits_{i} {p_i \log p_i} } = H(A)
\end{equation}

由此,定義編碼的冗餘度(Redundancy of a code),表示編碼的冗餘描述:
\begin{equation}
\rho = L - H(A)
\end{equation}

可以證明,字首碼的編碼長度滿足不等式
\begin{equation}
H(A) \le L \le H(A) + 1
\end{equation}

因此,字首碼的冗餘度滿足\(0 \le \rho \le 1\)

2. Huffman編碼

Huffman編碼採用小頂堆來優化編碼二叉樹的建立過程,確保低概率的碼字字長不短於高概率的碼字,具體編碼過程如下:

  1. 將字元空間的字元以概率為關鍵值建立小頂堆;
  2. 依次取堆頂元素兩次,將該兩個字符合成一棵二叉樹,根節點的關鍵值為兩個字元的概率相加;然後將該新合成的二叉樹做為節點插入到小頂堆中;
  3. 重複步驟2直至小頂堆中只有一個節點,此節點即為編碼二叉樹。

編碼二叉樹建立過程如圖所示

此字元空間有9個字元,採用等長編碼則需要\(4\) bit;Huffman編碼的期望字長則為\(2.77\) bit;字元空間的熵為\(2.69\) bit;冗餘度為\(2.77-2.69=0.08\) bit.

Python 3.6實現Huffman編碼,程式碼參考了rosettacode

# -*- coding: utf-8 -*-
# @Time    : 2017/1/21
# @Author  : rain
from collections import Counter
from heapq import heapify, heappop, heappush


def huffman_coding(message):
    freq = Counter(message)  # counter for every character
    min_heap = [[cnt, [ch, '']] for ch, cnt in freq.items()]
    heapify(min_heap)
    while len(min_heap) > 1:
        low1 = heappop(min_heap)
        low2 = heappop(min_heap)
        for pair in low1[1:]:  # update children node
            pair[1] += '0'
        for pair in low2[1:]:  # update children node
            pair[1] += '1'
        # push [low1_cnt+low2_cnt, low1's children, low2's children]
        heappush(min_heap, [low1[0] + low2[0]] + low1[1:] + low2[1:])
    vocabulary = {pair[0]: pair[1] for pair in min_heap[0][1:]}  # text -> code
    return vocabulary

sentence = 'this is an example for huffman encoding'
print(huffman_coding(sentence))

上述實現中,並沒有直接構建二叉樹,而是用到了一個小技巧——在小頂堆中迴圈編碼。Huffman編碼存在一個缺點:解碼端要根據碼字與編碼之間的對映關係才能解碼,即解碼端與編碼端應共享一棵Huffman編碼樹。

3. 參考資料

[1] Huffman, David A. "A method for the construction of minimum-redundancy codes." Proceedings of the IRE 40.9 (1952): 1098-1101.
[2] Cover, Thomas M., and Joy A. Thomas. Elements of information theory. John Wiley & Sons, 2012.
[3] Bernd Girod, EE398A Image and Video Compression.

相關推薦

資料壓縮Huffman編碼

1. 壓縮編碼概述 資料壓縮在日常生活極為常見,平常所用到jpg、mp3均採用資料壓縮(採用Huffman編碼)以減少佔用空間。編碼\(C\)是指從字元空間\(A\)到碼字表\(X\)的對映。資料壓縮編碼指編碼後資訊的長度較於原始資訊要短。本文試圖探討Huffman編碼是如何保證唯一可譯性、如何壓縮、以及壓縮

貪心演算法Huffman編碼

問題描述 有一組字符集{c1, c2, …, cn},在使用這組字符集的過程中,通過統計發現每個字元都有其相應的出現頻率,假設對應的頻率為{f1, f2, …, fn}。現在需要對這些字元進行二進位制編碼,我們希望的編碼結果如下:每個字元都有其獨一無二的編碼;

資料壓縮LZ77演算法原理及實現

1. 引言 LZ77演算法是採用字典做資料壓縮的演算法,由以色列的兩位大神Jacob Ziv與Abraham Lempel在1977年發表的論文《A Universal Algorithm for Sequential Data Compression》中提出。 基於統計的資料壓縮編碼,比如Huffman編

資料壓縮LZ78演算法原理及實現

在提出基於滑動視窗的LZ77演算法後,兩位大神Jacob Ziv與Abraham Lempel於1978年在發表的論文 [1]中提出了LZ78演算法;與LZ77演算法不同的是LZ78演算法使用動態樹狀詞典維護歷史字串。 1. 原理 壓縮 LZ78演算法的壓縮過程非常簡單。在壓縮時維護一個動態詞典Dictio

資料結構哈夫曼樹及哈夫曼編碼

哈夫曼樹 給定n個權值作為n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。 樹節點間的邊相關的數叫做權。 從樹

資料結構哈夫曼樹的編碼與譯碼

#include <stdio.h> #include <malloc.h> #include <string.h> typedef struct { char info; int weight; int parent, lchild, rchild;

資料結構稀疏矩陣的壓縮儲存和轉置演算法(C++程式碼)

一 稀疏矩陣的定義 矩陣是如今很多科學與工程計算問題中常用的數學物件,矩陣涉及到的計算通常會出現矩陣的階數比較高但是非零元素的個數卻比較少的情況,因此,我們需要有一種方法來壓縮這種比較稀疏的矩陣。 那麼,首先第一個問題就是如何定義一個矩陣是否是稀疏的?參考嚴蔚敏的資料結構教

資料收集PCA降維

post hive ron str AD span clas htm logs 重點整理: PCA(Principal Components Analysis)即主成分分析,是圖像處理中經常用到的降維方法 1、原始數據: 假定數據是二維的 x=[2.5, 0.5, 2.2,

資料搬遷LINUX 下安裝JDK

root用戶登錄 腳本語句 下載jdk 查詢 上傳 use 方法 path環境變量 自己 今天閑著沒事,就在linux下安裝一些應用軟件,學著裝那個JDK,和配置它的環境變量。雖然網上有很多的方法,自己做下也好,順帶記錄下,以便今後好查詢: 1,下載JDK 到sun的主頁

資料搬遷虛擬機安裝CentOS

set 文件 tor new cati 系統 安裝 選項 conn 今天用虛擬機安裝CentOS5.5,還是第一次使用虛擬機,順便學習了下,做了一些記錄,留著今後做參考: 虛擬機下安裝CentOS5.5步驟1、下載VMWare,然後解壓安裝2、建立虛擬機.建立一臺虛擬機。點

資料搬遷mysql問題

wpa 密碼 linux table tables etc skip 遠程連接 和我 不知道怎麽回事,進行了一次授權語句的執行,希望任何一臺機子都能遠程連接mysql ,並且給mysql的root用戶設置了密碼,可是在重新啟動linux的時候,用root用戶的新密碼去不能進

資料搬遷windows 下更改mysql的root密碼

無法 登陸 打開 無法登陸 服務器 不用 csdn 探索 安裝 初次使用MySQL,有很多的東西都不是很清楚,還在探索之中。因為一個軟件,必須要更改MySQL中root的密碼,原本想重新裝MySQL的,但不能每次遇到都要重新裝吧。就找了些資料,如何去更改密碼,網上眾說蕓蕓,

資料搬遷安裝phpunit

既然 追加 win8系統 資料 安裝 安裝php pat installer 文件 學習php也有一段時間了,學這個的目的其實也就是為了做單元測試,既然要做單元測試,那必須的有其相應的工具吧。今天在查閱相關資料的時候了解到需要先安裝 phpunit。那咱們就先來安裝一下唄。

神經網絡編碼聚類算法--DEC (Deep Embedded Clustering)

arr ole 參加 這也 criterion dia one ims 方法 1.算法描述 最近在做AutoEncoder的一些探索,看到2016年的一篇論文,雖然不是最新的,但是思路和方法值得學習。論文原文鏈接 http://proceedings.mlr.p

神經網路編碼聚類演算法--DEC (Deep Embedded Clustering)

1.演算法描述      最近在做AutoEncoder的一些探索,看到2016年的一篇論文,雖然不是最新的,但是思路和方法值得學習。論文原文連結 http://proceedings.mlr.press/v48/xieb16.pdf,論文有感於t-SNE演算法的t-

資料結構—— 1、不要小瞧陣列

2-1、 使用Java中的陣列 2-2 二次封裝屬於我們自己的陣列 2-3 向陣列中新增元素 2-4 陣列中查詢元素和修改元素 2-5 包含,搜尋和刪除 2-6 使用泛型 2-7 動態陣列 2-8 簡單的複雜度分析 2-9 均攤複雜度和防止複雜度的震盪

資料結構多項式連結串列實現相加

#include<bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 1006; struct node { double coef; int exp; struct n

資料結構合併兩個有序連結串列

#include<stdio.h> #include<string.h> #include<stdlib.h> const int maxn = 1e5 + 5; struct node { int num; struct node *next; }; s

資料分析電商平臺數據分析

目錄 電商模式 年度重複購買率 轉化率 年均購買率 購物車大小 棄買率 客戶獲取成本 平均每位客戶營收 關鍵詞和搜尋詞 推薦接受率 病毒性 郵件列表點入率 線下線上相結合 運送時間 庫存可供率

點選某列表介面上的某按鈕時調出來另一個設計項的列表介面,選中被調出的列表介面上的值時彈出資料能輸入資料的程式碼樣例

//例如:【出庫管理】設計項的列表介面上有一個【出庫】按鈕,點擊出庫按鈕時調出【入庫管理】設計項的列表介面,選中【入庫管理】列表介面上的一條記錄時,彈出輸入框,程式碼樣例如下   function(button, e) { debugger; // 中斷除錯指令,可以手動刪除它