信息论与编码课程设计报告.docx
- 文档编号:30182423
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:21
- 大小:64.39KB
信息论与编码课程设计报告.docx
《信息论与编码课程设计报告.docx》由会员分享,可在线阅读,更多相关《信息论与编码课程设计报告.docx(21页珍藏版)》请在冰豆网上搜索。
信息论与编码课程设计报告
信息论与编码课程设计报告
Huffman编码与译码
姓名:
学院:
班级:
学号:
指导老师:
2010年12月
一、课程设计目的
掌握通过计算机编程实现Huffman编码
二、基本原理
Huffman编码又称哈夫曼编码,是一种可变长编码方式,是由美国数学家DavidHuffman创立的,是二叉树的一种特殊转化形式。
编码的原理是:
将使用次数多的代码转换成长度较短的代码,而使用次数少的可以使用较长的编码,并且保持编码的唯一可解性。
Huffman算法的最根本的原则是:
累计的(字符的统计数字*字符的编码长度)为最小,也就是权值(字符的统计数字*字符的编码长度)的和最小。
Huffman编码的步骤:
1)把信源符号按概率大小顺序排列,并设法按逆次序分配码字的长度。
2)在分配码字长度时,首先将出现概率最小的两个符号的概率相加合成一个概率。
3)把这个合成概率看成是一个新组合符号地概率,重复上述做法直到最后只剩下两个符号概率为止。
4)
5)
6)
7)
8)
9)
10)
11)完成以上概率顺序排列后,再反过来逐步向前进行编码,每一次有二个分支各赋予一个二进制码,可以对概率大的赋为0,概率小的赋为1。
Huffman编码分析:
从以上编码步骤可以看出,Huffman码是用概率匹配的方法进行信源匹配方法进行信源。
它有两个明显的特点:
一是Huffman码的编码方法保证了概率大的符号对应于短码,概率小的符号对应于长码,充分应用了短码;二是缩减信源的最后二个码字总是最后一位不同,从而保证了Huffman编码是及时码。
Huffman变长码的效率是相当高的,它可以单个信源符号编码或用长度较小的信源序列编码,对编码的设计来说也将简单得多。
本次实验将采用C语言对Huffman编码的实现进行编程,主要实现的功能是对字符串编码(实现压缩的功能),而后进行相反译码过程(即解压的功能)。
其中将使用数据结构中的Huffman树的知识,最后将基本功能进行扩展,从而实现对一个txt文档的压缩和解压功能。
本次实验中的编码过程的基本流程图如下所示:
三、方案实践与调试
本程序主要分为以下4部分:
1.构造Huffman树
给定n个实数w1,w2,......,wn(n≥2),求一个具有n个结点的二叉数,使其带权路径长度最小。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
树的带权路径长度记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。
可以证明Huffman树的WPL是最小的。
(1)根据与n个权值{w1,w2…wn}对应的n个结点构成具有n棵二叉树的森林F={T1,T2…Tn},其中第i棵二叉树Ti(1≤i≤n)都只有一个权值为wi的根结点,其左、右子树均为空
(2)在森林F中选出两棵根结点的权值最小的树作为一棵新树的左、右子树,且置新树的根结点的权值为其左、右子树上根结点权值之和
(3)从F中删除构成新树的那两棵,同时把新树加入F中
(4)重复第
(2)和第(3)步,直到F中只含有一棵为止,此树便为Huffman树
2.Huffman编码算法
根据最优二叉树构造Huffman编码,利用Huffman树很容易求出给定字符集及其概率(或频度)分布的最优前缀码。
Huffman编码正是一种应用广泛且非常有效的数据压缩技术。
该技术一般可将数据文件压缩掉20%至90%,其压缩效率取决于被压缩文件的特征。
(1)用字符s[i]作为叶子,count[i]做为叶子s[i]的权,构造一棵Huffman树,并将树中左分支和右分支分别标记为0和1;
(2)将从根到叶子的路径上的标号依次相连,作为该叶子所表示字符的编码。
该编码即为最优前缀码(也称Huffman编码)。
3.Huffman译码算法
依次读人文件的二进制码,从Huffman树的根结点(即T[m-1])出发,若当前读人0,则走向左孩子,否则走向右孩子。
一旦到达某一叶子T时便译出相应的字符H.ch。
然后重新从根出发继续译码,直至文件结束。
4.二进制转换算法
将Huffman编码存储为二进制文件以及将生成的二进制文件转换为Huffman编码以实现压缩的过程。
程序中主要定义了两个结构体:
1.定义赫夫曼树节点结构体
typedefstructnode{
intweight;
structnode*LChild,*RChild,*Parent;//分别指向该节点的左孩子,右孩子,和双亲节点
structnode*next;//指向建立的赫夫曼树的下一个节点
}HFMNode,*HFMTree;
2.定义赫夫曼编码的结构体
typedefstruct{
charch;//存储对应的字符
charcode[N+1];//存储对应字符的编码
intstart;//存储编码的起始位置
}CodeNode;
详细设计
1.构造霍夫曼树
主要代码如下:
voidCreatHFMTree(HFMTree*HT,intcount[]){//创建赫夫曼树
inti;
HFMTreep,HT1,HT2;//HT1,HT2分别存放权值最小和次小的节点的位置
p=*HT=(HFMTree)malloc(sizeof(HFMNode));
p->next=p->LChild=p->RChild=p->Parent=NULL;//初始化赫夫曼链表且有2n-1个节点
for(i=1;i<2*n-1;i++)
{p->next=(HFMTree)malloc(sizeof(HFMNode));
p=p->next;
p->next=p->LChild=p->RChild=p->Parent=NULL;
}
for(i=0,p=*HT;i p->weight=count[i]; p=p->next; } for(i=n;i<2*n-1;i++){//将后n-1个节点赋权值,建树 SelectMin(*HT,i,&HT1,&HT2);//每次从前i个节点中选取权值最小的两个节点 HT1->Parent=HT2->Parent=p; p->LChild=HT1; p->RChild=HT2; p->weight=HT1->weight+HT2->weight;//将两个节点的权值相加存入一个节点 p=p->next;//p指向下一个没有存储权值的节点 } } 2.霍夫曼编码算法 主要代码如下: voidTotalCoding(chars[],CodeNodeHC[],charcode[]){ //利用赫夫曼编码表对整个字符串进行编码 inti,j;code[0]='\0';//编码数组初始化 for(i=0;s[i];i++) for(j=0;j if(s[i]==HC[j].ch) strcpy(code+strlen(code),HC[j].code+HC[j].start); } 给定字符集的赫夫曼树生成后,求赫夫曼编码的具体实现过程是: 依次以叶子s[i](0≤i≤n-1)为出发点,向上回溯至根为止。 上溯时走左分支则生成代码0,走右分支则生成代码1。 (1)由于生成的编码与要求的编码反序,将生成的代码先从后往前依次存放在一个临时向量中,并设一个指针start指示编码在该向量中的起始位置. (2)当某字符编码完成时,从临时向量的start处将编码复制到该字符相应的位串中即可。 (3)因为字符集大小为n,故变长编码的长度不会超过n,加上一个结束符'\0'. 3.霍夫曼译码算法 主要代码如下: voidDeCoding(charcode[],HFMTreeHT,charstr[],chars[]){ //对赫夫曼编码进行解码,放入字符串s中 inti,j,k=0; HFMTreeroot,p,q; for(root=HT;root->Parent;root=root->Parent);//用root指向赫夫曼树的根结点 for(i=0,p=root;code[i];i++){//从根结点开始按编码顺序访问树 if(code[i]=='0') p=p->LChild; elsep=p->RChild; if(p->LChild==NULL&&p->RChild==NULL){ //到根节点时将该节点对应的字符输出 for(j=0,q=HT;q! =p;q=q->next,j++); s[k++]=str[j]; p=root;//回溯到根结点 } } s[k]='\0';//解码完毕,在字符串最后一个单元存入'\0' } 编码界面 解码界面 四、总结体会 在这次课程设计中,通过对程序的编写,调试和运行,使我更好的掌握了Huffman树等数据结构方面的基本知识和各类基本程序问题的解决方法,熟悉了各种调用的数据类型,在调试和运行过程中,加深我对程序运行的环境了解和熟悉的程度,同时也提高了我对程序调试分析的能力和对错误纠正的能力。 这次信息论与编码的程序设计,对于我来说是一个挑战。 我对数据结构的学习在程序的设计中也有所体现。 课程设计是培养学生综合运用所学知识,发现问题、提出问题、分析问题和解决问题的过程,锻炼学生的逻辑思维能力和实践能力,是对学生实际工作能力的具体训练和考察过程。 在整个课程程序中,我们充分应用和调用各个程序模块,从而部分实现了此次程序设计的所应该有的功能。 就是我在课程设计是比较成功的方面,而在这个过程中,让我感觉收获最大的就是我们都能利用这次课程设计学到很多我们在课本上没有的知识,充分的发挥了我们的主动性,使我们学会了自主学习,和独立解决问题的能力。 很多程序在结构上是独立的,但是本此设计的程序功能不是零散的,它有一个连接是的程序是一个整体,达到这种统一体十分重要,因为这个输出连接是贯穿始终的。 这次的程序软件基本上运行成功,可以简单的输入进行压缩,并且运用简单的数字告诉程序的操作者下一步该如何进行,使得程序规模相对较小,即功能还不很全面,应用也不很普遍。 原来数据结构可以涉及很多知识,而不是枯燥无聊的简单的代码部分而已,利用数据结构方面的知识,我们可以设计出更完善的软件。 总而言之,这次数据结构课程设计让我们感触很深,使我们每个人都了解到学习不应该只局限于课本,因为课本上告诉我们的只是很有限的一部分,只是理论上的死知识,所涉及的范围也是狭窄的。 我们若想在有限的范围内学习到无限的知识,就要我们自己懂得争取,懂得自学,懂得充分利用身边的任何资源。 在我们的程序中有一部分查找许多该方面的资料,我竭力将所获得的信息变成自己的资源。 我动手上机操作的同时,在了解和看懂的基础上对新学的知识进行改进和创新,但是在我们的程序软件中还有很多的不足,需要加以更新。 通过这次课程设计,我们都意识到了自己动手实践的弱势,特别是在编程方面,于是我们知道了计算机的实践操作是很重要的,只有通过上机编程才能充分的了解自己的不足。 通过这次的课程设计,我们深刻意识到自己在学习中的弱点,同时也找到了克服这些弱点的方法,这是在此活动中得到的一笔很大的财富。 在以后的时间中,我们应该利用更多的时间去上机实验,多编写程序,相信不久后我们的编程能力都会有很大的提高,能设计出更多的更有创新的软件。 同时也感谢老师给我们这次机会,发现自身存在的缺点与不足,从而在以后的大学生活中更好的提升和完善自我。 参考书目: [2]严蔚敏,吴伟民.《数据结构》.清华大学出版社,2007.4 附录源代码 #include #include #include #include #defineM10000//定义字符串最大长度 #defineN128//定义叶子节点个数 typedefstructnode//定义赫夫曼树节点结构体 { intweight; structnode*LChild,*RChild,*Parent;//分别指向该节点的左孩子,右孩子,和双亲节点 structnode*next;//指向建立的赫夫曼树的下一个节点 }HNode,*HTree; typedefstruct//定义赫夫曼编码的结构体 { charch;//存储对应的字符 charcode[N+1];//存储对应字符的编码 intstart;//存储编码的起始位置 }CodeNode; intn;//存储真正叶子节点个数 voidOpen(chars[]){//打开存放字符或编码的文件,将其存入字符串数组中 charname[10]; FILE*fp; inti=0,j; printf("请输入待压缩文件的文件名: ");getchar(); scanf("%s",name);//要打开的文件名 if((fp=fopen(name,"rt"))==NULL){ printf("打开失败! \n");//若打开失败,则直接退出 exit (1); } s[i++]=fgetc(fp); while(s[i-1]! =EOF) s[i++]=fgetc(fp); s[i]='\0';//存取字符串结束 fclose(fp); } voidSave(chars[]){//保存字符或编码到文件中 charname[10]; FILE*fp; printf("请输入要保存的文件名: "); gets(name); if((fp=fopen(name,"wt"))==NULL) { printf("存储失败! "); exit (1); } fputs(s,fp); printf("\n保存成功,文件名为: %s。 \n",name); printf("\n按回车键继续..."); getchar(); fclose(fp); } voidSearchStr(chars[],charstr[],intcount[]){//查找字符串中字符的个数和每个字符出现的次数 inti,j,k=0; for(i=0;i count[i]=0; for(i=0;s[i];i++){ for(j=0;j if(str[j]==s[i]){ count[j]++; break; } if(j==k){//在str[]中无该字符,将其存入最后一个单元 str[k]=s[i]; count[k++]++; } } str[k]='\0';//将字符串结尾置“\0” n=k;//将实际的字符个数作为叶子节点个数存入n } voidSelectMin(HTreeHT,intk,HTree*HT1,HTree*HT2){//查找赫夫曼链表中两个权值最小的节点 inti,min; HTreep; min=32767; for(i=0,p=HT;i if(p->weight { min=p->weight; *HT1=p; } min=32767; for(i=0,p=HT;i if(p->weight =*HT1)//令第二个最小的节点不等于第一个节点 { min=p->weight; *HT2=p; } } voidCreatHFMTree(HTree*HT,intcount[])//创建赫夫曼树 { inti; HTreep,HT1,HT2;//HT1,HT2分别存放权值最小和次小的节点的位置 p=*HT=(HTree)malloc(sizeof(HNode)); p->next=p->LChild=p->RChild=p->Parent=NULL; for(i=1;i<2*n-1;i++) { p->next=(HTree)malloc(sizeof(HNode)); p=p->next; p->next=p->LChild=p->RChild=p->Parent=NULL;//将各个字符出现的次数作为权值 } for(i=0,p=*HT;i { p->weight=count[i]; p=p->next; } for(i=n;i<2*n-1;i++)//将后n-1个节点赋权值,建树 { SelectMin(*HT,i,&HT1,&HT2);//每次从前i个节点中选取权值最小的两个节点 HT1->Parent=HT2->Parent=p; p->LChild=HT1; p->RChild=HT2; p->weight=HT1->weight+HT2->weight;//将两个节点的权值相加存入最后一个节点中 p=p->next;//p指向下一个没有存储权值的节点 } } voidHFMCode(HTreeHT,CodeNodeHC[],charstr[]){//从每个叶子节点开始,利用赫夫曼树对每个字符进行编码,最终建立一个赫夫曼表 inti; HTreeq,p=HT; for(i=0;i { HC[i].ch=str[i]; HC[i].code[n-1]='\0';//初始化编码的最后一位 } for(i=0;i HC[i].start=n-1; for(q=p;q->Parent;q=q->Parent)//判断q所指向的节点,左孩子置0,右孩子置1 if(q==q->Parent->LChild) HC[i].code[--HC[i].start]='0'; elseHC[i].code[--HC[i].start]='1'; p=p->next;//判断下一个叶子节点 } } voidTotalCoding(chars[],CodeNodeHC[],charcode[])//利用赫夫曼编码表对整个字符串进行编码 { inti,j;code[0]='\0';//编码数组初始化 for(i=0;s[i];i++)//将每个字符在赫夫曼编码表中对应的编码存入存放总编码的数组中 for(j=0;j if(s[i]==HC[j].ch) strcpy(code+strlen(code),HC[j].code+HC[j].start); } voidDeCoding(charcode[],HTreeHT,charstr[],chars[])//对赫夫曼编码进行解码,放入字符串s中 { inti,j,k=0; HTreeroot,p,q; for(root=HT;root->Parent;root=root->Parent);//用root指向赫夫曼树的根结点 for(i=0,p=root;code[i];i++){//从根结点开始按编码顺序访问树 if(code[i]=='0') p=p->LChild; elsep=p->RChild; if(p->LChild==NULL&&p->RChild==NULL){ //到根节点时将该节点对应的字符输出 for(j=0,q=HT;q! =p;q=q->next,j++); s[k++]=str[j]; p=root;//回溯到根结点 } } s[k]='\0';//解码完毕,在字符串最后一个单元存入'\0' } voidTransCode(charcode[],charstr[],charss[],HTree*HT,CodeNodeHC[]) { charstr_in; printf("\n打开编码的文件...\n\n"); Open(code);//打开编码文件 DeCoding(code,*HT,str,ss);//将编码进行解码存入字符串数组ss[]中 printf("\n得到的最终字符串为: \n"); puts(ss); printf("你要保存译码吗? 输入y或n: "); scanf("%c",&str_in); if(str_in=='y') Save(ss);//保存译码后的字符串 } voidshowtip1(chars[M]) { charchr_in; printf("\t你选择了编码: \n"); cout<<"\t\t|-------------------------------
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 信息论 编码 课程设计 报告