信息论与编码课程设计报1.docx
《信息论与编码课程设计报1.docx》由会员分享,可在线阅读,更多相关《信息论与编码课程设计报1.docx(23页珍藏版)》请在冰豆网上搜索。
信息论与编码课程设计报1
信息论与编码课程设计报告
姓名:
时旭东
专业:
电科10-01
学号:
311008002320
指导老师:
成凌飞
完成日期:
2013.03.20
目录
一.课程描术。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
1
二.设计原理。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
2
三.设计内容。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
3
四.总结。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
22
五.参考文献。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
23
一.课程设计教学目的
通过本次课程设计的练习,使学生进一步巩固信源熵、信源编码的基本原理,掌握具体的编码方法,熟悉编程软件的使用,培养学生自主设计、编程调试的开发能力,同时提高学生的实践创新能力。
二.题目一:
判断唯一可译码
一.设计要求:
利用尾随后缀法判断任意输入的码是否为唯一可译码。
二.题目分析:
设计一个程序实现判断输入码组是否为唯一可译码这一功能。
在我们学习使用了克劳夫特不等式之后,知道唯一可译码必须满足克劳夫特不等式。
但是克劳夫特不等式仅仅是存在性的判定定理,即该定理不能作为判断一种码是否为唯一可译码的依据。
也就是说当码字长度和码符号数满足克劳夫特不等式时,则必可以构造出唯一可译码,否则不能构造出唯一可译码。
因此我们必须找到一种能够判断一种码是否为唯一可译码的方法—尾随后缀法。
三.算法分析:
尾随后缀法算法描述:
设C为码字集合,按以下步骤构造此码的尾随后缀集合F:
(1)考查C中所有的码字,若Wi是Wj的前缀,则将相应的后缀作为一个尾随后缀放入集合F0中;
(2)考查C和Fi两个集合,若Wj∈C是Wi∈Fi的前缀或Wi∈Fi是Wj∈C的前缀,则将相应的后缀作为尾随后缀码放入集合Fi+1中;
(3)F=∪Fi即为码C的尾随后缀集合;
(4)若F中出现了C中的元素,则算法终止,返回假(C不是唯一可译码);否则若F中没有出现新的元素,则返回真。
在我们设计的算法中,需要注意的是我们需要的是先输出所有尾随后缀的集合,然后再判断该码是否是唯一可译码,即如F中出现了C中的元素,则C不是唯一可译码,否则若F中没有出现新的元素,则C为唯一可译码。
而不是F中出现C中的元素就终止,这也是在本题的要求中需要注意的问题。
简明流程图
开始
输入码字个数和码字
进行尾随后缀编码
判断是否为唯一码
调用main()函数
结束
四.概要设计:
由于需要判断尾随后缀,所以我们需要反复的比较C和F中的码字。
1)首先我们用一个b[40][40]的数组来存放所有的尾随后缀的集合;用Q记录所有尾随后缀的个数;
2)用数组a[40][40]来存放输入的码字,L[50]来存放码字的长度;
通过一个双重循环并调用Hz(a[i],a[j],L[i],L[j])函数来找到a[40][40]中的为随后缀,即:
for(i=0;i{
for(j=0;j{
if(i!
=j&&L[i]Hz(a[i],a[j],L[i],L[j]);
}
}
3)通过判断Q是否大于0,如果不大于0,即b[40][40]中没有码字,也就是不存在尾随后缀,那么可判断a[40][40]是唯一可译码,否则进行如下操作;
4)计算b[40][40]中尾随后缀的长度,用k1表示;并调用Hz(b[i],a[j],k1,L[j])其中k1for(i=0;i{
k1=strlen(b[i]);
for(j=0;j{
if(k1Hz(b[i],a[j],k1,L[j]);
}
}
5)寻找b[40][40]中的尾随后缀;用k2表示b[40][40]中码字的长度,并调用Hz(a[i],b[j],L[i],k2)来实现,其中k2>L[j];通过循环调用即可找到b[40][40]中的所有尾随后缀,最后再将他们分别存放在b[40][40]中;即通过
for(i=0;i{
for(j=0;j{
k2=strlen(b[j]);
if(k2>L[i])
{
Hz(a[i],b[j],L[i],k2);
}
}
}
6)在反复调用Hz(a[i],a[j],L[i],L[j])函数中如果b[40][40]中有重复出现的,即尾随后缀相同的不用再次放入b[40][40]中。
7)在调用函数中所需要注意的问题就是一个比较的问题,也就是实现6)中所提到的。
五.测试结果
5.1、测试数据为0101100111010111101
5.2、测试数据为110111000010
五、源代码
#include
#include
charb[40][40];
intQ;
voidHz(charc[],chard[],intL1,intL2)
{
inti,j,temp=0;
charm[50];
for(i=0;i{
if(c[i]==d[i])continue;
elsebreak;
}
if(i==L1)
{
for(j=0;jm[j]=d[L1+j];
m[j]='\0';
for(i=0;i{
if(strcmp(b[i],m)==0)
{
temp=1;
break;
}
}
if(temp!
=1)
{
strcpy(b[Q],m);
Q++;
}
}
}
voidmain()
{
inti,j,k,k1,k2,n;
chara[40][40];
intL[50];
inttemp=1;
intf=0;
printf("请输入码字个数:
");
scanf("%d",&n);
printf("请分别输入码字:
");
for(i=0;i{
scanf("%s",&a[i]);
L[i]=strlen(a[i]);
}
for(i=0;i{
for(j=0;j{
if(i!
=j&&L[i]Hz(a[i],a[j],L[i],L[j]);
}
}
if(Q>0)
{
for(i=0;i{
k1=strlen(b[i]);
for(j=0;j{
if(k1Hz(b[i],a[j],k1,L[j]);
}
for(k=0;k{
for(j=0;j{
k2=strlen(b[j]);
if(k2>L[k])
{
Hz(a[k],b[j],L[k],k2);
}
}
}
}
printf("尾随后缀集合为:
");
for(i=0;iprintf("%s",b[i]);
for(i=0;i=0;i++)
{
for(j=0;j{
if(strcmp(a[i],b[j])==0)
{
temp=0;
break;
}
elsecontinue;
}
}
printf("\n");
if(temp==0)printf("该码不是唯一可译码!
\n");
elseprintf("该码是唯一可译码!
\n");
}
elseprintf("该码组是唯一可译码!
");
f++;
printf("\n");
}
题目二:
哈夫曼编码
1课题描述
在这个信息量爆炸的时代,凡是能载荷一定信息量,且码字的平均长度最短,可分离的变长码的码字集合称为最佳变长码。
为此,必须将概率大的信息符号编以短的码字,概率小的符号编以长的码字,使得平均码字最短。
能获得最佳码的编码方法主要有:
香农(Shannon)、费诺(Fano)、哈夫曼(Huffman)编码等。
哈夫曼(Huffman)编码是一种常用的压缩编码方法,是Huffman于1952年为压缩文本文件建立的。
它的基本原理是频繁使用的数据用较短的代码代替,较少使用的数据用较长的代码代替,每个数据的代码各不相同。
哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。
哈夫曼压缩属于可变代码长度算法一族。
意思是个体符号用一个特定长度的位序列替代。
因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
哈夫曼编码是哈夫曼树的一个应用,是一种最优的前缀技术,然而其存在的不足却制约了它的直接应用。
首先,其解码时间为O(lavg),其中lavg为码字的平均长度;其次,更为重要的是,解码器需要知道哈夫曼编码树的结构,因而编码器必须为解码器保存或传输哈夫曼编码树。
对于小量数据的压缩而言,这是很大的开销。
因而,应用哈夫曼编码的关键是如何降低哈夫曼编码树的存储空间。
目前流行的很多压缩方法都是用了该技术,如GZIB、ZLIB、PNC等。
2设计原理
对于多进制哈夫曼编码,为了提高编码效率,就要是长码的符号数量尽量少、概率尽量小,所以信源符号数量最好满足n=(m-1)*k+r,其中m为进制数,k为缩减的次数。
设计步骤如下:
[1]将信源符号按概率从大到小的顺序排列,令
p(x1)≥p(x2)≥…≥p(xn)
[2]给两个概率最小的信源符号p(xn-1)和p(xn)各分配一个码位“0”和“1”,将这两个信源符号合并成一个新符号,并用这两个最小的概率之和作为新符号的概率,或者在新添加一个信源符号,令其概率为0,则个分配一个码位“0”、“1”和“2”,将其合并,结果得到一个只包含(n-1)个信源符号的新信源。
称为信源的第一次缩减信源,用S1表示。
[3]将缩减信源S1的符号仍按概率从大到小顺序排列,此后每次合并3个信源符号,得到只含(n-3)个符号的缩减信源S2。
[4]重复上述步骤,直至最后,此时所剩符号的概率之和必为1。
然后从最后一级缩减信源开始,依编码路径向前返回,就得到各信源符号所对应的码字。
3设计过程
3.1软件介绍
3.1.1VisualC++6.0简介
VisualC++6.0,简称VC或者VC6.0,是微软推出的一款C++编译器,将“高级语言”翻译为“机器语言(低级语言)”的程序。
VisualC++是一个功能强大的可视化软件开发工具。
自1993年Microsoft公司推出VisualC++1.0后,随着其新版本的不断问世,VisualC++已成为专业程序员进行软件开发的首选工具。
VisualC++6.0由Microsoft开发,它不仅是一个C++编译器,而且是一个基于Windows操作系统的可视化集成开发环境(integrateddevelopmentenvironment,IDE)。
VisualC++6.0由许多组件组成,包括编辑器、调试器以及程序向导AppWizard、类向导ClassWizard等开发工具。
这些组件通过一个名为DeveloperStudio的组件集成为和谐的开发环境。
Microsoft的主力软件产品。
VisualC++是一个功能强大的可视化软件开发工具。
VisualC++6.0以拥有“语法高亮”,自动编译功能以及高级除错功能而著称。
比如,它允许用户进行远程调试,单步执行等。
还有允许用户在调试期间重新编译被修改的代码,而不必重新启动正在调试的程序。
其编译及创建预编译头文件(stdafx.h)、最小重建功能及累加连结(link)著称。
这些特征明显缩短程序编辑、编译及连结的时间花费,在大型软件计划上尤其显著。
3.1.2主要部分
[1]DeveloperStudio
图1DeveloperStudio环境
这是一个集成开发环境,我们日常工作的99%都是在它上面完成的,再加上它的标题赫然写着“MicrosoftVisualC++”,所以很多人理所当然的认为,那就是VisualC++了。
其实不然,虽然DeveloperStudio提供了一个很好的编辑器和很多Wizard,但实际上它没有任何编译和链接程序的功能,真正完成这些工作的幕后英雄后面会介绍。
我们也知道,DeveloperStudio并不是专门用于VC的,它也同样用于VB,VJ,VID等VisualStudio家族的其他同胞兄弟。
所以不要把DeveloperStudio当成VisualC++,它充其量只是VisualC++的一个壳子而已。
这一点请切记!
[2]MFC
从理论上来讲,MFC也不是专用于VisualC++,BorlandC++,C++Builder和SymantecC++同样可以处理MFC。
同时,用VisualC++编写代码也并不意味着一定要用MFC,只要愿意,用VisualC++来编写SDK程序,或者使用STL,ATL,一样没有限制。
不过,VisualC++本来就是为MFC打造的,VisualC++中的许多特征和语言扩展也是为MFC而设计的,所以用VisualC++而不用MFC就等于抛弃了VisualC++中很大的一部分功能。
但是,VisualC++也不等于MFC。
[3]PlatformSDK
这才是VisualC++和整个VisualStudio的精华和灵魂,虽然我们很少能直接接触到它。
大致说来,PlatformSDK是以MicrosoftC/C++编译器为核心(不是VisualC++,看清楚了),配合MASM,辅以其他一些工具和文档资料。
上面说到DeveloperStudio没有编译程序的功能,那么这项工作是由谁来完成的呢?
是CL,是NMAKE,和其他许许多多命令行程序,这些我们看不到的程序才是构成VisualStudio的基石。
3.2设计内容
例:
对如下单符号离散无记忆信源编三进制哈夫曼码。
这里:
m=3,n=8
令k=3,m+k(m-1)=9,则s=9-n=9-8=1
所以第一次取m-s=2个符号进行编码。
由计算可得:
平均码长为:
(3.1)
信息率为:
(3.2)
编码效率为:
(3.3)
可见:
哈夫曼的编码效率相当高,对编码器的要求也简单得多。
编码过程如下:
表1哈夫曼编码
信源
符号
概率
缩减信源
码字
码长
0.4
0.09
0
1
0
0.22
2
0
1
2
1
01.0
1
2
0
1
0.18
10
2
0.1
11
2
0.1
12
2
0.07
21
2
0.06
22
2
0.05
200
3
0.04
201
3
2
图2哈夫曼编码
4编码程序及其分析//**哈夫曼编码**
#include"stdio.h"
#include"iostream.h"
#include"math.h"
#defineMaxNo100
typedefcharElemType;
typedefstruct
{
ElemTypedata[MaxNo];
doubleweight;/*信源概率*/
intparent,lchild,rchild;/*父亲和左右孩子节点*/
}HTNode;
typedefstruct
{
charcd[MaxNo];/*存放编码用*/
intstart;
}HCode;
voidCreateHCode(HTNodeht[],HCodehcd[],intn)/*哈夫曼编码子程序*/
{
inti,f,c;
intchoose;
HCodehc;
printf("请输入0或1进行编码方式选择:
");/*方式选择*/
scanf("%d",&choose);
for(i=0;i{
hc.start=n;c=i;
f=ht[i].parent;
if(choose==0)/*若选择0,则哈夫曼编码以先标0开始*/
{
while(f!
=-1)
{
if(ht[f].lchild==c)hc.cd[hc.start--]='0';
elsehc.cd[hc.start--]='1';
c=f;f=ht[f].parent;
}
hc.start++;
hcd[i]=hc;
}
else/*反之以先标1开始*/
{
{
while(f!
=-1)
{
if(ht[f].lchild==c)hc.cd[hc.start--]='1';
elsehc.cd[hc.start--]='0';
c=f;f=ht[f].parent;
}
hc.start++;
hcd[i]=hc;
}
}
}
}
voidCreateHT(HTNodeht[],intn)/*构造哈夫曼树子程序*/
{
inti,j,k;
ints1,s2;/*节点*/
doublemin1,min2;
for(i=0;i<2*n-1;i++)/*进行2*n-1次合并*/
ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
for(i=n;i<2*n-1;i++)
{
min1=min2=32767;
s1=s2=-1;
for(k=0;k<=i-1;k++)/*不断循环组合最终生成Huffman树*/
{
if(ht[k].parent==-1)
{
if(ht[k].weight{
min2=min1;
s2=s1;
min1=ht[k].weight;
s1=k;
}
elseif(ht[k].weight{
min2=ht[k].weight;
s2=k;
}
}
}
ht[i].weight=ht[s1].weight+ht[s2].weight;
/*组合树根节点权重为左右子之和*/
ht[i].lchild=s1;
ht[i].rchild=s2;/*组合树左右子分别为s1,s2*/
ht[s1].parent=ht[s2].parent=i;/*新树加入数组*/
}
}
intmain()
{
inti,j,n;
doubleaverage_length,sum=0;
HTNodeht[MaxNo];
HCodehcd[MaxNo];
floatD[100];/*各概率的对数*/
floatH=0.00;
floath[100];/*存放信息熵*/
floatR;/*编码效率*/
cout<<"姓名:
时旭东学号:
311008002320"<cout<<"请输入信源符号个数:
"<while(scanf("%d",&n))
{
cout<<"请输入各个信源符号及其概率,比如:
A0.12:
"<for(i=0;i{
scanf("%s%lf",&ht[i].data,&ht[i].weight);
sum=ht[i].weight+sum;
}
if(sum>1)/*避免总概率大于1*/
cout<<"概率大于1,请重新输入"<for(i=0;i{
D[i]=3.322*(-log10(ht[i].weight));
h[i]=ht[i].weight*D[i];/*各信源的熵*/
H=H+h[i];
}
CreateHT(ht,n);/*生成哈夫曼树*/
CreateHCode(ht,hcd,n);/*生成哈夫曼编码*/
cout<<"哈夫曼编码如下:
"<for(i=0;i{
printf("%s:
",ht[i].data);
for(j=hcd[i].start;j<=n;j++)
printf("%c",hcd[i].cd[j]);
printf("\n");
}
cout<<"下面将计算该码的平均码长、编码效率:
"<average_length=0;
for(i=0;iaverage_length+=ht[i].weight*(n-hcd[i].start+1);
/*计算该信源的平均码长*/
cout<<"平均编码长度:
K="<R=H/average_length;/*编码效率*/
cout<<"编码效率:
R="<}
return0;
}
2.截图
图3程序运行结果
总结
在这次课程设计中,通过对程序的编写,调试和运行,使我更好的掌握了Huffman树等数据结构方面的基本知识和各类基本程序问题的解决方法,熟悉了各种调用的数据类型,在调试和运行过程中,加深我对程序运行的环境了解和熟悉的程度,同时也提高了我对程序调试分析的能力和对错误纠正的能力。
这次信息论与编码的程序设计,对于我来说是一个挑战。
我对数据结构的学习在程序的设计中也有所体现。
课程设计是培养学生综合运用所学知识,发现问题、提出问题、分析问题和解决问题的过程,锻炼学生的逻辑思维能力和实践能力,是对学生实际工作能力的具体训练和考察过程。
在整个课程程序中,我们充分应用和调用各个程序模块,从而部分实现了此次程序设计的所应该有的功能。
就是我在课程设计是比较成功的方面,而