算术编码与哈夫曼编码

算术编码与哈夫曼编码
算术编码与哈夫曼编码

安徽大学

本科毕业论文(设计、创作)题目:哈夫曼编码与算术编码压缩效率比较

学生姓名:李伟学号:E20714134

院(系):计算机科学与技术专业:软件工程

入学时间:2007年9月

导师姓名:韩莉职称/学位:讲师/硕士

导师所在单位:安徽大学计算机科学与技术学院

完成时间:2011年5月

哈夫曼编码与算术编码压缩效率比较

摘要

算术编码和哈夫曼编码都利用信源符号的概率分布特性进行编码,使平均码长逼近信息熵是压缩编码算法的第一要求,算术编码比哈夫曼编码逼近信息熵的能力要强,但是编码效率和实现往往是一对矛盾,编码效率的提高,往往要在实现上付出代价,所以,选择压缩算要权衡这两点。本论文开篇先引入了信息论的一些概念,因为编码理论发源于信息论,是各种编码算法的数学基础。然后在第2章分析了算术编码原理,并从无限精度的算术编码原理过渡到在计算机上能够实现的二进制编码原理。在第3章紧接着介绍了哈夫曼编码原理,并讨论了怎样把信源符号映射为对应的码字,过程中用到的哈夫曼编码表是编码与解码的关键。在第4章对两者的编码效率作了比较,主要是结合信息论中的一些概念从微观上对两者逼近信息熵的能力作了比较,并在这章最后对两者在文本文件的压缩效果的表现上给出了一些实验结果。最后,在5章,主要是对前面内容做了些补充和总结。

关键词:信息熵;算术编码;哈夫曼编码;编码效率

The comparison of Huffman Coding and Arithmetic Coding in File

Compression

Abstract

Full use of the probability distribution of source symbols is the feature of the arithmetic encoding and Huffman encoding. Approaching the average code length to the information entropy come first when designing a compression algorithm. To the capacity of closing to information entropy, arithmetic encoding is stronger than Huffman encoding. However, the coding efficiency and implementation is often a contradiction: to improve coding efficiency, which means the algorithm implementation process needs to pay a higher price. Therefore, you need to weigh both when choosing a compression algorithm. In the beginning of this thesis, it first introduced some of the concepts of information theory. Because encoding algorithms are derived from information theory and information theory is the mathematical foundation of various coding algorithms. Then in Chapter 2, it introduces the principle of arithmetic coding. For better to understand the binary arithmetic coding principle, it first introduces the unlimited precision arithmetic coding. In Chapter 3, it describes the Huffman coding theory, and discusses how to map source symbol to the corresponding code word, among which Huffman coding and decoding table is the key. In Chapter 4, the coding efficiency of the two algorithms is compared. Mainly compare the capacities to approximate information entropy with some of the concepts in information theory. And the final part in this chapter, some experimental results are given to show the compression effect to compress a text file. Finally, in Chapter 5, it gives some additions and summary.

Keywords:Information Entropy; Arithmetic Coding; Huffman Coding;Coding Efficiency

目录

1 引言 (1)

1.1 数据压缩概念及技术分类 (1)

1.2 统计编码的数学准备 (2)

1.3 统计编码简介 (5)

2 算术编码 (5)

2.1 算术编码简介 (5)

2.2 无限精度的算术编码 (6)

2.3 二进制编码 (9)

2.4 二进制解码 (13)

3 哈夫曼编码 (14)

3.1 哈夫曼编码简介 (14)

3.2 哈夫曼编码原理 (14)

3.3 哈夫曼解码原理 (16)

3.4 哈夫曼编码与解码系统模型 (16)

3.5 哈夫曼编码形式不唯一 (17)

4 算术编码与哈夫曼编码的比较 (17)

4.1 两者编码效率的比较 (17)

4.2 两者压缩率的比较 (19)

5 总结 (20)

主要参考文献 (22)

致谢 (23)

1引言

1.1数据压缩概念及技术分类

数据压缩,就是将信息的一种表示方式转换为另一种表示方式,信息的新的表示方式与原有表示方式相比较所含的信息量相同或是在可以承受的范围内有

所损失,但是新的表示方式所用到的符号数量要比原有表示方式要尽可能的少。

数据压缩是必要的,不管是在生物系统中还是在人工系统中都存在数据压缩。尤其是处在这个信息爆炸的时代,怎样更有效的存储信息和传递信息对于文明进步的作用都是显而易见的。从不同视角看到的数据压缩的益处有:在通信信道上更快的传输信息,这就相应的减少了信道的占有时间,这可看作在时间上进行了压缩;在同一通信信道上并行的传输各种频率的互不干扰的信息,如xDSL 技术在普通电话线上实现把低端频谱留给传统电话使用,把高端频谱留给上网用户使用,这可看作在频率域上进行了压缩;传输信号当然需要能量消耗,因此对数据进行压缩也就间接地减少了能量消耗,这可看作是在能量上进行了压缩;各种存储系统的存储空间都不是无限的,对数据进行压缩后,就可以在同样的存储空间存储更多的数据,这可看作是空间上的压缩。任何系统,尤其在活动时,都同时涉及到时间、空间和能量上的消耗,所以数据压缩就是从这三方面同时进行了压缩,这样就使得系统更加的有效的运行。

既然数据压缩是必要的,那么就要考虑从哪些方面来说数据可以被压缩,一般可从以下几方面考察:

一是,数据中通常存在着多余成分,即允余度。例如,存储在计算机上的一份文本文件,内容可假设是一部英文小说,可以想象,26个英文字母重复出现,各个字母出现的频率有大有小,而且不仅字母重复出现,字母组成的单词也重复出现,进一步,某些字母总是在某单词的一定位置上以高概率重复出现,某些单词也总是在一个特定句式的特定位置以高概率重复出现等等,计算机是以二进制形式存储文件的,那么用二进制符号怎样有效的表示这个由英文字符、各种标点符号和控制字符组成的文本文件就是怎样对数据进行压缩的问题,而压缩显然就要利用这些允余度的不同类型,例如,我们赋予高概率字符以相对少的二进制位数,赋予低概率字符以相对多的的二进制位数,那么这样表示后所得的总的二进制位数肯定比不考虑各个字符出现概率,而给不同字符按照ASCII码分配二进制位数所得的总的二进制位数要少很多。

二是,数据中的各个部分前后之间总是存在着相关性。例如,电视上显示的动态画面,实际上是由离散的不同画面(帧)组成的,之所以看似是连续的只是因为帧的播放速度超过了人类的反应速度并且利用了人类的视觉暂留效应,但为使

画面看似连续,不仅要利用人类本身的生物特性,还要保证帧之间的过渡是相对平滑的,也就是相邻两帧之间只有动态上的细微变化,而大部分画面在两帧之间是相同的,很显然,这种相关性是可以很好的加以利用的。

三是,对于人而言,我们的感受器只能接受外界中很小一部分的信息。例如,

人眼只能感受阳光中的可见光,而对紫外光不可见,但一些昆虫如蜜蜂却能感受紫外光,如果人为的屏蔽掉紫外光部分,对人眼而言,我们并不能对屏蔽前与屏蔽后的阳光加以区分,但蜜蜂就不同了,可能就因为无法感受紫外光,就找不到花蜜了,所以,对于人而言,并不是数据中所有的信息对我们都是必要的,不必要的部分就可以屏蔽掉。

数据压缩可分为无损压缩和有损压缩,下面分别简要的介绍可逆压缩和不可

逆压缩:

可逆压缩,是利用数据的统计特性,即数据的允余度进行压缩的一类方法,

允余度的去除或是减少并不影响原始数据无失真的恢复,即是一个可逆的过程。但从编码效率上来讲,可逆压缩受到待编码数据本身的信息熵的限制,无法达到更高的编码效率。也就是由于这样的限制,仅使用可逆压缩方法是不可能解决多媒体的存储和传输的所有问题的,因此,这类方法一般适用于在压缩过程中不允许有丝毫损失的情况,如在计算机磁盘上存储文件,数据通信等领域。

常用的可逆压缩方法有:哈夫曼编码、算术编码、LZW 编码等。

不可逆压缩,是利用人类对图像或声波中的某些频率成分的不敏感特性,允

许压缩过程中损失一定的信息,虽然不能完全恢复原始数据,但是所损失的部分对人类理解理解原始数据的影响可承受,好处就是与可逆压缩相比,能够获得更高的编码效率,因此,广泛适用与语音、图像和视频数据的压缩。

不可逆压缩的方法有很多,这里不列举。

1.2 统计编码的数学准备

统计编码某种程度上与可逆压缩同义,因此可混用。在1.1节中,我们用到

了允余度和信息熵的概念,这两个概念都由信息论的开创者香农(Claude E.shannon )首次提出,因此这一小节主要介绍信息论中的一些概念,这些概念有助于我们更好的理解统计编码。

