博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【数据压缩】Huffman编码
阅读量:7042 次
发布时间:2019-06-28

本文共 3467 字,大约阅读时间需要 11 分钟。

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码。变长编码则是码字的长度可能存在不相等。

前缀码可表示为叶子节点为码字的编码二叉树,如图所示。

399159-20151120105620608-2120494095.png

期望编码长度

如上图所示的两种变长编码,哪一种编码压缩效率比较好?显然,若信息编码之后的长度越小,则编码的压缩效率越好。为此,我们引出刻画量度期望编码长度

首先我们定义字符空间\(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不等式。

399159-20151120113255030-1776670531.png

最优编码指期望编码长度最小的编码,求解最优编码等价于数学问题:

\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直至小顶堆中只有一个节点,此节点即为编码二叉树。

编码二叉树建立过程如图所示

399159-20151120105743640-942940423.png

此字符空间有9个字符,采用等长编码则需要\(4\) bit;Huffman编码的期望字长则为\(2.77\) bit;字符空间的熵为\(2.69\) bit;冗余度为\(2.77-2.69=0.08\) bit.

Python 3.6实现Huffman编码,代码参考了:

# -*- coding: utf-8 -*-# @Time    : 2017/1/21# @Author  : rainfrom collections import Counterfrom heapq import heapify, heappop, heappushdef 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 vocabularysentence = '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, .

转载地址:http://pixal.baihongyu.com/

你可能感兴趣的文章
SQL Server 命令行操作
查看>>
当cpu飙升时,找出php中可能有问题的代码行
查看>>
独孤九剑与黑客编程
查看>>
【windows8开发】序
查看>>
NAT方式,宿主机无法ping通虚拟机
查看>>
RabbitMQ配置
查看>>
bzoj3654 图样图森破
查看>>
四则运算一
查看>>
用Javascript获取页面元素的位置
查看>>
electron 学习笔记
查看>>
vs 开发 qt 遇到 无法找到 Visual Studio 2010 的生成工具(平台工具集 =“v100”) 解决方案...
查看>>
Oracle死锁处理实例
查看>>
[转]Android Studio创建Xposed模块项目时BridgeApi的正确添加方式
查看>>
【hive】——Hive sql语法详解
查看>>
python 全栈开发,Day50(Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏)...
查看>>
一篇网络流的好blog
查看>>
Python基础之继承与派生
查看>>
filter、map、every函数的使用
查看>>
黑马程序员——iOS学习——UITableView表视图单元样式
查看>>
Bash基础——减号-
查看>>