1、题目
英文压缩
根据英文单词统计频率(概率,见图,也可自行网络下载),进行Huffman编码,图中状态27表示“空格,逗号,句号”的总概率。编程实现压缩下面两种文字:
- 一句诗词:deep into that darkness peering, long I stood there, wondering, fearing,
- 一本小说:爱丽丝奇遇记(见附件.mat文件)。
求出两种情况的理论压缩率A(定长编码比特数/Huffman理论平均编码比特数)和实际压缩率B(定长编码比特数/Huffman理论实际输出比特数),分析两者为什么不同(为什么A>B或者A<B)?
![](https://i-blog.csdnimg.cn/blog_migrate/eb2ed3eb7c611600bf41bb89504294ac.png)
2、题目分析
本次仿真需要哈夫曼编码的方法分别对一句诗词和一本小说进行压缩,计算理论压缩率和实际压缩率,并成功将压缩后的数据进行解压缩。
诗词的压缩和解压仿真分以下7个步骤:
- 读取文本:用char()将str转为char,用double()将char转为double,因为histcounts()输入不支持char或str类型;
- 整理字符种类:用unique()函数统计不同字符的个数,用以计算定长编码长度;
- 计算字符出现概率:用histcounts()函数统计不同字符出现频率;
- 制作哈夫曼码表:用huffmandict()函数构建哈夫曼码表,并输出实际平均码长;
- 哈夫曼编码:用huffmanenco()函数对数据进行编码;
- 计算压缩率:根据公式计算理论压缩率(定长编码比特数/Huffman理论平均编码比特数)和实际压缩率(定长编码比特数/Huffman理论实际输出比特数),由题目所给字符概率表计算的平均码长(即Huffman理论平均编码比特数)为4.1,如图1;
- 解压缩:用huffmandeco()函数对压缩的数据进行解压缩,将double转回char类型,再转为str即成功恢复原数据。
小说的仿真步骤基本一样,只不过所读入数据是多行的str,需要在步骤(1)先统计一下每行str长度,构建行尾索引(为了解压缩时能正确恢复每行str),再用strjoin()函数将所有str拼接成一个str;在步骤(7)按照索引将解压后的一行str正确分割成多行str即可成功解压缩。
3、仿真结果
(a)理论压缩率、实际压缩率和解压缩结果 (b)实际编码表 (c)小说的全部符号类型
图 2 诗词压缩与解压缩
图 3小说压缩与解压缩
4、结果分析
由图2(a)可以看出,诗词的实际压缩率要比理论压缩率大,最后成功解压缩;从图3(a)可以看到小说的实际压缩率比理论压缩率小,最后也成功解压缩。
小说的理论压缩率要比实际压缩率大,是因为理论压缩率是在假设数据中每个符号出现的概率都已知、且只使用最优的编码方式的情况下计算得出的,如图1所示。而在小说的实际数据中,各种符号出现的频率可能会有所不同,某些字符可能没有出现在图1的表中(如图3(b)所示),导致实际的数据更复杂,编码长度更长(如图3(c)所示),从而实际压缩率要比理论压缩率小。
诗词的实际压缩率比理论压缩率大,是因为输入的诗词字符种类远小于固定码表的字符种类(如图2(c)所示),所以原有固定码表对于哈夫曼编码方式来说便不是最优的码表,其码表平均码长会比实际平均码长要长出许多(如图2(b)所示),故实际压缩率会比由固定的哈夫曼码表计算的理论压缩率要大。
综上所述,对于哈夫曼编码,不同的字符出现概率分布,不同的编码表生成方式,都会影响实际压缩率的结果,所以要根据实际情况调整哈夫曼码表,以达到尽量高的压缩率。
5、Matlab代码
%% 输入诗词或小说,用哈夫曼编码方法进行编码,比较理论压缩率和实际压缩率的不同
%% 用isequal函数判断压缩之后是否能成功恢复原始数据,1表示成功,0表示失败
%% 题目所给英文字符统计概率表的理论平均码长=4.1
clear;
sp = 1 ; %输入1为诗词,2为小说
if sp==1
%% 诗词
t = "deep into that darkness peering, long I stood there, wondering, fearing,";
else
%% 小说
load('AliceInWonderland.mat');
t = textData;
index=zeros(size(t));%记录小说每一节点长度
for i=1:length(t)
index(i)=length(char(t(i)));
end
index=cumsum(index);%累计和
end
%% 统计字符概率
str=double(char(strjoin(t(:),'')));%将所有字符串拼接成一个字符串,转为char类型,再转为double型
edges = [unique(str),max(str)+1];%构建计算字符出现次数的区间
freq_num = histcounts(str, edges);%计算每种字符出现的次数
freq = freq_num / sum(freq_num);%计算每种字符出现的概率
%% 构建哈夫曼码表
[dict,avglen] = huffmandict(unique(str), freq);%生成二进制霍夫曼码,输出包含码字字典的单元数组和平均码长
%% 对句子进行编码并计算编码后文件大小
code = huffmanenco(str, dict);%哈夫曼编码
%% 计算理论压缩率(定长编码比特数/Huffman理论平均编码比特数)
theoryCompRate = ceil(log2(length(freq)))/4.1
%% 计算实际压缩率(定长编码比特数/Huffman理论实际输出比特数)
realCompRate = ceil(log2(length(freq)))/avglen
%% 解压缩
sig = huffmandeco(code,dict);%哈夫曼解码
str_re=char(sig);%double转char
if sp==1
str_re = string(str_re);
isequal(t,str_re)%判断诗词解压后和原数据是否相等
else
%% 小说解压缩
index_l=length(index);
index=[0;index];%在索引第一位加0,表示开始
str_novel_re=repmat("",index_l,1);%构建空的字符串矩阵
%将一行字符串按照拼接时的索引重新截断
for i=1:index_l
str_novel_re(i)=str_re(index(i)+1:index(i+1));
end
isequal(t,str_novel_re)%判断小说解压后和原数据是否相等
end
小说文档的百度网盘链接:https://pan.baidu.com/s/1bKBS5EwEYmnXPVzwvj1iaA?pwd=mt7r
提取码:mt7r