信源空间概念:

信源空间表示为:)()()()(:)(:32132

1m m

a p a p a p a p X P a a a a X

其中,),,2,1(1)(0m i a p i =≤≤ ∑==m

i m a p 11)(

(1)符号i a 代表信源可能发出的符号,)(i a p 发符号i a 的先验概率。

(2)用信源空间表示信源X 的必要前提,就是信源可能发出的各种不同符号的概率先验可知,或事先可测定,测定信源的概率空间是构建信源空间的关键。

信源符号的自信息量:

数学上定义为:)(log )(i r i a p a I -= 说明:

(1) i a 为信源所发符号,)(i a p 为信源发符号i a 的概率,概率越大,不确定性越

小,概率越小,不确定性越大。

(2) 而自信息量)(i a I 既表示收信者确切无误收到信源符号i a 后,从i a 中获得的信息量,同时也表示收到符号i a 之前,收信者对于信源所发符号i a 存在的不确

定性的度量。

(3) 自信息量)(i a I 的单位取决于对数的底,即r 值,若r=2,则自信息量的单位为比特)(bit ,不指明正整数r 具体值时,则自信息量的单位为r 进制信息单位。

信源的信息熵:

数学上定义为: r 进制形式 ∑=-=m

i i r i r a p a p X H 1

)(log )()(

2进制形式 ∑=-=m i i i a p a p X H 1

22)(log )()( 说明:

(1) 运用对数换底公式,可得:r og X H X H r 22l )()(=

(2) 信息熵作为信源X 的总体信息测度,表示信源每发一个符号所能提供的平均信息量,同时也表示收信者在接收到符号之前,对信源X 存在的平均不确定

性的度量。

最大离散熵定理:

),(),,,(21m ax 21m m p p p H p p p H ≤ 其中,m m og m

m m m H p p p H m i m 22121m ax log 1l 1)1,,1,1(),,,(=-==∑= 说明: (1) 熵函数),,,(21m p p p H 是信源X 的信息熵的另一种表示方法,其中p i 代表

)(i a p 。

(2) 熵函数具有上凸性,所以熵函数具有极大值,此极大值就是其最大值max H ,最大值在p i =1/m 时,即信源符号等概率分布时取得。

(3) 离散信源信息熵的最大值只取决于信源的符号种类数m 值,m 值越大,其信息熵的最大值越大。

信源X 所含有的允余度:

)(log )()(2m ax X H m X H X H -=-=ζ 说明:

(1) ,,允余度信源符号等概率分布时0=ζ因此离散独立信源的允余度隐含在信

源符号的非等概率分布之中。也就是说,只要信源符号不是等概率分布,就

存在能够对信源数据进行数据压缩的可能性,允余度的存在是能够进行统计

编码的前提。

对信源编码所得平均码长:

∑==+++=m

i i i m m a p n a p n a p n a p n n 12211)()()()( 说明:

(1) i n 是信源符号i a 的码字长度。

(2) 平均码长n 是各个信源符号的码字长度i n 在信源X 的概率空间

{})(,),(),(21m a p a p a p 上的统计平均值。

编码效率:

n X H )(=η 说明:

(1) η值越大,表示每一个码符号所携带的平均信息量越多,反之,就越少。

(2) 对于给定的待编码信源,由于各个信源符号的概率已经经过统计确定,所以 信源熵的值也就固定不变了,所以编码效率的高低只取决于平均码长n ,所 以提高信源编码的效率,就是要设法减小平均码长n 。

平均码长n 存在下限:

n )(X H r ≥ 说明:

(1) 在证明过程中,得到:当且仅当对所有),,2,1(m i i =,都有i n i r a p 1)(=时,

n )(X H r =才成立,其中i n 是信源符号i a 的码字长度。

(2) 单义可译码的平均码长n ,再小也不会小于信源熵。所以一旦信源确定,其信源熵也就随之确定,平均码长的下限值也随之确定,在这个范畴内编码效

率不可能超过1(100%),要想进一步提高编码效率,只有通过改变信源本

身才能做到。

1.3 统计编码简介

1.2节的内容是设计统计编码的理论基础,所以有了1.2节的介绍,统计编 码这类方法就可简单表述为:

利用信源符号的概率分布特性,寻找每个信源符号的出现概率与其码字长度

之间的最优匹配,这种最优匹配就是要做到平均码长最小,也即平均码长要最大程度的趋近信息熵值。

怎么利用信源符号的概率分布特性,来为每个信源符号分配码字,达到最大

程度趋近信息熵值的目的在具体介绍哈夫曼编码和算术编码时会清楚地了解到。

2 算术编码

2.1 算术编码简介

很明显,各种统计编码都是变字长编码方法,但变字长的含义对于哈夫曼编

码和算术编码是有些区别的,哈夫曼编码独立的为每个符号依据其概率大小分配相应长度的码字,码字长度都是整数个比特)(bit ,而算术编码无需为一个符号设定一个码字,它直接对整个输入符号序列进行编码,并输出一个码字来代表整个符号序列,编码过程也自然是递推形式的和连续的,这样在衡量平均每个符号所对应的码字长度时,会知道码字长度都是以分数形式的比特)(bit 来分配的,精确度可以很趋近于)(log i a p ,这样算术编码与哈夫曼编码相比就能够更大程度地趋近信源(整个符号序列)的信息熵。

稍具体的来讲,算术编码不是将单个的信源符号映射成一个码字,而是将整

个输入符号映射为左闭右开区间[0,1)内的一个子区间,这一子区间的宽度等于整个符号序列的概率(整个符号序列的概率又等于输入符号的概率之积),可以想象在编码过程中,每编码一个信源符号,都会相应得到新的区间,新的区间作为编码下一个信源符号所要待划分的区间,就这样一直对后续的符号依次编码,直到所有的符号被编码,即编码完毕,最后得到的区间内的任何一个小数都能代表整个符号序列,但一般选择一个容易变成二进制数字的小数作为实际的编码输出。

上述阐述过程可能有点抽象,在2.2节将更加详细的加以说明。

回顾算术编码的发展历程会发现,算术编码的发展不是一蹴而就的,其思想

的源头可追溯到香农(shannon ),再由P.Elias 正式提出其完整思想(但还不实用),后来又由R.Pasco 、J.Rissanen 、G .G .Langdon 和Written 等人将其实用化,一直到算术编码家族中的一些成员成为如H.263视频压缩标准和JPEG2000图像压缩标

准的核心,共经历了约40多年的时间,直到现在算术编码的研究改进也没有停止。

2.2 无限精度的算术编码

由P.Elias 正式提出的算术编码需要无限精度的浮点运算,随着符号序列的输入,计算精度将无限制的增加,相应的编码位数也无限制的增加,而这样的要求是在当前计算机上无法实现的。

虽然如此,但无限精度的算术编码可用来更清楚地说明算术编码的基本原理,同时通向实用化的有限精度、不做乘法的二进制算术编码也是基于此而改进得到的。

如果我们把编码过程中得到的每一区间的下限作为码字,并用C 表示,区间大小用A 表示,那么算术编码原理可表述如下:

设输入符号序列s 取自信源空间 )()()()(:)(:32132

1m m

a p a p a p a p X P a a a a X ,s

后接符号)(X a a i i ∈,那么就扩展为符号序列i sa ,空符号序列记作φ,只有一个符号i a 的序列就是i a φ,那么算术编码的两个迭代关系可表示为:

I . 码字更新:)()()()(s A a P s C sa C i i ?+=

II .区间更新:)()()(s A a p sa A i i ?=

其中)()()()(121-+++=i i a p a p a p a P 被称为符号i a 的累积概率。

如果初始区间为实数区间[0,1),那么初始条件为:1)(,0)(==φφA C 和1)(,0)(==φφp P 。

可见,在编码任何符号之前,初始区间为[))()(),(φφφA C C +=[)1,0,从上述描述的迭代关系可看出每次编码符号i a ,区间宽度都依据符号i a 的出现概率而变窄,随着编码的符号序列越来越长,相应的区间宽度也随之变得越来越窄,那么表示区间所需的数字位数也会越来越多,而且,我们还会发现,大概率符号比小概率符号使区间变窄的幅度要小,所增加的数字位数也较之更少,这是符合统计编码原理的。

下面的实例追踪这一编码过程,有助于我们理解算术编码工作的细节。

实例1:设待编码符号来自信源空间:1.02.03.01.01.02.0:)(!

:X p e d c b a X ,注意,

符号!作为结束符,作为编码和解码结束的标识,就像中文中句号的作用,若待

编码的符号串为一英文单词“dead!”,当然编码和解码过程都是从初始区间[)1,0开始的。

注意:上述信源X 的各符号的概率值来自某一统计模型,统计模型得到的各符号的概率值独立于特编码的符号串中各符号的概率值,是大量统计的产物,能比较真实的预测一个字符在待编码的整个符号序列中的概率值,例如一本英文小说中各字符的概率值是确定的,但是在编码时可能不允许事先统计其符号概率分布,但是我们事先经过某些方法统计过大量其他英文小说的字符概率分布,那么就用之前已知的字符概率分布来预测未知待编码字符串的字符概率分布。

如果让654321a a a a a a 分别依次对应!abcde ,那么就能用算术编码原理中介绍的两个迭代关系实现算术编码。开始时得到一张表,反映了开始时各个字符的累积概率和区间划分情况:

表1:信源X 中字符的分布概率、累积分布概率和初始区间划分情况

好,现在开始编码:

编码第1个字符d: 4.00.14.00)()()()(=?+=?+=φφφA d P C d C

3.00.13.0)()()(=?=?=φφA d p d A

区间范围由[0,1)更新为[)[)7.0,4.0)()(),(=+d A d C d C φφφ; 编码第2个字符e: 61.03.07.04.0)()()()(=?+=?+=d A e P d C e d C φφφ

06.03.02.0)()()(=?=?=d A e p e d A φφ

区间由[)7.0,4.0更新为[)[)67.0,61.0)()(),(=+de A de C de C φφφ;

编码第3个字符a: 过程类似;

编码第4个字符d: 过程类似;

编码第5个字符 !:

61804.00036.09.06148.0)()(!)()!(=?+=?+=dead A P dead C dead C φφφ 00036.00036.01.0)()(!)!(=?=?=dead A p dead A φφ

至此,所有字符都已编码,最后得到的区间为:

[)[)61840.0,61804.0)!()!(),!(=+dead A dead C dead C φφφ。

然后从此区间内选择一个数位较短的实数作为最后的码字输出给解码器。 上述编码过程如图1所示:

图1:编码“dead!”过程

解码原理可以看作是编码原理的逆过程,解码器开始也按照编码时各个字符的概率分布(来自统计模型)对初始区间[)1,0进行划分,然后解码器把从编码器接收到的码字与各字符相对应的概率区间作比较,比较的意思是说看码字属于哪个字符对应的概率区间,比如选择编码得到的最后区间的左端点实数0.61804作为码字,解码器就会发现码字0.61804属于字符d 所对应的概率区间[)7.0,4.0,然后就解码出第一个字符d,为了避免一次又一次的为字符分配区间,减少计算量,在解码出第一个字符后,比如解码出字符d 后,按照表1就把区间更新为d 所对应的区间)7.0,4.0[,区间大小A=0.3,然后开始解码下一个字符,用原码字0.61804减去字符d 对应区间的左端点值L=0.4,得到的值再除以d 对应区间的大小A=0.3即计算式:7268.03.0)4.061804.0()61804.0(=-=-A L ,然后把0.7268作为新的码字,通过查找表1发现0.7268属于字符e 对应的区间)9.0,7.0[,这就解码出第二个字符e,并把e 对应的区间)9.0,7.0[作为新区间,左端点L=0.7,区间大小A=0.2,不断重复上述过程,直到解码出结束符!,解码过程宣告完毕。

需要说明的是作为结束符的!是不可见的,严格说并不属于被编码字符串,是人为附加上去的,作用只是为了在解码器解码出全部字符之后作为标识来停止解码器的运行,如果没有结束符,解码器将无法判断何时结束解码。

2.3 二进制编码

上节无限精度的算术编码理论由于当前计算机基于二进制的设计而无法直接编程实现,而计算机能做到的就是对无限精度的实数进行近似模拟,这种近似对大量数据的压缩效果的影响很小,而算术编码在理论上能够很大程度上趋近信息熵的优越性使得其有很大的现实意义。

通过上节可知,算术编码每次编码和解码都要做乘法,而且必须在规定的时间内完成(信源符号周期限制),有时就难以实现,但若被编码对象本身或是被映射成二元序列(如01符号序列),且其符号概率较小者(用L 表示)的概率为Q L p -=2)(,其中Q 为整数,则上节介绍的无限精度的算术编码原理的两个迭代关系就可以在计算机上用移位和单纯的加减运算来实现,从而避免乘法。

另外,随着输入符号序列s 长度的增长,码字)(s C 的数位也随之相应增加,而这样精度的码字在计算机上是无法表示的(寄存器位数有限),这就要求将寄存器中存储的已编码的码字高位及时输出。而当寄存器中为输出部分高位均为1时,则低位运算略有增加,就可能进位到已输出部分,特别是当这种连续的1很长时。这一问题就是有限精度算术编码所固有的进位问题。J.Rissanen 和

G .G .Langdon 利用插入一个额外的0(即填充位)来隔断进位的扩展,这会对编码略有影响,但由于影响很小,所以下面仍用填充位来隔断进位的扩展。

类似码字的情况,区间大小)(s A 也只能基于有限位数的寄存器来实现更新。 有限精度的算术编码原理所用到的迭代关系与上节介绍的相同,但由于这次的编码对象是二进制序列,并且用Q -2来估计小概率符号的出现概率,所以能够进一步得到如下迭代关系:

设待编码符号来自符号集{}L H ,,并且若Q L p -=2)(,那么

Q L p H p --=-=21)(1)(;而人为让0)(=H P ,那么Q H p L P --==21)()(。

上述对初始区间[)1,0的最初划分情况可用下图表示:

图2:大概率符号H 与小概率符号L 在初始区间中的分布情况

从而,有限精度、不作乘法且将设变量Q 已经估计出的二进制算术编码顺序步骤如下:

1.初始化步骤:111.0)(,000.0)( ==φφA C ,C 和A 小数点后的位数由参数q 确定。

2.对区间宽度)(s A 更新:

当编码小概率符号L 时:Q L p sL A -?=2)()( (相当于)(s A 右移Q 位) 当编码大概率符号H 时:

)()(2)()()()21()()()(sL A s A s A s A s A s A H p sH A Q Q -=?-=?-=?=--

3.对码字)(s C 更新:

当编码大概率符号H 时:)()()()()(s C s A H P s C sH C =?+=

当编码小概率符号L 时:

)()()()21()()()()()(sH A s C s A s C s A L P s C sL C Q +=?-+=?+=-

4.如果发现010.0)(

5.如果紧靠码字)(s C 的小数点前有连续v 个1,则紧靠小数点前的位置插入一填充位0,来隔断进位扩展。

6.按上述步骤对字符序列中的所有字符进行上述处理过程,编码完毕后输出最后的码字)(s C 。

步骤1中初始区间宽度在多大程度上趋近于1取决于参数q,而参数q 值大小又一般依据)(L p 而定,而)(L p 是统计模型对下一位是小概率符号L 可能出现的概率预测,例如如果有)(L p 的最小值为152-,那么参数q 值就一般取16。

如果没有步骤4,那么区间A 当缩小到很小时,就必须用很高的精度才能把它和0区分开来,而步骤4的作用就是,当发现区间小到某一程度时,就将区间左移若干位,直到区间大于某种程度,而区间A 加倍后,码字C 也须随之加倍。

实例2:编码二元符号串“01000101”,统计出大概率符号H 是‘0’,小概率符号是‘1’,某一统计模型提供的对这个长度为8的字符串各个位置出现小概率符号L 的概率预测分别为221322122,2,2,2,2,2,2,2--------,最大的Q 值为3,那么就取q=4,v=3。现在对其进行编码:

编码第1个符号‘0’:统计模型预测22)1(-=p

0011.021111.0)1()()1(2=?=?=-p A A φφ

1100.00011.01111.0)1()()0()()0(=-=-=?=φφφφA A p A A

0000.0)()0(==φφC C

编码第2个符号‘1’:统计模型预测12)1(-=p

0110.021100.0)1()0()01(1=?=?=-p A A

0110.00110.01100.0)01()0()00(=-=-=A A A

0110.00110.00000.0)00()0()01(=+=+=A C C

此时,由于,1000.0)01(

A (01)=0.1100,C (01)=0.1100

编码第3个符号‘0’:过程类似,得到:

A (010)=0.1001, C (010)=0.1100

编码第4个符号‘0’:过程类似,得到:

A (0100)=0.1110, C (0100)=1.1000

编码第5个符号‘0’:过程类似,得到:

A (01000)=0.1101, C (01000)=1.1000

编码第6个符号‘1’:过程类似,得到:

A (010001)=0.1100, C (010001)=11.1110

编码第7个符号‘0’:统计模型预测22)1(-=p

此次遇到了新的情况,下面是完整的过程:

0011.021100.0)1()010001()0100011(2=?=?=-p A A

0110.00011.01100.0)0100011()010001()0100010(=-=-=A A A

1110.11)010001()0100010(==C C

此时,由于A (0100010)<0.1000,因此A (0100010)与C (0100010)须左移1位,得: A (01000010)=0.1100,C (0100010)=111.1100

此时又发现,C (0100010)紧靠小数点前有v=3个连续1,因此还需在C (0100010)紧靠其小数点前插入一填充位0,那么C (0100010)=1110.1100

编码最后一个符号‘1’:统计模型预测22)1(-=p

0011.021100.0)1()0100010()01000101(2=?=?=-p A A

1001.00011.01100.0)01000101()0100010()01000100(=-=-=A A A

0101.11111001.01100.1110)01000100()0100010()01000101(=+=+=A C C

此时,由于A (01000101)<0.1000,因此,须将A (01000101)和C (01000101)左移两位,得:

A (01000101)=0.1100,C (01000101)=111101.0100

上述编码过程如图所示:

图3:编码“01000101”过程

注:上图中的箭头是为了更清楚的表明左移的过程,每一区间只标明了两端点的值,右端点由码字C和区间A的和组成,在图的最右部分标明的符号是为了说明各个符号所最终对应的区间。

此时,编码就宣告结束了,编码器最后一项工作就是从最后得到的区间中选择一个代表性的二进制小数作为码字输出,最后得到的区间为:

[C (01000101),C (01000101)+A (01000101))=[111101.0100,111110.0000),回想在编码过程中共做了5次左移处理(在图3中用箭头标识),码字C 和区间A 共左移(1+1+1+1+2)=6比特位,因此,要考虑码字C 和区间A 实际的小数点位置,须将代表区间的数值右移6位,那么,最后得到的区间就变为

[0.1111010100,0.1111100000),此区间信息可作为编码输出信息给解码器,但是只要从此区间内选择一个位数比较短的值并且省略掉小数点和小数点前的0作为输出码字就可以,例如将1111011作为最后的码字输出。这个最终码字的码字长度有7位,比用ASCII 表示的被编码二元字符串的长度要小很多。

2.4 二进制解码

二进制解码是二进制编码的逆过程。

解码器从编码器接收到的码字,去掉了小数点和小数点之前的0,并且这个码字中可能还包括填充位,如果有填充位的话,在开始解码前还要除去填充位的影响,回想,在二进制编码步骤5中,如果发现码字C 的小数点前有v 个连续1,那么就在紧靠小数点前插入填充位0,那么,解码时,当发现码字整数部分的高位有v 个连续1,就要检测第v+1位,第v+1为可能为0可能为1,0的情况说明当编码计算码字时,没有向第v+1位有进位,1的情况说明编码计算码字时,有向第v+1位有进位,使得填充位位置的0变为了1,所以解码前去掉填充的影响要分别考虑这两种情况。

解码器也从区间大小为111.0)( =s A 的区间作为开始解码的初始区间,如果当前未解码字符表示为x ,在字符x 之前已经被解码的字符串表示为s ',有了以上讨论和假设,二进制解码步骤可表述如下:

1 初始化区间:111.0)( ='s A

2 解码器检测码字C 的高位部分的v 位的二进制位值:

如果发现v 位二进制位都是1,那么接着检测第v+1位即填充位的值:

若第v+1位为0,说明没有向第v+1位进位,填充位的位置还是0,那么就直接去掉第v+1位的0,即除去了填充位的影响。

若第v+1位为1,说明又向第v+1位进位,填充位位置的0由于进位的影响由0变为了1,那么去掉此位后,由于后面有进位,还要对码字进行加1处理,加1的位置是第v 位。

3 子区间宽度A 更新:

对小概率符号L :Q s A L s A -?'='2)()(

对大概率符号H :)()()(L s A s A H s A '-'='

4 判断解码字符:

如果:)()(H s A s C '<则解码出字符为H ,并置)()(H s A s A '='

如果:)()(H s A s C '≥则解码出字符为L ,并置)()()(H s A s C s C '-=, )()(L s A s A '=' 5 如果A (sx )<010.0 ,则A 与C 重复左移,直到010.0)( ≥sx A

6 回到步骤2,接着解码下一个字符,直到解码出所有字符。

3 哈夫曼编码

3.1 哈夫曼编码简介

哈夫曼编码是一种变字长编码方法,对出现概率较大的信源符号赋予较短的码字,对出现概率较小的信源符号赋予较长的码字,但哈夫曼编码被称为最佳码并不仅仅如此,重要的是按照什么方法去为信源符号分配码字,哈夫曼编码通过构造哈夫曼编码表来达到最佳性,哈夫曼编码对信源符号进行编码,能够保证各码字的码长严格按照所对应的信源符号的出现概率大小逆序排列,使得在变字长分组码范畴内,使其得到的平均码长最小。

3.2 哈夫曼编码原理

因为信源多种多样,那么不妨在编码时把普通的计算机文件作为信源,如一个文本文件且此文本文件中字符由ASCII 码表示,那么此种文件都是由一个个的字节组成,每字节都是一个字符,当然每个字符的ASCII 码表示都是8位,共256种,因此,这样的文件最多含有256种字符。这样,文件中的所包含的字符都可看作一个信源符号。

哈夫曼编码事先要统计文件中各个字符的出现概率,如果文件允许编码器 统计字符,那么通过统计文件中包含的总字符数和每种字符在文件中出现的次数,就很容易计算得到每种字符在文件中出现的概率。

构造一个文件的哈夫曼编码表是文件编码与解码的关键。

设某个文件中含有q 种字符:q s s s s ,,,,321 ,并且统计出每种字符在文件中的出现概率分别为:)(,),(),(),(321q s p s p s p s p ,则编码的具体步骤为:

(1) 将q 个信源符号q s s s s ,,,,321 ,按其概率分布)(,),(),(),(321q s p s p s p s p 的大小,以递减次序,由大到小进行排序。

(2) 用字符‘0’和‘1’分别代表概率最小的两个信源符号,并把这两个概率最小的信源符号的概率相加,所得的概率和值用一个虚拟信源符号代表,与余

下的(q -2)个信源符号组成含有(q -1)个信源符号的新信源,称作第一次缩减信源1X 。

(3) 把缩减信源1X 中的符号仍按照相应概率大小以递减次序进行排列,再将其中

两个概率最小的信源符号分别用‘0’和‘1’表示,并把这两个符号的概率相加,所得的概率和值用另一个虚拟信源符号代表,这样又形成了含有(q -2)个信源符号的第二次缩减信源2X 。

(4) 按照上述步骤,依次继续下去,直到信源中只剩下两个信源符号为止,将这最后两个信源符号分别用‘0’和‘1’表示,这两个信源符号的概率之和必定等于1,也用一虚拟信源符号代表。

(5) 然后从最后得到的概率为1的信源符号开始,进行回推,把回推过程中所遇到的‘0’和‘1’按先后顺序排列成字符串,这样的字符串对应着各个不同字符,因为这样的‘0’和‘1’组成的字符串并不是计算机中的二进制,所以不妨称为伪码字。

上述编码过程,就是为文件中的字符建立了一一映射的关系i i c s f →:,i=1,2,……,q,其中i s 代表不同的字符,i c 代表对应字符i s 的伪码字。

为了将伪码字替换为真正的码字,还必须建立一个映射关系i i w c g →:,i=1,2,……,q,其中i c 与映射关系f 中同义,i w 代表对应字符的码字。映射g 的作用就是将由字符串组成的伪码字替换成码字,从而通过映射i i w s f g =))((,

建立了哈夫曼编码表。

下面具体说明一下怎样把伪码字替换为码字,在此用一种间接的方法解决这个问题,具体做法为:

(1) 将源文件中每个字符通过查找哈夫曼编码表的办法用伪码字作替换,将替换的结果保存在一个临时文件temporary.txt 中,临时文件temporary.txt 就是一个由字符‘0’和‘1’组成的字符串。一般来说,这个临时文件要比原文件大得多,比如,原文件中的字符‘a ’用伪码字“001110010”替换,前者只占一个字节,而后者最少占10个字节,临时文件当然不是最终得到的压缩文件,只是为了便于实现压缩与解压。

(2) 接下来是将临时文件temporary.txt 变为由码字组成的文件。方法是,相当于建立一个映射关系i i d str h →:,i=1,2,……,256,其中i str 是由字符‘0’和‘1’组成的字符数为8的字符串,i d 是介于0和266之间的整数。临时文件

从结构上说是由不等长的伪码字组成的,由伪码字映射后的码字也是不等长的,而计算机中的存储单位一般都是单字节的倍数,如字节,双字节,四字节,因此很难用一个准确的存储空间单位来存储码字,如果空间太大,对码字长度短的码字来说是一种空间浪费,而空间太小,对码字长度长的码字来说又无法存放,因此,为了压缩与解压的方便,不如将temporary.txt 进行等长划分,使得连续的8个字符为一组,组成i str ,用一整数i d 做替换,替换的结果是得到由整数i d 即二进制组成的压缩文件。

上述码字替换伪码字的过程,可能会出现最后一组‘0’和‘1’组成的字符串不足8个字符,那么就保持不变,不做替换,附加到压缩文件中。

3.3 哈夫曼解码原理

哈夫曼解码过程是文件编码过程的逆过程,由于哈夫曼编码可即时解码,因此只要得到一个码字i w ,则通过查找哈夫曼编码表得到相应的字符,映射过程是编码时映射的逆过程:))((11i w g f --。因此,每从压缩文件中读出一个码字,就从通过查找哈夫曼编码表用字符替换相应的码字,当压缩文件中所有的码字被字符替换掉,也就宣告解压过程完成了。

虽然解压是压缩的逆过程,但是还不能直接通过对哈夫曼编码表反向查找将压缩文件解压。首先,在压缩文件中除了最后字符数不满8的字符串作为附加数据没有压缩外,其他的每个字节都对应着一个长度为8的字符串。因此,通过映射h 的逆映射1-h 将压缩文件中的整数替换为8位字符串i str ,全部替换完毕后不要忘了将没有压缩的字符串附加到临时文件temporary.txt 中去,这样就得到了完整的临时文件temporary.txt 。由于临时文件是由伪码字组成的,通过映射f 的逆映射i i s c f →-:1,实现用字符i s 替换伪码字i c ,全部都替换完毕后,就实现了解压。

3.4 哈夫曼编码与解码系统模型

有了上述讨论,哈夫曼编码与解码系统模型可以简单如图所示:

图4:哈夫曼编码系解码系统模型

哈夫曼编码与译码报告

一、设计思想 程序要求: 利用哈夫曼树对字符串进行编码,要求每个字符有自己唯一的编码。将得到的一串字串译成0、1编码后存到一个文件夹中,然后再从这个文件夹中读出这串编码进行解码。 实现方法: 输入一串字符,要求字符的区间为大写的26个英文字母,将获得的串字符用计算权值的函数(jsquanzhi())进行字符统计,统计出现的字符种数以及每种字符出现的次数,将该种字符出现的次数作为它的权值。将出现的字符的权值和该字符依次分别赋给两个结构体HT和HC,利用HT(节点)权值的大小建立哈夫曼树,首先用选择函数select()函数选择两个权值最小的字符作为叶子节点,创建一个新的节点作为这两个叶节点的父节点,被选中的节点给他的HT[i].parent赋值是他下次不再被选中,父节点的权值为,子节点的权值之和。然后将该将父节点放入筛选区中,再进行选择(被选过的不再被使用),直到所有的节点都被使用,这样一个哈夫曼树就被建立了。根据每个字符在哈夫曼书中的位置来编译每个字符的0、1密文代码,从叶节点判断该叶节点是其父节点的左右字左字为‘0’,右子为‘1’,在判断父节点是上级父节点的左右子直至根节点,将生成的0、1字符串按所表示的字符倒序存入HC相应的字符的bins[]数组。 重新一个一个字符的读取输入的字符串,按照字符出现的顺序将它转为0、1代码并存到一个txt文件夹中去。解码时从文件夹中,一个一个字符的读出那串0、1代码赋给一个临时字符串cd[],用该字符串与每个字符的HC[i].bins密文比较,直到与一个字符的密文相同时,译出该字符,将字符存放在临时字符数组tempstr[]中,清空临时字符串继续读取0、1代码进行翻译,直至文件密文结束为止。于是就得到了原先被加密的那串字符串。

中衡算法分析与【设计明细】-实验二-哈夫曼编码

昆明理工大学信息工程与自动化学院学生实验报告 (201 —201 学年第一学期) 课程名称:算法设计与分析开课实验室:年月日 一、上机目的及内容 1.上机内容 设需要编码的字符集为{d1, d2, …, dn},它们出现的频率为{w1, w2, …, wn},应用哈夫曼树构造最短的不等长编码方案。 2.上机目的 (1)了解前缀编码的概念,理解数据压缩的基本方法; (2)掌握最优子结构性质的证明方法; (3)掌握贪心法的设计思想并能熟练运用。 二、实验原理及基本技术路线图(方框原理图或程序流程图) (1)证明哈夫曼树满足最优子结构性质; (2)设计贪心算法求解哈夫曼编码方案; (3)设计测试数据,写出程序文档。 数据结构与算法: typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码 typedef struct { unsigned int weight; //用来存放各个结点的权值 unsigned int parent,LChild,RChild; //指向双亲、孩子结点的指针 } HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树 程序流程图:

三、所用仪器、材料(设备名称、型号、规格等或使用软件) 1台PC及VISUAL C++6.0软件

四、实验方法、步骤(或:程序代码或操作过程) 程序代码: #include #include #include typedef struct { unsigned int weight; unsigned int parent,LChild,RChild; } HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树 typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码 void Select(HuffmanTree *ht,int n,int *s1,int *s2) { int i,min; for(i=1; i<=n; i++) { if((*ht)[i].parent==0) { min=i; break; } } for(i=1; i<=n; i++) { if((*ht)[i].parent==0) { if((*ht)[i].weight<(*ht)[min].weight) min=i; } } *s1=min; for(i=1; i<=n; i++) { if((*ht)[i].parent==0 && i!=(*s1)) { min=i; break; } } for(i=1; i<=n; i++) { if((*ht)[i].parent==0 && i!=(*s1)) { if((*ht)[i].weight<(*ht)[min].weight)

哈夫曼树编码译码实验报告(DOC)

数据结构课程设计设计题目:哈夫曼树编码译码

目录 第一章需求分析 (1) 第二章设计要求 (1) 第三章概要设计 (2) (1)其主要流程图如图1-1所示。 (3) (2)设计包含的几个方面 (4) 第四章详细设计 (4) (1)①哈夫曼树的存储结构描述为: (4) (2)哈弗曼编码 (5) (3)哈弗曼译码 (7) (4)主函数 (8) (5)显示部分源程序: (8) 第五章调试结果 (10) 第六章心得体会 (12) 第七章参考文献 (12) 附录: (12)

在当今信息爆炸时代,如何采用有效的数据压缩技术节省数据文件的存储空间和计算机网络的传送时间已越来越引起人们的重视,哈夫曼编码正是一种应用广泛且非常有效的数据压缩技术。哈夫曼编码是一种编码方式,以哈夫曼树—即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。哈弗曼编码使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。哈夫曼编码的应用很广泛,利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。树中从根到每个叶子都有一条路径,对路径上的各分支约定:指向左子树的分支表示“0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为和各个叶子对应的字符的编码,这就是哈夫曼编码。哈弗曼译码输入字符串可以把它编译成二进制代码,输入二进制代码时可以编译成字符串。 第二章设计要求 对输入的一串电文字符实现哈夫曼编码,再对哈夫曼编码生成的代码串进行译码,输出电文字符串。通常我们把数据压缩的过程称为编码,解压缩的过程称为解码。电报通信是传递文字的二进制码形式的字符串。但在信息传递时,总希望总长度能尽可能短,即采用最短码。假设每种字符在电文中出现的次数为Wi,编码长度为Li,电文中有n种字符,则电文编码总长度为∑WiLi。若将此对应到二叉树上,Wi为叶结点的权,Li为根结点到叶结点的路径长度。那么,∑WiLi 恰好为二叉树上带权路径长度。因此,设计电文总长最短的二进制前缀编码,就是以n种字符出现的频率作权,构造一棵哈夫曼树,此构造过程称为哈夫曼编码。设计实现的功能: (1) 哈夫曼树的建立; (2) 哈夫曼编码的生成; (3) 编码文件的译码。

哈夫曼树的编码与译码

目录 一、摘要 (3) 二、题目 (3) 三、实验目的 (3) 四、实验原理 (3) 五、需求分析 (4) 5.1实验要求 (4) 5.2实验内容 (4) 六、概要设计 (4) 6.1所实现的功能函数 (4) 6.2主函数 (5) 6.3 系统结构图 (6) 七、详细设计和编码 (6) 八、运行结果 (12) 九、总结 (15) 9.1调试分析 (15) 9.2 心得体会 (15) 参考文献 (16)

一、摘要 二、题目 哈夫曼树的编码与译码 三、实验目的 (1)熟悉对哈夫曼的应用以及构造方法,熟悉对树的构造方式的应用; (2)进一步掌握哈夫曼树的含义; (3)掌握哈夫曼树的结构特征,以及各种存储结构的特点以及使用范围; (4)熟练掌握哈夫曼树的建立和哈夫曼编码方法; (5)提高分析问题、解决问题的能力,进一步巩固数据结构各种原理与方法; (6)掌握一种计算机语言,可以进行数据算法的设计。 四、实验原理 哈夫曼(Huffman)编码属于长度可变的编码类,是哈夫曼在1952年提出的一种编码方法,即从下到上的编码方法。同其他码词长度一样,可区别的不同码词的生成是基于不同符号出现的不同概率。生成哈夫曼编码算法基于一种称为“编码树”(coding tree)的技术。算法步骤如下: (1)初始化,根据富豪概率的大小按由大到小顺序对符号进行排序; (2)把概率最小的两个符号组成一个新符号(节点),即新符号的概率等于这两个符号概率之和; (3)重复第(2)步,直到形成一个符号为止(树),其概率最后等于1; (4)从编码树的根开始回溯到原始的符号,并将每一下分支赋值1,上分支赋值0; 译码的过程是分解电文中字符串,从根出发,按字符“0”或者“1”确定找做孩 子或右孩子,直至叶子节点,便求得该子串相应的字符。

实验三.哈夫曼编码的贪心算法设计

实验四 哈夫曼编码的贪心算法设计(4学时) [实验目的] 1. 根据算法设计需要,掌握哈夫曼编码的二叉树结构表示方法; 2. 编程实现哈夫曼编译码器; 3. 掌握贪心算法的一般设计方法。 实验目的和要求 (1)了解前缀编码的概念,理解数据压缩的基本方法; (2)掌握最优子结构性质的证明方法; (3)掌握贪心法的设计思想并能熟练运用 (4)证明哈夫曼树满足最优子结构性质; (5)设计贪心算法求解哈夫曼编码方案; (6)设计测试数据,写出程序文档。 实验内容 设需要编码的字符集为{d 1, d 2, …, dn },它们出现的频率为 {w 1, w 2, …, wn },应用哈夫曼树构造最短的不等长编码方案。 核心源代码 #include #include #include typedef struct { unsigned int weight; //用来存放各个结点的权值 unsigned int parent,LChild,RChild; //指向双亲、孩子结点的指针 } HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树 typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码 ∑=j i k k a

//选择两个parent为0,且weight最小的结点s1和s2 void Select(HuffmanTree *ht,int n,int *s1,int *s2) { int i,min; for(i=1; i<=n; i++) { if((*ht)[i].parent==0) { min=i; break; } } for(i=1; i<=n; i++) { if((*ht)[i].parent==0) { if((*ht)[i].weight<(*ht)[min].weight) min=i; } } *s1=min; for(i=1; i<=n; i++)

哈夫曼编码与译码的实现

数据结构课程设计评阅书

2011—2012学年第一学期 专业:信息管理与信息系统学号: 1021024016 姓名:万永馨 课程设计名称:数据结构课程设计 设计题目:哈夫曼编码与译码的实现 完成期限:自 2012 年 2 月 20 日至 2012 年 3 月 2 日共 2 周 设计依据、要求及主要内容(可另加附页): 该设计题目将按以下要求完成: 哈夫曼编码与译码是信息传输中应用的经典算法,运用C或VC++结合数据结构等基础知识,按 以下要求编程实现各种进制的转换。 任务要求:1)阐述设计思想,画出流程图;2)需要对哈夫曼编码/译码的相关原理有所了解,设计数 据结构,建立必要的信息数据文件(最好存储成外部文件),并分析完成用户所需的基本操作功能;3)实现给定信息的编码和译码功能;4)应有较好的界面设计,说明程序测试方法;5)按照格式要 求完成课程设计说明书。 设计要求: 1)问题分析和任务定义:根据设计题目的要求,充分地分析和理解问题,明确问题要求做什么?(而不是怎么做?)限制条件是什么?确定问题的输入数据集合。 2)逻辑设计:对问题描述中涉及的操作对象定义相应的数据类型,并按照以数据结构为中心的 原则划分模块,定义主程序模块和各抽象数据类型。逻辑设计的结果应写出每个抽象数据类型的定 义(包括数据结构的描述和每个基本操作的功能说明),各个主要模块的算法,并画出模块之间的调 用关系图; 3)详细设计:定义相应的存储结构并写出各函数的伪码算法。在这个过程中,要综合考虑系统 功能,使得系统结构清晰、合理、简单和易于调试,抽象数据类型的实现尽可能做到数据封装,基 本操作的规格说明尽可能明确具体。详细设计的结果是对数据结构和基本操作做出进一步的求精, 写出数据存储结构的类型定义,写出函数形式的算法框架; 4)程序编码:把详细设计的结果进一步求精为程序设计语言程序。同时加入一些注解和断言, 使程序中逻辑概念清楚; 5)程序调试与测试:能够熟练掌握调试工具的各种功能,设计测试数据确保程序正确。调试正 确后,认真整理源程序及其注释,形成格式和风格良好的源程序清单和结果; 6)结果分析:程序运行结果包括正确的输入及其输出结果和含有错误的输入及其输出结果。算 法的时间、空间复杂性分析; 7)编写课程设计报告; 以上要求前三个阶段的任务完成后,将设计说明书的草稿交指导老师面审,审查合格方可进入 后续阶段的工作。设计工作结束,经指导老师验收合格后将设计说明书装订,并答辩。

哈夫曼编码的方法

1.哈夫曼编码的方法 编码过程如下: (1) 将信源符号按概率递减顺序排列; (2) 把两个最小的概率加起来, 作为新符号的概率; (3) 重复步骤(1) 、(2), 直到概率和达到1 为止; (4) 在每次合并消息时,将被合并的消息赋以1和0或0和1; (5) 寻找从每个信源符号到概率为1处的路径,记录下路径上的1和0; (6) 对每个符号写出"1"、"0"序列(从码数的根到终节点)。 2.哈夫曼编码的特点 ①哈夫曼方法构造出来的码不是唯一的。 原因 ·在给两个分支赋值时, 可以是左支( 或上支) 为0, 也可以是右支( 或下支) 为0, 造成编码的不唯一。 ·当两个消息的概率相等时, 谁前谁后也是随机的, 构造出来的码字就不是唯一的。 ②哈夫曼编码码字字长参差不齐, 因此硬件实现起来不大方便。 ③哈夫曼编码对不同的信源的编码效率是不同的。 ·当信源概率是2 的负幂时, 哈夫曼码的编码效率达到100%; ·当信源概率相等时, 其编码效率最低。 ·只有在概率分布很不均匀时, 哈夫曼编码才会收到显著的效果, 而在信源分布均匀的情况下, 一般不使用哈夫曼编码。 ④对信源进行哈夫曼编码后, 形成了一个哈夫曼编码表。解码时, 必须参照这一哈夫编码表才能正确译码。 ·在信源的存储与传输过程中必须首先存储或传输这一哈夫曼编码表在实际计算压缩效果时, 必须考虑哈夫曼编码表占有的比特数。在某些应用场合, 信源概率服从于某一分布或存在一定规律

