1.霍夫曼编码简介
霍夫曼编码是一种可变长编码( VLC, variable length coding))方式,比起定长编码的 ASCII 编码来说,哈夫曼编码能节省很多的空间,因为每一个字符出现的频率不是一致的;
是一种用于无损数据压缩的熵编码算法,通常用于压缩重复率比较高的字符数据。
如果我们通过转换成ASCII码对应的二进制数据将字符串 BCAADDDCCACACAC 通过二进制编码进行传输,那么一个字符传输的二进制位数为 8bits,那么总共需要 120 个二进制位;而如果使用霍夫曼编码,该串字符可压缩至 28位。
2. 编码方式:
1.初始字符串:
2.计算字符频率:
3. 按照字符出现的频率进行排序,组成一个队列 Q
出现频率最低的在前面,出现频率高的在后面。
3. 把这些字符作为叶子节点开始构建一颗哈夫曼树
霍夫曼树又称为最优二叉树,是一种带权路径长度最短的二叉树。所谓霍夫曼编码可以看为倒着的霍夫曼树。
(1)首先创建一个空节点 z,将最小频率的字符分配给 z 的左侧,并将频率排在第二位的分配给 z 的右侧,然后将 z 赋值为两个字符频率的和;然后从队列 Q 中删除 B 和 D,并将它们的和添加到队列中,上图中 * 表示的位置。
(2)紧接着,重新创建一个空的节点 z,并将 4 作为左侧的节点,频率为 5 的 A 作为右侧的节点,4 与 5 的和作为父节点,并把这个和按序加入到队列中,再根据频率从小到大构建树结构(小的在左)。
(3)继续按照之前的思路构建树,直到所有的字符都出现在树的节点中,哈弗曼树构建完成。
节点的带权路径长度为从根节点到该节点的路径长度与节点权值的乘积。
该二叉树的带权路径长度 WPL = 6 * 1 + 5 * 2 + 3 * 3 + 1 * 3 = 28。
4. 对字符进行编码:
哈夫曼树构建完成,下面我们要对各个字母进行编码,编码原则是:对于每个非叶子节点,将 0 分配给连接线的左侧,1 分配给连接线的右侧,最终得到字符的编码就是从根节点开始,到该节点的路径上的 0 1 序列组合。
因此各个字母的编码分别为:
A | B | C | D |
---|---|---|---|
11 | 100 | 0 | 101 |
在没有经过哈夫曼编码之前,字符串“BCAADDDCCACACAC”的二进制为:
10000100100001101000001010000010100010001000100010001000100001101000011010000010100001101000001010000110100000101000011
也就是占了 120 比特;
编码之后为:
1000111110110110100110110110
占了 28 比特。
3. 霍夫曼(Huffman)文本读取编码
1.文本部分输入
clc
clear all
% 读取文本文件
filename = 'input.txt';
text = fileread(filename);
inData = text; %输入文本
disp(inData)
2.创建霍夫曼树、
% 计算字符出现次数
char_count = cell(1, length(text));
disp(length(text))
for i = 1:length(text)
char_count{i} = numel(regexp(text, char(i)));
end
%%创建哈夫曼树
%对字符出现的概率按照从低到高排序
p = uniqueCharacter_p;
[a,b]=sort(p); %对p概率矩阵进行排列
m(1,:) = b;
for i = 1:length(a)
c(i) = uniqueCharacter(b(i));%更新字符队列的排序
end
q = sort(p); %更新概率顺序
n = length(uniqueCharacter_p);
for i = 2:n
matrix(i-1,2) = c(1); %在matrix中记录左孩子
matrix(i-1,3) = c(2); %在matrux中记录右孩子
matrix(i-1,1) = double(i-1); %在matrix中记录根节点
c(2) = double(i-1);%此处补充数值1,目的是为了以后排序该位置总排在最后,不被处理
q(2) = q(1) + q(2); %更新根节点数值
q(1) = 1;
%对新的概率组合进行排序
[a,b]=sort(q);
[a1,b1] = sort(b);
m(i,:)=b1; %%进行两次sort排记录记录概率对应的位置
temp_c = c; %引入中间变量
temp_q = q;
for i = 1:length(a1)
c(i) = temp_c(b(i));%更新字符队列的排序
q(i) = temp_q(b(i));
end
end
3. 实现编码及文件输出部分
% 指定要保存编码结果的文件名
file_name = 'huffman.txt';
file_name1 = 'code.dat';
%读哈夫曼编码
disp('哈夫曼编码表:')
code = uniqueCharacter';%这里的单引号表示这是一个字符串,而不是一个单独的字符。
fileID = fopen(file_name, 'w');
fileID1 = fopen(file_name1, 'w');
disp(n)
for i = 1:n
[temp_code,n] = Coding(matrix,uniqueCharacter(i));
code(i,3:n+2) = temp_code;
len(i) = n;
%存储到Huffman.txt
fprintf(fileID, '字符为:');
fprintf(fileID, '%s',uniqueCharacter(i));
fprintf(fileID, ' ');
fprintf(fileID, '编码为:');
fprintf(fileID, '%s',code(i,3:n+2));
fprintf(fileID, ' ');
fprintf(fileID, '字符出现次数为:');
fprintf(fileID, '%d\n',uniqueCharacter_num(i));
end
%存储到code.dat
for i=1:length(text)
for j=1:n
if text(i) ==uniqueCharacter(j)
fprintf(fileID1,code(j,3:n+2),'ubit1');
end
end
end
disp(uniqueCharacter_num)%输出字符数量
disp(code)%输出字符编码
fclose(fileID);
fclose(fileID1);
%编码函数
function [code,n] = Coding(matrix,character)
[a,b] = size(matrix);
for i = 1:a
[row,col] = find(matrix(:,2:3)==character);
character = matrix(row,1);
if col == 1
temp_code(i) = '0';
else
temp_code(i) = '1';
end
code(i) = temp_code(i);
if row == a
break
end
end
%此刻需要将编码结果倒转
temp_code = code;
n = length(code);
for i = 1:n
code(i) = temp_code(length(code)-i+1) ;
end
end
4.编码效率部分
%编码压缩效率
disp('编码压缩效率:')
e = (sum(uniqueCharacter_num)*8)/sum(len.*uniqueCharacter_num)
5.实现解码部分
%解哈夫曼编码
% 读取code.dat文件
filename = 'code.dat';
fid = fopen(filename, 'r');
if fid == -1
error('无法打开文件');
end
% 逐行读取文件内容
codeData = '';
tline = fgetl(fid);
while ischar(tline)
codeData = [codeData tline];
tline = fgetl(fid);
end
% 关闭文件
fclose(fid);
% 打印读取到的内容
disp(codeData);
code = double(code);
decodedText = huffmandeco(codeData, code(:, 3:n+2));
disp("解码结果为:")
disp(decodedText)
%霍夫曼解码
function [decoded_string] = Decoding(matrix, code)
a = size(matrix, 1);
decoded_string = '';
% 将编码结果倒转
reversed_code = fliplr(code);
disp(length(reversed_code))
for i = 1:length(reversed_code)
current_code = reversed_code(i);
if current_code == '0'
index = find(matrix(:, 1) == matrix(i, 2));
else
index = find(matrix(:, 1) == matrix(i, 3));
end
decoded_string = strcat(decoded_string, matrix(index, 1));
if index == a
break
end
end
end
6. 测试
先使用小文本测试
input.txt:
输出结果:
文件存储为:
Huffman.txt:
大文件测试:
input.txt:
Huffman.txt:
code.dat:
霍夫曼原理部分借鉴了大佬的博客: