实验2编码相关实验实验报告.docx
《实验2编码相关实验实验报告.docx》由会员分享,可在线阅读,更多相关《实验2编码相关实验实验报告.docx(28页珍藏版)》请在冰豆网上搜索。
实验2编码相关实验实验报告
信息论与编码实验二
实验报告
学生姓名周群创
指导教师张祖平
学号0909110814
专业班级电子信息1101
一、实验目的
1.掌握香农码和Huffman编码原理和过程。
2.熟悉matlab软件的基本操作,练习使用matlab实现香农码和Huffman
编码。
3.熟悉C/C++语言,练习使用C/C++实现香农码和Huffman编码。
4.应用Huffman编码实现文件的压缩和解压缩。
二、实验原理
香农编码:
香农第一定理指出了平均码长与信源之间的关系,同时也指出了可以通过编码使平均码长达到极限值,这是一个很重要的极限定理。
如何构造这种码?
香农第一定理指出,选择每个码字的长度Ki满足下式
I(xi)≤K﹤I(xi)+1,
就可以得到这种码。
这种编码方法就是香农编码。
香农第一定理:
设离散无记忆信源为
SP=s1s2.............sqp(s1)p(s2).....p(sq)
熵为H(S),其N次扩展信源为
熵为H(SN)。
码符号集X=(x1,x2,…,xr)。
先对信源SN进行编码,总可以
找到一种编码方法,构成惟一可以码,使S中每个信源符号所需的平均码长满足:
当N→∞时
L是平均码长
λ是ai对应的码字长度
哈夫曼编码:
要完成哈夫曼的编码和解码需要首先建立哈夫曼树,之后对所有字符根据权重进行编码,最后再对文件内容进行编码和解码。
首先定义适合哈夫曼树的节点类型,需要定义的有当前节点的字符,当前节点的左子、右子和父亲指针。
在建立哈夫曼树之前还需要对出现的字符和权重进行统计和记录,并且定义一个可以筛选出最小权重的函数。
初始化树节点之后开始建立哈夫曼树。
先在所有可能出现的字符中筛选出当前权重最小的两个字符,将这两个字符分别作为新节点的左子和右子建立一个小的二叉树,并将两个字符的权重之和赋值给新节点,将新二叉树放入筛选字符中,再将筛选过的两个字符从筛选列表中淘汰掉。
依次对列表中剩下的字符进行权重最小的筛选,直到根节点(如果编码表共有N个字符,则2*N-1就为最终根节点)为止,也就是当筛选列表为空的时候,哈夫曼树即建立完成。
对于哈夫曼编码树来说,由于哈夫曼编码是前缀码,所以所有要编码的字符最终都将是这颗树的叶子节点,而其它节点并没有真正的字符意义。
即当哈夫曼编码树建立之后,对树的所有叶子节点进行打印可知道是否有字符遗漏或多余。
建立哈夫曼编码表。
建立编码表时要根据每个出现的字符的权重对建立的哈夫曼树的每个叶子节点进行编码。
编码时要从叶子节点出发向根节点进行逆向编码。
判断如果当前节点为左子则对其编码‘0’,如果当前节点为右子则对其编码‘1’。
以此类推进行编码直到根节点为止。
此时的编码是逆向的,所以需要将码值逆向存储。
依次对每一个叶子节点进行编码操作,即可得到当前哈夫曼树的编码表。
对于码值的逆向存储可以使用栈结构,先将一个码的每一步编码存入栈,再在一个码结束后出栈至空。
当然也可以定义一个字符型数组,将值从后向前存入数组,再将数组有值部分粘贴到新的数组中进行存储。
本次采用了后者,因为个人认为为此一步操作建立栈结构不划算,而且前一个设计也已经熟练掌握了栈的方法,此处进行新的尝试会更好。
对文件进行编码。
首先需要建立一个原始文件,在文件中输入需要编码的内容。
之后将文件打开,将其中的内容存储到字符串中以便程序编码调用。
开始对需要编码的字符进行编码,将字符逐一读取与刚刚建立的编码表中的每个叶子节点代表的字符进行比较,找出相同的对象,并将当前节点的编码打印到屏幕,并将编码存入到新建的密码文件当中。
对文件进行解码。
先打开密码文件,将之前编码后得到的密文内容存储到字符串中以便解码调用。
开始对密文的字符串进行解码,树索引从根节点开始走,当密文中的当前字符是‘0’的时候,则索引走向左子节点;当是‘1’的时候,则走向右子节点。
以此类推,一直走到叶子节点为止,则当前叶子节点所代表的字符即为前一段密文的解码结果,。
再对下一个字符依次从根节点开始解码,如此循环对每一段密文进行解码直到解码结束。
将解码打印到屏幕,并将解码结果存入到新的解码文件当中。
在解码之前,还应该先确认之前是否建立了哈夫曼树并且是否构建了编码表。
不过由于本次将‘a’到‘z’都进行了编码,所以此步省略了,因为编码表是唯一的。
需要的时候可以在Encoder函数中先进行判定。
将编码和解码写在了一起,可以在运行时进行选择调用。
三、实验内容
1.使用matlab实现香农码和Huffman编码,并自己设计测试案例。
2.使用C\C++实现香农码和Huffman编码,并自己设计测试案例。
3.可以用任何开发工具和开发语言,尝试实现Huffman编码应用在数据文
件的压缩和解压缩中,并自己设计测试案例。
四、实验要求
1.提前预习实验,认真阅读实验原理以及相应的参考书。
2.认真高效的完成实验,实验中服从实验室管理人员以及实验指导老师的
管理。
3.认真撰写实验报告,内容可以自己编排,可以考虑包括以下一些方面:
原理概述、程序设计与算法描述、源程序及注释(程序太长可以只选取
重要部分)、运行输出结果实例、调试和运行程序过程中产生的问题及
采取的措施、对实验的讨论分析、总结。
五、调试结果
香农编码:
哈夫曼编码:
C语言之香农编码:
C语言之哈夫曼编码:
六、代码
Matlab实现
香农编码:
clear;
A=input('pleaseinputanumber:
')%提示输入界面
A=fliplr(sort(A));%降序排列
[m,n]=size(A);
fori=1:
n
B(i,1)=A(i);%生成B的第1列
end
%生成B第2列的元素
a=sum(B(:
1))/2;
fork=1:
n-1
ifabs(sum(B(1:
k,1))-a)<=abs(sum(B(1:
k+1,1))-a)
break;
end
end
fori=1:
n%生成B第2列的元素
ifi<=k
B(i,2)=0;
else
B(i,2)=1;
end
end
%生成第一次编码的结果
END=B(:
2)';
END=sym(END);
%生成第3列及以后几列的各元素
j=3;
while(j~=0)
p=1;
while(p<=n)
x=B(p,j-1);
forq=p:
n
ifx==-1
break;
else
ifB(q,j-1)==x
y=1;
continue;
else
y=0;
break;
end
end
end
ify==1
q=q+1;
end
ifq==p|q-p==1
B(p,j)=-1;
else
ifq-p==2
B(p,j)=0;
END(p)=[char(END(p)),'0'];
B(q-1,j)=1;
END(q-1)=[char(END(q-1)),'1'];
else
a=sum(B(p:
q-1,1))/2;
fork=p:
q-2
ifabs(sum(B(p:
k,1))-a)<=abs(sum(B(p:
k+1,1))-a);
break;
end
end
fori=p:
q-1
ifi<=k
B(i,j)=0;
END(i)=[char(END(i)),'0'];
else
B(i,j)=1;
END(i)=[char(END(i)),'1'];
end
end
end
end
p=q;
end
C=B(:
j);
D=find(C==-1);
[e,f]=size(D);
ife==n
j=0;
else
j=j+1;
end
end
B
A
END
fori=1:
n
[u,v]=size(char(END(i)));
L(i)=v;
end
avlen=sum(L.*A)
哈夫曼编码:
p=input('pleaseinputanumber:
')%提示输入界面
n=length(p);
fori=1:
n
ifp(i)<0
fprintf('\nTheprobabilitiesinhuffmancannotlessthan0!
\n');
p=input('pleaseinputanumber:
')%如果输入的概率数组中有小于0的值,则重新输入概率数组
end
end
ifabs(sum(p)-1)>0
fprintf('\nThesumoftheprobabilitiesinhuffmancanmorethan1!
\n');
p=input('pleaseinputanumber:
')%如果输入的概率数组总和大于1,则重新输入概率数组
end
q=p;
a=zeros(n-1,n);%生成一个n-1行n列的数组
fori=1:
n-1
[q,l]=sort(q);%对概率数组q进行从小至大的排序,并且用l数组返回一个数组,该数组表示概率数组q排序前的顺序编号
a(i,:
)=[l(1:
n-i+1),zeros(1,i-1)];%由数组l构建一个矩阵,该矩阵表明概率合并时的顺序,用于后面的编码
q=[q
(1)+q
(2),q(3:
n),1];%将排序后的概率数组q的前两项,即概率最小的两个数加和,得到新的一组概率序列
end
fori=1:
n-1
c(i,1:
n*n)=blanks(n*n);%生成一个n-1行n列,并且每个元素的的长度为n的空白数组,c矩阵用于进行huffman编码,并且在编码中与a矩阵有一定的对应关系
end
c(n-1,n)='0';%由于a矩阵的第n-1行的前两个元素为进行huffman编码加和运算时所得的最
c(n-1,2*n)='1';%后两个概率,因此其值为0或1,在编码时设第n-1行的第一个空白字符为0,第二个空白字符1。
fori=2:
n-1
c(n-i,1:
n-1)=c(n-i+1,n*(find(a(n-i+1,:
)==1))-(n-2):
n*(find(a(n-i+1,:
)==1)));%矩阵c的第n-i的第一个元素的n-1的字符赋值为对应于a矩阵中第n-i+1行中值为1的位置在c矩阵中的编码值
c(n-i,n)='0';%根据之前的规则,在分支的第一个元素最后补0
c(n-i,n+1:
2*n-1)=c(n-i,1:
n-1);%矩阵c的第n-i的第二个元素的n-1的字符与第n-i行的第一个元素的前n-1个符号相同,因为其根节点相同
c(n-i,2*n)='1';%根据之前的规则,在分支的第一个元素最后补1
forj=1:
i-1
c(n-i,(j+1)*n+1:
(j+2)*n)=c(n-i+1,n*(find(a(n-i+1,:
)==j+1)-1)+1:
n*find(a(n-i+1,:
)==j+1));%矩阵c中第n-i行第j+1列的值等于对应于a矩阵中第n-i+1行中值为j+1的前面一个元素的位置在c矩阵中的编码值
end
end%完成huffman码字的分配
fori=1:
n
h(i,1:
n)=c(1,n*(find(a(1,:
)==i)-1)+1:
find(a(1,:
)==i)*n);%用h表示最后的huffman编码,矩阵h的第i行的元素对应于矩阵c的第一行的第i个元素
ll(i)=length(find(abs(h(i,:
))~=32));%计算每一个huffman编码的长度
end
disp('二元Huffman编码平均码长')
l=sum(p.*ll);%计算平均码长
disp(l)
%fprintf('\nhuffmancode:
\n');
h
disp('信源熵')
hh=-sum(p.*log2(p))%计算信源熵
%fprintf('\nthehuffmaneffciency:
\n');
disp('编码效率')
t=hh/l%计算编码效率
C语言实现
香农编码:
#include
#include
#include
#include
classT
{
public:
T(){}
~T();
voidCreate();
voidCoutpxj();
voidCoutk();
voidCoutz();
voidPrint();
protected:
intn;
double*p;
double*pxj;
int*k;
double*mz;
};
voidT:
:
Create()
{
cout<<"输入信源符号个数:
";
cin>>n;
p=newdouble[n];
cout<<"请输入这几个"<\n";
inti;
for(i=0;icin>>p[i];
pxj=newdouble[n];
k=newint[n];
mz=newdouble[n];
doublesum=0.0;
for(i=0;isum+=p[i];
if(sum!
=1.0)
throw1;
else
{
for(i=0;i{
intk=i;
for(intj=i+1;jif(p[k]
doublem=p[i];
p[i]=p[k];
p[k]=m;
}
}
}
T:
:
~T()
{
deletep;
deletepxj;
deletek;
deletemz;
}
voidT:
:
Coutpxj()
{
pxj[0]=0;
for(inti=1;i{
pxj[i]=0;
for(intj=0;j
pxj[i]+=p[j];
}
}
voidT:
:
Coutk()
{
for(inti=0;i{
doubled=(-1)*(log(p[i])/log
(2));
if(d-(int)d>0)k[i]=(int)d+1;
elsek[i]=(int)d;
}
}
voidT:
:
Print()
{
cout<<"Xi"<<<<<inti;
for(i=0;i{cout<<"X"<
<(2)<
<(2)<<mz[i]=pxj[i];
for(intj=0;j{
if(2*mz[i]-1>=0)
{
cout<<1;
mz[i]=2*mz[i]-1;
}
else
{
cout<<0;
mz[i]=2*mz[i];
}
}
cout<}
doubleK=0.0,H=0.0,Y;
for(i=0;i{
K+=(double)p[i]*k[i];
H+=(-1)*p[i]*(log10(p[i])/log10(2.0));
}
Y=H/K;
cout<<"平均码长:
"<cout<<"信源熵:
"<cout<<"编码效率:
"<}
voidmain()
{
Tt;inte;
try
{
t.Create();
t.Coutpxj();
t.Coutk();
t.Print();
}
catch(inte)
{if(e==1)cout<<"输入错误,请重新运行";}
}
哈夫曼编码:
#include
#include
#include
#defineMAXLEN100
typedefstruct
{
intweight;
intlchild;
intrchild;
intparent;
charkey;
}htnode;
typedefhtnodehfmt[MAXLEN];
intn;
voidinithfmt(hfmtt)//对结构体进行初始化
{
inti;
printf("\n");
printf("------------------------------------------------------------------\n");
printf("******************************输入区******************************\n");
printf("\n请输入n=");
scanf("%d",&n);
getchar();
for(i=0;i<2*n-1;i++)//对结构体进行初始化
{
t[i].weight=0;
t[i].lchild=-1;
t[i].rchild=-1;
t[i].parent=-1;
}
printf("\n");
}
voidinputweight(hfmtt)//输入函数
{
intw;//w表示权值
inti;
chark;//k表示获取的字符
for(i=0;i{
printf("请输入第%d个字符:
",i+1);
scanf("%c",&k);
getchar();
t[i].key=k;
printf("请输入第%d个字符的权值:
",i+1);
scanf("%d",&w);
getchar();
t[i].weight=w;
printf("\n");
}
}
voidselectmin(hfmtt,inti,int*p1,int*p2)//选中两个权值最小的函数
{
longmin1=999999;
longmin2=999999;
intj;
for(j=0;j<=i;j++)//选择最小权值字符的下标返回
if(t[j].parent==-1)
if(min1>t[j].weight)
{
min1=t[j].weight;
*p1=j;
}
for(j=0;j<=i;j++)//选择次小权值字符的下标还回
if(t[j].parent==-1)
if(min2>t[j].weight&&j!
=(*p1))//注意j!
=(*p1))
{
min2=t[j].weight;
*p2=j;
}
}
voidcreathfmt(hfmtt)//创建哈夫曼树的函数
{
inti,p1,p2;
inithfmt(t);
inputweight(t);
for(i=n;i<2*n-1;i++)
{
selectmin(t,i-1,&p1,&p2);
t[p1].parent=i;
t[p2].parent=i;
t[i].lchild=p1;
t[i].rchild=p2;
t[i].weight=t[p1].weight+t[p2].weight;
}
}
voidprinthfmt(hfmtt)//打印哈夫曼树
{
inti;
printf("------------------------------------------------------------------\n");
printf("**********************哈夫曼编数结构:
*****************************\n");
printf("\t\t权重\t父母\t左孩子\t右孩子\t字符\t");
for(i=0;i<2*n-1;i++)
{
printf("\n");
printf("\t\t%d\t%d\t%d\t%d\t%c",t[i].weight,t[i].parent,t[i].lchild,t
[i].rchild,t[i].key);
}
printf("\n------------------------------------------------------------------\n");
printf("\n\n");
}
voidhfmtpath(hfmtt,inti,intj)//编码的重要哈夫曼树路径递归算法
{
inta,b;
a=i;
b=j=t[i].parent;
if(t[j].parent!
=-1)
{
i=j;
hfmtpath(t,i,j);
}
if(t[b].lchild==a)
printf("0");
else
printf("1");
}
voidphfmnode(hfmtt)//对字符进行初始编码
{
inti,j,a;
printf("\n------------------------------------------------------------------\n");
printf("**************************哈夫曼编码******************************");
for(i=0;i{
j=0;
printf("\n");
printf("\t\t%c\t",t[i].key,t[i].weight);
hfmtpath(t,i,j);
}
printf("\n------------------------------------------------------------------\n");
}
voidencoding(hfmtt)//对用户输入的电文进行编码
{
charr[1000];//用来存储输入的字符串