霍夫曼编码

一:八卦


在《算法为什么这么难?》这篇博客里,刘未鹏讲了一个八卦:

根据wikipedia的介绍,霍夫曼同学(当年还在读Ph.D,所以的确是“同学”,而这个问题是坑爹的导师Robert M. Fano给他们作为大作业的,Fano自己和Shannon合作给出了一个suboptimal的编码方案,为得不到optimal的方案而寝食难安,情急之下便死马当活马医扔给他的学生们了)当年为这个问题憔悴了一个学期,最后就快到deadline的时候“忽然”想到二叉树这个等价模型,然后在这个模型下三下五除二就搞定了一篇流芳千古的论文,超越了其导师。

霍夫曼使用自底向上的方法构建二叉树,避免了次优算法Shannon-Fano编码的最大弊端──自顶向下构建树。1952年,霍夫曼在麻省理工攻读博士时发表了《一种构建极小多余编码的方法》(A Method for the Construction of Minimum-Redundancy Codes)一文,它一般就叫霍夫曼算法。


二:分析


霍夫曼编码,就是一种根据概率构建无损编码方式。简单的说,就是把被编码的对象按照出现概率高低排序,使出现概率高的尽量占用bit少一些,这样使整体编码结果较小。wiki上有这么个例子:

在英文中,e的出現機率最高,而z的出現概率則最低。當利用霍夫曼編碼對一篇英文進行壓縮時,e極有可能用一個位元來表示,而z則可能花去25個位元(不是26)。用普通的表示方法時,每個英文字母均占用一個字節(byte),即8個位元 。二者相比,e使用了一般編碼的1/8的長度,z則使用了3倍多。倘若我們能實現對於英文中各個字母出現概率的較準確的估算,就可以大幅度提高無損壓縮的比例。

为了方便讲述,我们说编码前的是字母流,编码后的是比特流。然后某字母对应的几个比特就是它的编码,学名叫前缀码。这个编码需要满足两个条件:

1. 出现概率大的字母编码短,概率小的字母编码长。

2. 编码后的比特流可以唯一还原成字母流。



比如按照上面的表:0,101,100,111,1101,1100是a,b,c,d,e,f的前缀码,那么比特流001011101就可以毫无歧义的还原成0.0.101.1101,就是aabe。

怎么满足这两个条件呢?霍夫曼设计了一种方式:二叉树,因为二叉树的两个分叉可以代表0和1,而且树又是无环的,不会产生歧义。

用树来表示的话,霍夫曼编码的cost公式可以这么表达:cost=Σ freq(i) * depth(i),i是所有的叶子节点。

但是如果我们假设所有的父节点的频率都是两个子节点的和的话,那么,cost公式也可以表达为:除了根节点外的所有节点的频率和。

这样,假设所有字母的概率值组成一个集合Q,其中Fi,Fj分别是两个最小的概率,那么cost(Q)=Fi+Fj+cost(Q'),Q'就是Q去掉Fi和Fj并加入概率为(Fi+Fj)的新节点后组成的新集合。



上图中,红框中的节点组成的集合就是Q’。


三:代码


HUFFMAN(C)
n=|C|
Q=C
for i=1 to n-1
     allocate a new tree node z
     z.left=x=EXTRACT-MIN(Q)
     z.right=y=EXTRACT-MIN(Q)
     z.freq=x.freq+y.freq
     INSERT(Q,z)
return EXTRACT-MIN(Q)

其中的EXTRACT-MIN(Q)就是从队列Q中取最小数,可以用最小堆。

通过每次都取最小的,最终得到了一个可用的解,这就是贪心算法。

假如Q是由二叉堆实现的,那么,建堆使用的时间是O(n),每次调整堆使用的时间是O(lgn),共调整了n-1次。

总之时间复杂为O(nlgn)。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值