霍夫曼编码(Huffman Coding)是一种编码方法,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,是可变字长编码(VLC)的一种,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。
霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
霍夫曼编码的具体步骤如下:
先按出现的概率大小排队,把两个最小的概率相加,作为新的概率和剩余的概率重新排队,再把最小的两个概率相加,再重新排队,直到最后变成1。每次相 加时都将“0”和“1”赋与相加的两个概率,读出时由该符号开始一直走到最后的“1”, 将路线上所遇到的“0”和“1”按最低位到最高位的顺序排好,就是该符号的赫夫曼编码。
例如a7从左至右,由U至U″″,其码字为0000;
a6按路线将所遇到的“0”和“1”按最低位到最高位的顺序排好,其码字为0001…
用赫夫曼编码所得的平均比特率为:Σ码长×出现概率
上例为:0.2×2+0.19×2+0.18×3+0.17×3+0.15×3+0.1×4+0.01×4=2.72bit
可以算出本例的信源熵为2.61bit,二者已经是很接近了。
MATLAB代码:
function check( p)
if length(find(p<0))~=0
error('概率不应该小于0!')
end
if abs(sum(p)-1)>10e-10
error('概率之和大于1,请检查输入!')
end
end
function s = first_S( n,r )
%计算第一次霍夫曼编码的压缩量
k=rem(n,r-1);
if k==0
s=r-1;
else if k==1
s=r;
else
s=k;
end
end
end
function Huffman_CLQ(p,r )
%这个是霍夫曼编码的核心代码,分别调用了check,first_S,yasuo等三个函数,具体功能见各个函数
%%
[pp,s]=yasuo_pp(p,r) ; %pp为对p排序后矩阵
c={};%c为最终编码存储的位置
[n,m]=size(pp);
for ii=1:r
c=[c;num2str(ii-1)];
end
%%
for i=2:n-1
k1=pp(n-i+2,:);
k1(find(k1==0))=[];
k2=pp(n-i+1,:);
k2(find(k2==0))=[];
Ln1=length(k1);
Ln2=length(k2);
%
for hh=1:Ln1
if k1(hh)==k2(hh)
continue;
else
hh=hh;%此时hh表示的意思:矩阵pp中第n-i+2行第hh个数字是由n-i+1行中的数字相加而来
break;
end
end
% strcat
%%
%
if hh==1
cc=c(hh+1:length(c));
for j=1:r
bb=strcat ( c(hh) , num2str(j-1) );
cc=[cc; bb ];
end
%%
else %当hh不为1的时候
cc=c(1:hh-1);
cc=[cc;c(hh+1:length(c))];
for j=1:r
bb=strcat ( c(hh) , num2str(j-1) );
cc=[cc; bb ];
end
end
c=cc;
end
%%
%第一次压缩s个,现在进行最后一步编码
k1=k2;
k2=pp(1,:);
for hh=1:length(k1)
if k1(hh)==k2(hh)
continue;
else
hh=hh;%此时hh表示的意思:矩阵pp中第n-i+2行第hh个数字是由n-i+1行中的数字相加而来
break;
end
end
if hh==1
cc=c(hh+1:length(c));
for j=1:s
bb=strcat ( c(hh) , num2str(j-1) );
cc=[cc; bb ];
end
%%
else %当hh不为1的时候
cc=c(1:hh-1);
cc=[cc;c(hh+1:length(c))];
if s==1
s=s+1;%当s=1时需要加1,因为s=1时并未真正压缩,之前已经处理过,因此这里对应要做出变化
end
for j=1:s
bb=strcat ( c(hh) , num2str(j-1) );
cc=[cc; bb ];
end
end
c=cc;
disp('第一行的概率以及对应的第二行霍夫曼编码:')
[sprintfc('%g',pp(1,:));c']
end
function [pp,s1]= yasuo_pp(p,r)
%对p进行压缩排序后得到的矩阵pp
check(p);%对p进行检验;
n=length(p);
p=sort(p,'descend');
p1=[];
%%
%对初始的p长度进行验证,如果小于等于r则直接输出排序后的pp=p
if n<=r
pp=p;
return;
end
%%
s1=first_S(n,r); %s1表示第一次编码时的压缩量
p1=sort([p(1:n-s1),sum(p(n-s1+1:n))],'descend');%第一次压缩后的顺序
%%
%令s1不为1使得p矩阵不会出现两行相同的情况
if s1~=1
p11=[p1,zeros(1,s1-1)];
p=[p;p11];
end
nn=length(p1);
while(nn>(r))
p1=sort([p1(1:nn-r),sum(p1(nn-r+1:nn))],'descend');
nn=length(p1);
p=[p;[p1,zeros(1,n-nn)]];
% nn
% p1
% p
end
pp=p;
end
%主程序
clc;
clear;
%%
%第一组测试
p=[0.16 0.14 0.13 0.12 0.10 0.09 0.08 0.07 0.06 0.05];
r=2;%r代表r元码
%第二组测试
% p=[0.24,0.2,0.18,0.13,0.1,0.06,0.05,0.03,0.01];
% r=4;
Huffman_CLQ(p,r)
第一行的概率以及对应的第二行霍夫曼编码:
ans =
'0.16' '0.14' '0.13' '0.12' '0.1' '0.09' '0.08' '0.07' '0.06' '0.05'
'000' '010' '011' '100' '110' '111' '0010' '0011' '1010' '1011'