使用缺省的哈夫曼编码表有

解:为了进行哈夫曼编码, 先把这组数据由大到小排列, 再按上方法处理 (1)将信源符号按概率递减顺序排列。 (2)首先将概率最小的两个符号的概率相加,合成一个新的数值。 (3)把合成的数值看成是一个新的组合符号概率,重复上述操作,直到剩下最后两个符号。 5.4.2 Shannon-Famo编码 Shannon-Famo(S-F) 编码方法与Huffman 的编码方法略有区别, 但有时也能编 出最佳码。 1.S-F码主要准则 符合即时码条件; 在码字中,1 和0 是独立的, 而且是( 或差不多是)等概率的。 这样的准则一方面能保证无需用间隔区分码字,同时又保证每一位码字几乎有 1位的信息量。 2.S-F码的编码过程 信源符号按概率递减顺序排列; 把符号集分成两个子集, 每个子集的概率和相等或近似相等;

哈夫曼编码算法实现完整版

实验三树的应用 一.实验题目: 树的应用——哈夫曼编码 二.实验内容: 利用哈夫曼编码进行通信可以大大提高信道的利用率,缩短信息传输的时间,降低传输成本。根据哈夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求哈夫曼编码。 要求:从键盘输入若干字符及每个字符出现的频率,将字符出现的频率作为结点的权值,建立哈夫曼树,然后对各个字符进行哈夫曼编码,最后打印输出字符及对应的哈夫曼编码。 三、程序源代码: #include #include #include #include typedef struct{ char data; int weight; int parent,lchild,rchild; }HTNode,*HuffmanTree; typedef char * * HuffmanCode; void Select(HuffmanTree &HT,int n,int m) {HuffmanTree p=HT; int tmp; for(int j=n+1;j<=m;j++) {int tag1,tag2,s1,s2; tag1=tag2=32767; for(int x=1;x<=j-1;x++) { if(p[x].parent==0&&p[x].weights2) //将选出的两个节点中的序号较小的始终赋给s1 { tmp=s1; s1=s2; s2=tmp;} p[s1].parent=j;

