python实现信息论哈夫曼编码_哈夫曼压缩原理及python3实现(非面向对象结构)

1 哈夫曼编码综述

在计算机科学和信息论,哈夫曼编码是一种特殊类型的最优前缀码(prefix code),通常用于无损数据压缩(英文文本,更一般地说 ASCII 码位于 0-255 位的文本)。哈夫曼编码是一种变长编码,相比使用定长的 ASCII 码,哈夫曼编码可以节省很多的空间 (试想如果一篇文章中全为同一种字符,对应的哈夫曼编码为 "0" ,那么原先表达 1 个字符的 1 字节就能用来表示 8 个字符)。

哈夫曼压缩对频数最高的字符赋予较短的编码,实现压缩效率最大化

哈夫曼编码以二叉树为基础实现的,二叉树到每一个叶子节点的路径是唯一的,对应的编码也就唯一。

哈夫曼编码的前缀码唯一,从图 1 中可以看出,各编码的前缀码都是唯一的,这就保证了在字符表确定下,不断搜索一定可以定位到对应的字符。图 1 示例文本长度为 17,占用空间17字节。使用哈夫曼编码进行转码,对应的转码文本为:10110110001000111111111001000000000 ,长度为 35,按照 8 位一字节进行切割后,正文文本就变成了 5 字节 (不足 8 位需要补 0)

对转码文本进行还原时,读取 1 位字符,对其后的字符搜索。如读取 "1" 后接着读取 "0" 变成了 "10" ,编码表中没有 "10",则继续向后读取 "101",此时码表中有 "101"项,对文本进行还原得到 "a",继续向后搜索,直到匹配完全文。图 1 哈夫曼树及编码示例

2 哈夫曼压缩及解压基本原理

哈夫曼压缩基本思想是将二进制代码分配给字符,用于减少编码这些符号的字符串比特数(如上图 1 中,原先表达一个字符 "e",需要1字节数据 "01100101",而在哈夫曼编码中,只需要1位数据 "0" 即可表达)。构造过程可以参考图 1 ,简书中较好的资料详细介绍了算法原理(但是代码运行不了,报错...)。

算法的主要过程分为:构造哈夫曼编码表、转码压缩、转码解压。

压缩与解压中,数据根据哈夫曼编码表进行转换,因此写入编码表信息是必须的。此外,字节位数不足 8 位时,需要补充 "0" 达到 8 位,而补 "0" 的情况又对解码信息有影响,还需要在文件中声明补 "0"情况。每一步的大致过程如下所示(在本文第 4 部分结合代码有更详细的示例):构造哈夫曼编码表

Step1 遍历需要处理的字符串,得到每个字符出现的次数(频数)

Step2 将频数最低的两位字符作为叶子节点,左子树频数大于右子树,构造分支节点,同时将分支节点作为新的字符,频数即为子叶频数之和,进行重新排序(如图 1 中,将 "bd" 作为新字符,进行重新排序)

Step3 重复 Step1 和 Step2,直到所有字符编码完成

Step4 从树的顶端开始编码,左子树编为 0,右子树编为 1,直到树的底端

Step5 根据编码情况构造哈夫曼编码表转码压缩

Step1 根据哈夫曼编码表将正文转换为 0-1 编码

Step2 每 8 位编码进行一次切割,作为 1 个字节数据写入压缩文件

Step3 文末不足 8 位则需要补 "0",直到刚好达到 8 位编码

Step4 解码时避免补 "0" 干扰,需要将补 0 情况写入压缩文件中

Step5 将码表写入压缩文件转码解压

Step1 根据补 0 情况,删减文本

Step2 读取哈夫曼编码表,作为转码对照表

Step3 读取正文文本,与转码对照表进行比对,还原信息

3 分析及扩展应用

3.1 为什么哈夫曼编码是最优的?

参考:维基百科香农源编码定理https://en.wikipedia.org/wiki/Shannon%27s_source_coding_theorem​en.wikipedia.org

(其实也很像运筹学课本上提到的最优带权连接图,但是证明实在是太长了。。。)

也可以这样考虑:对频数最多的字符赋予最少的字符长度,依次类推。

3.2 其他压缩算法的"后端"

哈夫曼压缩作为数据流压缩的“先驱者”,由于哈夫曼编码简单、高速且无数据损失,常常被用于其他压缩算法的“后端”。如DEFLATE和多媒体编码器(如图像的JPEG,音频的MP3)都有自己的压缩算法,但都应用到了这种前缀码的思想。尽管大多数无损压缩算法都使用预定义的可变长度(如 LZW 算法)而不是使用哈夫曼算法,但这写算法也通常被称为 "Huffman codes"。

3.3 层次聚类算法聚类树构造

哈夫曼树构造过程与层次聚类算法思想也极其相似,将权值改为“样本距离”,并重新定义节点权值更新函数,即可得到层次聚类的聚类树构造。下图中展示了笔者使用哈夫曼树实现的层次聚类,并进行可视化的效果。图 2 经典层次聚类(Hierarchical Clustering)算法图 3 使用哈夫曼树构造的聚类树(试验数据为17年国赛建模B题数据,使用欧式距离度量权值)

3.4 数据加密

哈夫曼压缩中,最重要的哈夫曼编码表是能否解读数据关键。在压缩的时候,将码表与文本分离(或者打乱码表的表头、表文顺序,自定义一定规则进行匹配),可以实现数据加密。同时,若将码表的表头与表文、表文长度进行分别处理,则可以实现多端口数据加密验证。

3.5 流式数据压缩

哈夫曼编码可以认为是基于统计的压缩算法,统计过程是算法的核心,而流式数据随着文本不断扩展,权值可能发生变化,此时使用哈夫曼压缩不一定能取得很好的压缩效果,但在特定场景下,通过预先设定字符编码,也能取得较好的情况。如据统计,在英文小说中,文本使用 "e"、"t"、"a" 等字符的频数较高,则可以对这些字符进行预先编码,再根据各文本差异进行后续编码扩充。当然,其他编码算法如 LZW 编码、RLE 算法也给出了更好的实现方案,这些算法压缩效果好、速度快,但是性能不够好。

3.6 压缩汉字构思

汉字在 GB2312 编码存储占 2 字节,而英文文本、数字占 1 字节,因此可以考虑在 GB2312 编码下构造汉字映射表,实现汉字压缩。(只是构思,写不写的出来就不知道咯~)

4 python3逐步实现哈夫曼压缩(附注 2 提供测试数据及完整源码)

4.1 说明

学习哈夫曼压缩过程中参考了许多资料,其中不少简书、CSDN 博文都给出了很漂亮的代码示例,但是也存在一些问题,如 python2 代码、结构混乱、使用类思想不便于新手理解(python的编程风格不同于JAVA)、没有给出压缩解压细节等。因此,本文以算法思想为蓝本逐步用代码进行实现,并在本文对每个部分的功能进行了详细说明。部分代码语句可能较为啰嗦,结构也不完美,但是可读性较强,便于理解。(完整代码见附注 2)

使用的编程语言:python3.6.4 (Anaconda3)

使用的编辑器:pycharm

使用的模块:os、six、tkinteros 模块:使用了 os 模块的 path.splitext 函数,用于分割文件名与扩展名。如"test.txt",分割为 ('test', '.txt'),实现重写扩展名功能

six 模块:

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值