信息论实验报告.docx
《信息论实验报告.docx》由会员分享,可在线阅读,更多相关《信息论实验报告.docx(63页珍藏版)》请在冰豆网上搜索。
信息论实验报告
信息论实验
实验一
*哈夫曼编码(HuffmanCoding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。
Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般叫作Huffman编码。
实验目的:
利用程序实现哈夫曼编码,以加深对哈夫曼编码的理解,并锻炼编程能力。
实验分析:
哈夫曼编码步骤:
一、对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F={T1,T2,T3,...,Ti,...,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。
(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。
)
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
简易的理解就是,假如我有A,B,C,D,E五个字符,出现的频率(即权值)分别为5,4,3,2,1,那么我们第一步先取两个最小权值作为左右子树构造一个新树,即取1,2构成新树,其结点为1+2=3,如图:
虚线为新生成的结点,第二步再把新生成的权值为3的结点放到剩下的集合中,所以集合变成{5,4,3,3},再根据第二步,取最小的两个权值构成新树,如图:
再依次建立哈夫曼树,如下图:
其中各个权值替换对应的字符即为下图:
所以各字符对应的编码为:
A->11,B->10,C->00,D->011,E->010
霍夫曼编码是一种无前缀编码。
解码时不会混淆。
其主要应用在数据压缩,加密解密等场合。
实验代码:
#include
#include
#include
#include
#defineMAXBIT100
#defineMAXVALUE10000
#defineMAXLEAF30
#defineMAXNODEMAXLEAF*2-1
typedefstruct
{
intbit[MAXBIT];
intstart;
}HCodeType;/*编码结构体*/
typedefstruct
{
intweight;
intparent;
intlchild;
intrchild;
intvalue;
}HNodeType;/*结点结构体*/
/*构造一颗哈夫曼树*/
voidHuffmanTree(HNodeTypeHuffNode[MAXNODE],intn)
{
/*i、j:
循环变量,m1、m2:
构造哈夫曼树不同过程中两个最小权值结点的权值,
x1、x2:
构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。
*/
inti,j,m1,m2,x1,x2;
/*初始化存放哈夫曼树数组HuffNode[]中的结点*/
for(i=0;i<2*n-1;i++)
{
HuffNode[i].weight=0;//权值
HuffNode[i].parent=-1;
HuffNode[i].lchild=-1;
HuffNode[i].rchild=-1;
HuffNode[i].value=i;//实际值,可根据情况替换为字母
}/*endfor*/
/*输入n个叶子结点的权值*/
for(i=0;i{
printf("Pleaseinputweightofleafnode%d:
\n",i);
scanf("%d",&HuffNode[i].weight);
}/*endfor*/
/*循环构造Huffman树*/
for(i=0;i{
m1=m2=MAXVALUE;/*m1、m2中存放两个无父结点且结点权值最小的两个结点*/
x1=x2=0;
/*找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树*/
for(j=0;j{
if(HuffNode[j].weight{
m2=m1;
x2=x1;
m1=HuffNode[j].weight;
x1=j;
}
elseif(HuffNode[j].weight{
m2=HuffNode[j].weight;
x2=j;
}
}/*endfor*/
/*设置找到的两个子结点x1、x2的父结点信息*/
HuffNode[x1].parent=n+i;
HuffNode[x2].parent=n+i;
HuffNode[n+i].weight=HuffNode[x1].weight+HuffNode[x2].weight;
HuffNode[n+i].lchild=x1;
HuffNode[n+i].rchild=x2;
printf("x1.weightandx2.weightinround%d:
%d,%d\n",i+1,HuffNode[x1].weight,HuffNode[x2].weight);/*用于测试*/
printf("\n");
}/*endfor*/
}/*endHuffmanTree*/
//解码
voiddecodeing(charstring[],HNodeTypeBuf[],intNum)
{
inti,tmp=0,code[1024];
intm=2*Num-1;
char*nump;
charnum[1024];
for(i=0;i{
if(string[i]=='0')
num[i]=0;
else
num[i]=1;
}
i=0;
nump=&num[0];
while(nump<(&num[strlen(string)]))
{tmp=m-1;
while((Buf[tmp].lchild!
=-1)&&(Buf[tmp].rchild!
=-1))
{
if(*nump==0)
{
tmp=Buf[tmp].lchild;
}
elsetmp=Buf[tmp].rchild;
nump++;
}
printf("%d",Buf[tmp].value);
}
}
intmain(void)
{
HNodeTypeHuffNode[MAXNODE];/*定义一个结点结构体数组*/
HCodeTypeHuffCode[MAXLEAF],cd;/*定义一个编码结构体数组,同时定义一个临时变量来存放求解编码时的信息*/
inti,j,c,p,n;
charpp[100];
printf("Pleaseinputn:
\n");
scanf("%d",&n);
HuffmanTree(HuffNode,n);
for(i=0;i{
cd.start=n-1;
c=i;
p=HuffNode[c].parent;
while(p!
=-1)/*父结点存在*/
{
if(HuffNode[p].lchild==c)
cd.bit[cd.start]=0;
else
cd.bit[cd.start]=1;
cd.start--;/*求编码的低一位*/
c=p;
p=HuffNode[c].parent;/*设置下一循环条件*/
}/*endwhile*/
/*保存求出的每个叶结点的哈夫曼编码和编码的起始位*/
for(j=cd.start+1;j{HuffCode[i].bit[j]=cd.bit[j];}
HuffCode[i].start=cd.start;
}/*endfor*/
/*输出已保存好的所有存在编码的哈夫曼编码*/
for(i=0;i{
printf("%d'sHuffmancodeis:
",i);
for(j=HuffCode[i].start+1;j{
printf("%d",HuffCode[i].bit[j]);
}
printf("start:
%d",HuffCode[i].start);
printf("\n");
}
printf("Decoding?
PleaseEntercode:
\n");
scanf("%s",&pp);
decodeing(pp,HuffNode,n);
getch();
return0;
}
实验结果:
实验二
*1950年,汉明描述了构造最小距离为3的编码的一般方法,现在称为汉明码。
对于任意i值,其方法能产生(2i-1)位的编码,其中包含i个校验位和2i-1-i个信息位。
实验目的:
*理解信道编码的原理,理解汉明码的基本原理与编码过程,利用程序实现汉明码编码,理解信道编码对可靠性的追求。
实验分析:
①汉明校验的基本思想
将有效信息按某种规律分成若干组,每组安排一个校验位,做奇偶测试,就能提供多位检错信息,以指出最大可能是哪位出错,从而将其纠正。
实质上,汉明校验是一种多重校验。
②汉明校验的特点
它不仅具有检测错误的能力,同时还具有给出错误所在准确位置的能力。
一、校验位的位数校验位的位数与有效信息的长度有关
设:
N--为校验码的位数K--是有效信息位r--校验位(分成r组作奇偶校验,能产生r位检错信息)汉明码应满足N=K+r≤2r-1若r=3则N=K+r≤7所以K≤4
二.分组原则
在汉明码中,位号数(1、2、3、……、n)为2的权值的那些位,即:
1(20)、2(21)、4(22)、8(23)、…2r-1位,作为奇偶校验位
并记作:
P1、P2、P3、P4、…Pr,余下各位则为有效信息位。
例如:
N=11K=7r=4相应汉明码可示意为
位号 1 2 3 4 5 6 7 8 9 10 11
P占位P1P2×P3×××P4 × × ×
其中×均为有效信息,汉明码中的每一位分别被P1P2P3P4…Pr中的一至若干位所校验,其规律是:
第i位由校验位位号之和等于i的那些校验位所校验
如:
汉明码的位号为3,它被P1P2(位号分别为1,2)所校验
汉明码的位号为5,它被P1P3(位号分别为1,4)所校验
归并起来:
形成了4个小组,每个小组一个校验位,校验位的取值,仍采用奇偶校验方式确定。
如表2·6、表2·7所示:
三.编码、查错、纠错原理
以4位有效信息(b1、b2、b3、b4)和3位校验位(P1、P2、P3)为例:
K=4r=3
汉明序号1 2 3 4 5 6 7
汉明码 P1P2b1P3b2b3b4
根据表2-8可以看到
(1)每个小组只有一位校验位,第一组是P1、第二组是P2、第三组是P3。
(2)每个校验位校验着它本身和它后面的一些确定位。
1.编码原理(采用偶校验)
1)若有效信息b1b2b3b4=1011先将它们分别填入第3、5、6、7位
2)再分组进行奇偶统计,分别填入校验位P1、P2、P3的值
如:
第一组有:
P1b1b2b4因b1b2b4含偶数个1,故P1应取值为0
第二组有:
P2b1b3b4因b1b3b4含奇数个1,故P2应取值为1
第三组有:
P3b2b3b4因b2b3b4含偶数个1,故P3应取值为0
汉明编码为:
P1P2b1P3b2b3b4=0110011
2.查错与纠错
因为分三组校验,每组产生一位检错信息、3组共3位检错信息,便构成一个指误字,上例指误字由G1G2G3组成。
其中:
G3=P3⊕b2⊕b3⊕b4 P3b2b3b4=0011
G2=P2⊕b1⊕b3⊕b4 P2b1b3b4=1111
G1=P1⊕b1⊕b2⊕b4 P1b1b2b4=0101
采用偶校验,在没有出错情况下G1G2G3=000。
由于在分组时,就确定了每一位参加校验的组别,所以指误字能准确地指出错误所在位。
如:
若第3位b1出错,由于b1参加了第一组和第二组的校验,必然破坏了第一组和第二组的偶性,从而使G1和G2为1。
因为b1未参加第三组校验,故G3=0,所以构成的指误字G3G2G1=011它指出第3位出错。
反之:
若G3G2G1=111则说明汉明码第7位b4出错。
因为只有第7位b4参加了3个小组的校验,破坏了三个小组的偶性。
假定:
源部件发送汉明码为:
0110011接收端接收汉明码为:
0110011
则:
三个小组都满足偶校验要求,这时G3G2G1=000,表明收到信息正确,可以从中提出有效信息1011参与运算处理。
纠错:
若接收端收到的汉明码为0110111,分组检测后指误字G3G2G1=101,它指出第5位出错,则只须将第5位变反,就可还原成正确的数码0110011。
实验代码:
//codebyzxf2010.4.10
#include
#include
#include
#include
//N代表待编码数据的上限位数
#defineN100
intHmLength(intk);//计算汉明码校验位位数
voidInCode(char*data,char*c,intk,intr);//计算汉明码每个校验位的数值
intmain()
{
intk=0,r=0,dnum=0,cnum=0;
chardata[N];
charc[N];
system("cls");
printf("NowpleaseinputthedatayouwanttoIncode:
");
for(k=0;k{
data[k]=getche();
if(data[k]!
='0'&&data[k]!
='1')
{
break;
}
}
printf("\n__________________________________________________________\n");
r=HmLength(k);//求取校验位数
printf("k=%dr=%dThelengthofHammingcode=%d\n",k,r,r+k);//输出汉明码相关位数信息
InCode(data,c,k,r);//计算汉明码
printf("TheHammingcodeis:
");
for(intj=1;j{
if(j==(int)pow(2,cnum))
{
printf("%c",c[cnum]);
cnum++;
}
else
{
printf("%c",data[dnum]);
dnum++;
}
}
getch();
}
/*
*@func:
计算校验码
*@param:
data待编码数据,c校验码,k数据长度,r校验码位数
*codebyzxf
*/
voidInCode(char*data,char*c,intk,intr)
{
for(inti=0;i{
intsum=0,dnum=0,cnum=0;
for(intj=1;j{
if(j==(int)pow(2,cnum))//去掉矩阵的全零行
{
cnum++;//记录校验码循环位数
}
else
{
intx=0,y=0;
x=pow(2,i);
y=j%(x*2);
x=y/x;//通过取余取商计算获取矩阵第r-i列第j行元素的值
sum+=data[dnum]*x;//累加乘积
dnum++;//记录数据的循环位数
}
}
c[i]=sum%2==0?
'0':
'1';//通过结果的奇偶设置校验位的值
}
}
/*
*@func:
计算校验码的位数
*@param:
k数据长度
*@return:
校验码位数
*codebyzxf
*/
intHmLength(intk)
{
intr=0,flag=1;
while(flag!
=0)//循环到2^r-1>=r+K时即得到校验码的位数
{
inttemp=0;
temp=pow(2,r);
temp=temp-1;
flag=(temp-r-k<0);
r++;
}
returnr-1;
}
实验结果:
实验三
实验分析:
卷积码的编码方法是对输入的数据流每次1比特或者K比特进行编码,输出N个编码字符。
但输出的码字的每个码元不仅与当前时刻输入的K个信息有关也和之前的M个连续输入的信息员有关。
因此卷积码是有记忆的。
输入信息序列为u=(u0,u1,…),其多项式表示为u(x)=u0+u1x+…+ulxl+…。
编码器的连接可用多项式表示为g(1,1)(x)=1+x+x2和g(1,2)(x)=1+x2,称为码的子生成多项式。
它们的系数矢量g(1,1)=(111)和g(1,2)=(101)称作码的子生成元。
以子生成多项式为阵元构成的多项式矩阵G(x)=[g(1,1)(x),g(1,2)(x)],称为码的生成多项式矩阵。
由生成元构成的半无限矩阵称为码的生成矩阵。
其中(11,10,11)是由g(1,1)和g(1,2)交叉连接构成。
编码器输出序列为c=u·G,称为码序列,其多项式表示为c(x),它可看作是两个子码序列c⑴(x)和c⑵(x)经过合路开关S合成的,其中c⑴(x)=u(x)g(1,1)(x)和c⑵(x)=u(x)g(1,2)(x),它们分别是信息序列和相应子生成元的卷积,卷积码由此得名。
在一般情况下,输入信息序列经过一个时分开关被分成k0个子序列,分别以u(x)表示,其中i=1,2,…k0,即u(x)=[u(x),…,u(x)]。
编码器的结构由k0×n0阶生成多项式矩阵给定。
输出码序列由n0个子序列组成,即c(x)=[c(x),c(x),…,c(x)],且c(x)=u(x)·G(x)。
若m是所有子生成多项式g(x)中最高次式的次数,称这种码为(n0,k0,m)卷积码。
实验代码:
#include
#include
usingnamespacestd;
inttable1[8]={1,2,4,8,16,32,64,128};
intmyn=0;//约束长度
intstalen=0;//状态个数
intstan0[256][2]={0};//输入0时个状态的输出
intstan1[256][2]={0};//输入1时各状态的输出
intstachn[256][2]={0};//状态转换表
intmyg1[10]={0};//用户输入生成序列1
intmyg2[10]={0};//用户输入生成序列2
intmyout[100];
intmyoutsym=0;
voidchartobits(charch,int*bits);
charbitstochar(int*bits);
voidconvolution(void);
//voidcreatsta(void);
voidmyoutput(int*p,intlength,intdepth);
voidmyinput(void);//用户可自行输入约束长度和编码矢量
intmain()
{
charexit_char;
myinput();
convolution();
cin>>exit_char;
}
voidmyinput(void)
{
inti,j,k,input[100]={0};
cout<<"请输入编码的约束长度M:
(3<=M<=9)";
cin>>myn;
cout<stalen=int(pow(2.0,myn-1));
cout<<"若选择默认的编码矢量则输入1,输入2则可输入其他的编码矢量:
";
cin>>i;
cout<cout<<"输入需要的二进制数据:
";
cin>>k;
cout<cout<<"请输入"<";
for(j=0;jcin>>input[j];
cout<if(i==1){
switch(myn){
case3:
myg1[0]=1,myg1[1]=1,myg1[2]=1;
myg2[0]=1,myg2[1]=0,myg2[2]=1;
break;
case4:
myg1[0]=1,myg1[1]=1,myg1[2]=1,myg1[3]=1;
myg2[0]=1,myg2[1]=0,myg2[2]=1,myg2[3]=1;
break;
case5:
myg1[0]=1,myg1[1]=0,myg1[2]=1,myg1[3]=1,myg1[4]=1;
myg2[0]=1,myg2[1]=1,myg2[2]=0,myg2[3]=1,myg2[4]=1;
break;
case6:
myg1[0]=1,myg1[1]=0,myg1[2]=1,myg1[3]=1,myg1[4]=1,myg1[5]=1;
myg2[0]=1,myg2[1]=1,myg2[2]=0,m