哈夫曼树的编码和译码

#include"stdafx.h" #include"stdio.h" #include"conio.h" #include #include #include using namespace std; #define maxbit 100 #define Maxvalue 2000//最大权值整数常量#define Maxleaf 100//最大叶子结点数 #define size 300//0、串数组的长度 static int n;//实际的叶子结点数 struct HNodeType { int weight; int parent; int lchild; int rchild; int ceng;//结点相应的层数 char ch;//各结点对应的字符 }; struct HCodeType { int bit[maxbit];//存放编码的数组 int start;//编码在数组中的开始位置}; static HNodeType *HuffNode;//定义静态指针HNodeType *init()//初始化静态链表 { HuffNode=new HNodeType[2*n-1]; for(int i=0;i<2*n-1;i++) { HuffNode[i].weight=0; HuffNode[i].parent=-1; HuffNode[i].lchild=-1; HuffNode[i].rchild=-1; HuffNode[i].ceng=-1; HuffNode[i].ch='0'; } return HuffNode; }

算术编码与哈夫曼编码

安徽大学 本科毕业论文(设计、创作)题目:哈夫曼编码与算术编码压缩效率比较 学生姓名:李伟学号:E20714134 院(系):计算机科学与技术专业:软件工程 入学时间:2007年9月 导师姓名:韩莉职称/学位:讲师/硕士 导师所在单位:安徽大学计算机科学与技术学院 完成时间:2011年5月

