霍夫曼编码(Huffman Coding)原理及MATLAB程序实现

霍夫曼编码(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'





 

你可能感兴趣的:(MATLAB)