实验目的
利用香农-费诺编码、霍夫曼编码、LZ编码、算术编码实现数据压缩
实验原理
1、香农-费诺编码
首先,将信源符号以概率递减的次序排列进来,将排列好的信源符号划分为两大组,使两组的概率和近于相同,并各赋予一个二元码符号“0”和“1”。然后将每一大组的信源符号再分成两组,使同一组的两个小组的概率和近于相同,并又分别赋予一个二元码符号。依次下去,直至每一个小组只剩下一个信源符号为止。这样,信源符号所对应的码符号序列则为编得的码字。译码原理,按照编码的二叉树从树根开始,按译码序列进行逐个的向其叶子结点走,直到找到相应的信源符号为止。之后再把指示标记回调到树根,按照同样的方式进行下一序列的译码到序列结束。如果整个译码序列能够完整的译出则返回成功,否则则返回译码失败。2、霍夫曼编码
霍夫曼编码属于码词长度可变的编码类,是霍夫曼在1952年提出的一种编码方法,即从下到上的编码方法。同其他码词长度可变的编码一样,可区别的不同码词的生成是基于不同符号出现的不同概率。生成霍夫曼编码算法基于一种称为“编码树”(coding tree)的技术。算法步骤如下:
(1)初始化,根据符号概率的大小按由大到小顺序对符号进行排序;
(2)把概率最小的两个符号组成一个新符号(节点),即新符号的概率等 于这两个符号概率之和;
(3)重复第2步,直到形成一个符号为止(树),其概率最后等于1;
(4)从编码树的根开始回溯到原始的符号,并将每一下分枝赋值为1,上 分枝赋值为0。3、LZ编码
在LZ算法中,离散信源的输出序列分解成长度可变的分组,码段(phrases)。每当信源输出字符组在最后位置加上一个字符后,与前面的一有已有码段都不相同时,把它作为一种新的码段引入。将这些码段列入一个位置词典,用来记载有码段的位置。在对一个新的码段编码时只要指出字典中现有码段的位置,把新的字符附在后面即可。4、算术编码
算术编码的编码对象是一则消息或一个字符序列,其编码思路是将消息或字符序列表示成0和1之间的一个间隔上的一个浮点小数。 在进行算术编码之前,需要对字符序列中每个字符的出现概率进行统计,根据各字符出现概率的大小,将每个字符映射到[0 ,1]区间上的某个子区间中。然后,在利用递归算法,将整个字符序列映射到[0,1]区间上的某个间隔中。在进行编码时,只需从该间隔中任选一个小数,将其转化为二进制数。 符号序列越长,编码表示他的间隔就越小,表示这个间隔所需的二进制位数就越多,编码输出的码字就越长。
在进行编码过程中,随着信息的不断出现,子区间按下列规律减小:
a.新子区间左端=前子区间左端+当前子区间左端×前子区间长度
b.新子区间长度=前子区间长度×当前子区间长度实验内容
1、对于给定的信源的概率分布,用香农-费诺编码实现压缩
2、对于给定的信源的概率分布,用霍夫曼编码实现压缩
3、对于给定的信源的概率分布,用LZ编码实现压缩
4、对于给定的信源的概率分布,用算术编码实现压缩实验过程
1、香农-费诺编码
function c=shannon(p)
[p,index]=sort(p)
p=fliplr(p)
n=length(p)
pa=0
for i=2:n
pa(i)= pa(i-1)+p(i-1)
end
k=ceil(-log2(p))
c=cell(1,n)
for i=1:n
c{i}=''
tmp=pa(i)
for j=1:k(i)
tmp=tmp*2
if tmp>=1
tmp=tmp-1
c{i}(j)='1'
else
c{i}(j)='0'
end
end
end
c = fliplr(c)
c(index)=c
2、霍夫曼编码
function c=huffman(p)
n=size(p,2)
if n==1
c=cell(1,1)
c{1}=''
return
end
[p1,i1]=min(p)
index=[(1:i1-1),(i1+1:n)]
p=p(index)
n=n-1
[p2,i2]=min(p)
index2=[(1:i2-1),(i2+1:n)]
p=p(index2);
i2=index(i2)
index=index(index2)
p(n)=p1+p2
c=huffman(p)
c{n+1}=strcat(c{n},'1')
c{n}=strcat(c{n},'0')
index=[index,i1,i2]
c(index)=c
3、LZ编码
function LZmain()
clc;
fid=fopen('source.txt','r');
seq=fread(fid);
fclose(fid);
seq=reshape(seq,1,length(seq));
if ~isempty(seq)
[entropy]=Entropy(seq);
[dictionary codelength]=LZcode(seq);
avglength=((codelength*length(dictionary))/length(seq));
disp(strcat('Entropy = ',num2str(entropy)));
disp(strcat('Code length = ',num2str(codelength)));
disp(strcat('Average length = ',num2str(avglength)));
display('Encoded Sequence : Look encode.txt.');
enseq=LZencode(dictionary);
fiden=fopen('encode.txt','w');
en=fwrite(fiden,enseq);
fclose(fiden);
display('Decoded Sequence : Look decode.txt.');
deseq=LZdecode(dictionary);
fidde=fopen('decode.txt','w');
de=fwrite(fidde,deseq);
fclose(fiden);
else
display('Empty Sequence....');
end
end
function [dictionary codelength]=LZcode(seq)
l=length(seq);
dictionary(1).sym=seq(1);
k=2;
index=0;
str='';
for i=2:l
str=[str seq(i)];
for j=1:(k-1)
index=0;
if strcmp(dictionary(j).sym,str)
index=1;
break;
end
end
if (index==0)
dictionary(k).sym=str;
k=k+1;
str='';
end
end
codelength=fix(log2(k-1))+1;
for i=1:(k-1)
dictionary(i).code=dec2bin((i-1),codelength);
end
end
function decode=LZdecode(dictionary)
ld=length(dictionary);
decode='';
for i=1:ld
decode=[decode dictionary(i).sym];
end
end
function encode=LZencode(dictionary)
ld=length(dictionary);
encode='';
for i=1:ld
encode=[encode dictionary(i).code];
end
end
function [entropy]=Entropy(seq)
alpha(1)=seq(1);
prob(1)=1;
l=length(seq);
k=2;
for i=2:l
idx=find(alpha==seq(i));
if isempty(idx)
alpha(k)=seq(i);
prob(k)=1;
k=k+1;
else
prob(idx)=prob(idx)+1;
end
end
prob=prob./l;
entropy=-prob.*log2(prob);
entropy=sum(entropy(:));
end
4、算术编码
function arithmetic
S = input('请输入信源符号=');
P = input('请输入信源概率向量P=');
str = input('输入编码的字符串=');
l = 0;
r = 1;
d = 1;
n = length(str);
n_S = length(P);
%**********处理第一个字符***********%
for i=1:n
flag = 0;
for k = 1:n_S
if str(i)==S(k)
m=k;
flag =1;
break;
end
end
if flag ==0
error('非信源字符');
end
%*********当前单个字符的左、右端以及长度处理**************%
pl = 0;
pr = 0;
for j = 1:m-1
pl = pl + P(j); %左端
end
pr = pl+P(m); %右端
pd = pr-pl; %子区间长度
%*********新子区间的左、右边界以及长度处理**************%
if i == 1 %首字符
l = pl;
r = pr;
d = pd;
else %算术编码规则
l = l+d*pl;
d = d*pd;
r = l+d;
end
strl = strcat('第',int2str(i),'个符号的间隔左右边界:');
disp(strl);
format long;
disp(l);disp(r);
end
strl = strcat('符号的间隔左右边界:');
disp(strl);
format long;
disp(l);disp(r);
end
- 实验结果
>> clear; clc;
>> P=[0.3 0.25 0.16 0.14 0.1 0.05];
>> c1=shannon(P);
c1 = '00' '01' '100' '101' '1101' '11110'
>> c2=huffman(P);
c2= '11' '01' '00' '100' '1011' '1010'
>> clear; clc;
>> arithmetic
请输入信源符号='abcdef'
请输入信源概率向量P=[0.3 0.25 0.16 0.14 0.1 0.05]
输入编码的字符串='adbecf'
第1个符号的间隔左右边界:
0
0.300000000000000
第2个符号的间隔左右边界:
0.213000000000000
0.255000000000000
第3个符号的间隔左右边界:
0.225600000000000
0.236100000000000
第4个符号的间隔左右边界:
0.234525000000000
0.235575000000000
第5个符号的间隔左右边界:
0.235102500000000
0.235270500000000
第6个符号的间隔左右边界:
0.235262100000000
0.235270500000000
符号的间隔左右边界:
0.235262100000000
0.235270500000000
>> clear; clc;
>> LZmain
Entropy =5.8172
Code length =8
Average length =4.6126
Encoded Sequence : Look encode.txt.
Decoded Sequence : Look decode.txt.