哈夫曼编码与算术编码压缩效率比较 摘要 算术编码和哈夫曼编码都利用信源符号的概率分布特性进行编码,使平均码长逼近信息熵是压缩编码算法的第一要求,算术编码比哈夫曼编码逼近信息熵的能力要强,但是编码效率和实现往往是一对矛盾,编码效率的提高,往往要在实现上付出代价,所以,选择压缩算要权衡这两点。本论文开篇先引入了信息论的一些概念,因为编码理论发源于信息论,是各种编码算法的数学基础。然后在第2章分析了算术编码原理,并从无限精度的算术编码原理过渡到在计算机上能够实现的二进制编码原理。在第3章紧接着介绍了哈夫曼编码原理,并讨论了怎样把信源符号映射为对应的码字,过程中用到的哈夫曼编码表是编码与解码的关键。在第4章对两者的编码效率作了比较,主要是结合信息论中的一些概念从微观上对两者逼近信息熵的能力作了比较,并在这章最后对两者在文本文件的压缩效果的表现上给出了一些实验结果。最后,在5章,主要是对前面内容做了些补充和总结。 关键词:信息熵;算术编码;哈夫曼编码;编码效率

The comparison of Huffman Coding and Arithmetic Coding in File Compression Abstract Full use of the probability distribution of source symbols is the feature of the arithmetic encoding and Huffman encoding. Approaching the average code length to the information entropy come first when designing a compression algorithm. To the capacity of closing to information entropy, arithmetic encoding is stronger than Huffman encoding. However, the coding efficiency and implementation is often a contradiction: to improve coding efficiency, which means the algorithm implementation process needs to pay a higher price. Therefore, you need to weigh both when choosing a compression algorithm. In the beginning of this thesis, it first introduced some of the concepts of information theory. Because encoding algorithms are derived from information theory and information theory is the mathematical foundation of various coding algorithms. Then in Chapter 2, it introduces the principle of arithmetic coding. For better to understand the binary arithmetic coding principle, it first introduces the unlimited precision arithmetic coding. In Chapter 3, it describes the Huffman coding theory, and discusses how to map source symbol to the corresponding code word, among which Huffman coding and decoding table is the key. In Chapter 4, the coding efficiency of the two algorithms is compared. Mainly compare the capacities to approximate information entropy with some of the concepts in information theory. And the final part in this chapter, some experimental results are given to show the compression effect to compress a text file. Finally, in Chapter 5, it gives some additions and summary. Keywords:Information Entropy; Arithmetic Coding; Huffman Coding;Coding Efficiency

哈夫曼树建立、哈夫曼编码算法的实现

#include /*2009.10.25白鹿原*/ #include /*哈夫曼树建立、哈夫曼编码算法的实现*/ #include typedef char* HuffmanCode;/*动态分配数组,存储哈夫曼编码*/ typedef struct { unsigned int weight ; /* 用来存放各个结点的权值*/ unsigned int parent, LChild,RChild ; /*指向双亲、孩子结点的指针*/ }HTNode, * HuffmanTree; /*动态分配数组,存储哈夫曼树*/ void select(HuffmanTree *ht,int n, int *s1, int *s2) { int i; int min; for(i=1; i<=n; i++) { if((*ht)[i].parent == 0) { min = i; i = n+1; } } for(i=1; i<=n; i++) { if((*ht)[i].parent == 0) { if((*ht)[i].weight < (*ht)[min].weight) min = i; } } *s1 = min; for(i=1; i<=n; i++) { if((*ht)[i].parent == 0 && i!=(*s1)) { min = i; i = n+1; } } for(i=1; i<=n; i++) { if((*ht)[i].parent == 0 && i!=(*s1)) {

if((*ht)[i].weight < (*ht)[min].weight) min = i; } } *s2 = min; } void CrtHuffmanTree(HuffmanTree *ht , int *w, int n) { /* w存放已知的n个权值,构造哈夫曼树ht */ int m,i; int s1,s2; m=2*n-1; *ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); /*0号单元未使用*/ for(i=1;i<=n;i++) {/*1-n号放叶子结点,初始化*/ (*ht)[i].weight = w[i]; (*ht)[i].LChild = 0; (*ht)[i].parent = 0; (*ht)[i].RChild = 0; } for(i=n+1;i<=m;i++) { (*ht)[i].weight = 0; (*ht)[i].LChild = 0; (*ht)[i].parent = 0; (*ht)[i].RChild = 0; } /*非叶子结点初始化*/ /* ------------初始化完毕!对应算法步骤1---------*/ for(i=n+1;i<=m;i++) /*创建非叶子结点,建哈夫曼树*/ { /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2返回*/ select(ht,i-1,&s1,&s2); (*ht)[s1].parent=i; (*ht)[s2].parent=i; (*ht)[i].LChild=s1; (*ht)[i].RChild=s2; (*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight; } }/*哈夫曼树建立完毕*/ void outputHuffman(HuffmanTree HT, int m) { if(m!=0) {

哈夫曼编码与译码器_数据结构课程设计报告

沈阳航空航天大学 课程设计报告 课程设计名称:数据结构课程设计 课程设计题目:实现哈夫曼编码和译码器 院(系):计算机学院 专业:计算机科学与技术 班级:24010102 学号:2012040101082 姓名:尹伟和 指导教师:徐蕾

此页为任务书

目录 1.题目分析 (1) 1.1.题目重述 (1) 1.1.1.系统功能需求分析 (1) 2.程序设计 (2) 2.1.系统功能模块说明 (2) 2.1.1.系统功能模块结构 (2) 2.1.2.系统模块功能说明 (3) 2.2.数据结构说明 (3) 2.2.1.结构体定义说明 (3) 2.2.2.哈夫曼树 (4) 2.2.3.字符-哈夫曼编码对照表 (4) 2.3.函数说明 (4) 3.算法描述 (6) 3.1.哈夫曼树的构建 (6) 3.2.字符-哈夫曼编码对照表 (6) 3.3.编码 (6) 3.4.译码 (7) 4.程序测试 (9) 4.1.字符集输入 (9) 4.2.编码测试 (10) 4.3.译码测试 (11) 参考文献 (13) 附录(程序清单) (14)

沈阳航空航天大学课程设计报告 1.题目分析 1.1.题目重述 本次课程设计的目标是实现一个哈夫曼编码和译码器。该哈夫曼编码和译码器需要根据用户输入的字符集及相应字符出现的频率,对字符集所包含的字符进行哈夫曼编码。同时,作为编码器需要其对用户提供的明文字符串进行编码,使明文字符串变为二进制密文;作为译码器需要对用户提供的二进制密文进行译码,使二进制密文变为字符明文。 1.1.1.系统功能需求分析 通过对课程设计的题目分析,可以得出哈夫曼编码和译码器的功能需求,需求如下: 1)读取用户输入的字符集和相应字符出现的频率; 2)根据用户输入构建哈夫曼树; 3)根据哈夫曼树构建字符-哈夫曼编码对照表; 4)根据字符-哈夫曼编码对照表对明文字符串进行编码; 5)根据哈夫曼树对二进制密文进行译码。

赫夫曼树的编码译码

#include #include #include typedef struct{ int weight; int lchild,rchild,parent; }Htnode,*HuffmanTree; //哈弗曼树节点类型,动态分配数组存储哈弗曼树typedef char * * Huffmancode; //动态分配数组存储哈弗曼编码表 void bianma(Huffmancode ch,int n); //编码 void yima(Htnode *HT, int n); //译码 int createtree(Htnode *&HT, Huffmancode &HC,int *weight,int n); //构建哈弗曼树void select(Htnode *HT,int n,int *s1,int *s2); //求节点中两个最小数 /*----------main 函数----------*/ int main (void) { Htnode *HT; int n=4,a; //叶子节点4个 int weight[5]={18,7,5,2,4}; //weight[0]为权值之和 char ch[4]={'A','B','C','D'},c; char **HC; createtree(HT, HC,weight, n); while(a!=0){ //a=0时退出,so是按0键退出,或者改成a!=3 printf("***编码请按1,译码请按2,退出请按0***"); printf("请输入您的选择:\n"); scanf("%d%c",&a,&c); switch(a){ case 1: bianma(HC,n); break; case 2: yima(HT,2*n); break; case 0: break; default:printf("输入错误!");break; } } return 0; }

0023算法笔记——【贪心算法】哈夫曼编码问题

0023算法笔记——【贪心算法】哈夫曼编码问题 1、问题描述 哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%~90%之间。哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。一个包含100,000个字符的文件,各字符出现频率不同,如下表所示。 有多种方式表示文件中的信息,若用0,1码表示字符的方法,即每个字符用唯一的一个0,1串表示。若采用定长编码表示,则需要3位表示一个字符,整个文件编码需要300,000位;若采用变长编码表示,给频率高的字符较短的编码;频率低的字符较长的编码,达到整体编码减少的目的,则整个文件编码需要(45×1+13×3+12×3+16×3+9×4+5×4)×1000=224,000位,由此可见,变长码比定长码方案好,总码长减小约25%。 前缀码:对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。这种编码称为前缀码。编码的前缀性质可以使译码方法非常简单;例如001011101可以唯一的分解为0,0,101,1101,因而其译码为aabe。

译码过程需要方便的取出编码的前缀,因此需要表示前缀码的合适的数据结构。为此,可以用二叉树作为前缀码的数据结构:树叶表示给定字符;从树根到树叶的路径当作该字符的前缀码;代码中每一位的0或1分别作为指示某节点到左儿子或右儿子的“路标”。 从上图可以看出,表示最优前缀码的二叉树总是一棵完全二叉树,即树中任意节点都有2个儿子。图a表示定长编码方案不是最优的,其编码的二叉树不是一棵完全二叉树。在一般情况下,若C是编码字符集,表示其最优前缀码的二叉树中恰有|C|个叶子。每个叶子对应于字符集中的一个字符,该二叉树有|C|-1个内部节点。 给定编码字符集C及频率分布f,即C中任一字符c以频率f(c)在数据文件中出现。C的一个前缀码编码方案对应于一棵二叉树T。字符c在树T中的深度记为d T(c)。d T(c)也是字符c的前缀码长。则平均码长定义为:

huffman编码译码实现文件的压缩与解压.

数据结构 课程设计 题目名称:huffman编码与解码实现文件的压缩与解压专业年级: 组长: 小组成员: 指导教师: 二〇一二年十二月二十六日

目录 一、目标任务与问题分析 (2) 1.1目标任务 (2) 1.2问题分析 (2) 二、算法分析 (2) 2.1构造huffman树 (2) 2.1.1 字符的统计 (2) 2.1.2 huffman树节点的设计 (2) 2.2构造huffman编码 (3) 2.2.1 huffman编码的设计 (3) 2.3 压缩文件与解压文件的实现 (3) 三、执行效果 (4) 3.1界面 (4) 3.2每个字符的编码 (4) 3.3操作部分 (5) 3.4文件效果 (6) 四、源程序 (7) 五、参考文献 (16)

huffman编码与解码实现文件的压缩与解压 一、目标任务与问题分析 1.1目标任务 采用huffman编码思想实现文件的压缩和解压功能,可以将任意文件压缩,压缩后也可以解压出来。这样即节约了存储空间,也不会破坏文件的完整性。 1.2问题分析 本问题首先应该是利用哈夫曼思想,对需要压缩的文件中的个字符进行频率统计,为了能对任意的文件进行处理,应该所有的文件以二进制的方式进行处理,即对文件(不管包含的是字母还是汉字)采取一个个的字节处理,然后根据统计的频率结果构造哈夫曼树,然后对每个字符进行哈夫曼编码,然后逐一对被压缩的文件的每个字符构建的新的哈夫曼编码存入新的文件中即得到的压缩文件。解压过程则利用相应的哈夫曼树及压缩文件中的二进制码将编码序列译码,对文件进行解压,得到解压文件。 二、算法分析 2.1构造huffman树 要利用哈夫曼编码对文本文件进行压缩,首先必须知道期字符相应的哈夫曼编码。为了得到文件中字符的频率,一般的做法是扫描整个文本进行统计,编写程序统计文件中各个字符出现的频率。由于一个字符的范围在[0-255]之间,即共256个状态,所以可以直接用256个哈夫曼树节点即数组(后面有节点的定义)空间来存储整个文件的信息,节点中包括对应字符信息,其中包括频率。 2.1.1 字符的统计 用结构体huffchar来存放文件字符的信息。其中有文件中不同字符出现的种类Count、字符data。 struct huffchar{ //存放读入字符的类; int Count;//字符出现的个数; char data;//字符; }; 函数实现: bool char_judge(char c)//判断字符出现的函数; void char_add(char c)//添加新出现的字符; void read_file_count() //文件的读取 2.1.2 huffman树节点的设计 用结构体huff_tree来存储结点信息,其中有成员频率weight、父亲节点parent、左儿子节点lchild、右儿子节点rchild。

哈夫曼树编码

哈夫曼树编码 #include #include #define MAX_NODE 1024 #define MAX_WEIGHT 4096 typedef struct HaffmanTreeNode { char ch, code[15]; int weight; int parent, lchild, rchild; } HTNode, *HaTree; typedef struct { HTNode arr[MAX_NODE]; int total; } HTree; /* 统计字符出现的频率 */ int statistic_char(char *text, HTree *t){ int i, j; int text_len = strlen(text); t->total = 0; for (i=0; itotal; j++) if (t->arr[j].ch == text[i]){ t->arr[j].weight ++; break;

} if (j==t->total) { t->arr[t->total].ch = text[i]; t->arr[t->total].weight = 1; t->total ++; } } printf("char frequence\n"); for (i=0; itotal; i++) printf("'%c' %d\n", t->arr[i].ch, t->arr[i].weight); printf("\n"); return t->total; } int create_htree(HTree *t) { int i; int total_node = t->total * 2 - 1; int min1, min2; /* 权最小的两个结点 */ int min1_i, min2_i; /* 权最小结点对 应的编号 */ int leaves = t->total; for (i=0; iarr[i].parent = -1;

算术编码工作原理.(优选)

算术编码工作原理 在给定符号集和符号概率的情况下,算术编码可以给出接近最优的编码结果。使用算术编码的压缩算法通常先要对输入符号的概率进行估计,然后再编码。这个估计越准,编码结果就越接近最优的结果。 例: 对一个简单的信号源进行观察,得到的统计模型如下: ?60% 的机会出现符号中性 ?20% 的机会出现符号阳性 ?10% 的机会出现符号阴性 ?10% 的机会出现符号数据结束符. (出现这个符号的意思是该信号源'内部中止',在进行数据压缩时这样的情况是很常见的。当第一次也是唯一的一次看到这个符号时,解码器就知道整个信号流都被解码完成了。) 算术编码可以处理的例子不止是这种只有四种符号的情况,更复杂的情况也可以处理,包括高阶的情况。所谓高阶的情况是指当前符号出现的概率受之前出现符号的影响,这时候之前出现的符号,也被称为上下文。比如在英文文档编码的时候,例如,在字母Q 或者q出现之后,字母u出现的概率就大大提高了。这种模型还可以进行自适应的变化,即在某种上下文下出现的概率分布的估计随着每次这种上下文出现时的符号而自适应 更新,从而更加符合实际的概率分布。不管编码器使用怎样的模型,解码器也必须使用同样的模型。 一个简单的例子以下用一个符号串行怎样被编码来作一个例子:假如有一个以A、B、C三个出现机会均等的符号组成的串行。若以简单的分组编码会十分浪费地用2 bits 来表示一个符号:其中一个符号是可以不用传的(下面可以见到符号B正是如此)。为此,这个串行可以三进制的0和2之间的有理数表示,而且每位数表示一个符号。例如,“ABBCAB”这个串行可以变成0.011201(base3)(即0为A, 1为B, 2为C)。用一个定点二进制数字去对这个数编码使之在恢复符号表示时有足够的精度,譬如 0.001011001(base2) –只用了9个bit,比起简单的分组编码少(1 – 9/12)x100% = 25%。这对于长串行是可行的因为有高效的、适当的算法去精确地转换任意进制的数字。 编码过程的每一步,除了最后一步,都是相同的。编码器通常需要考虑下面三种数据: ?下一个要编码的符号 ?当前的区间(在编第一个符号之前,这个区间是[0,1), 但是之后每次编码区间都会变化) ?模型中在这一步可能出现的各个符号的概率分布(像前面提到的一样,高阶或者自适应的模型中,每一步的概率并不必须一样) 编码其将当前的区间分成若干子区间,每个子区间的长度与当前上下文下可能出现的对应符号的概率成正比。当前要编码的符号对应的子区间成为在下一步编码中的初始区间。