香农三大定理是信息论的基础理论。香农三大定理是存在性定理,虽然并没有提供具体的编码实现方法,但为通信信息的研究指明了方向。香农第一定理是可变长无失真信源编码定理。香农第二定理是有噪信道编码定理。香农第三定理是保失真度准则下的有失真信源编码定理。
香农第一定理(可变长无失真信源编码定理)
设离散无记忆信源X包含N个符号{x1,x2,…,xi,..,xN},信源发出K重符号序列,则此信源可发出N^k个不同的符号序列消息,其中第j个符号序列消息的出现概率为PKj,其信源编码后所得的二进制代码组长度为Bj,代码组的平均长度B为
B=PK1B1+PK2B2+…+PKN^kBN^k
当K趋于无限大时,B和信息量H(X)之间的关系为B/k=H(X)(K趋近无穷)
香农第一定理又称为无失真信源编码定理或变长码信源编码定理。
香农第一定理的意义:将原始信源符号转化为新的码符号,使码符号尽量服从等概分布,从而每个码符号所携带的信息量达到最大,进而可以用尽量少的码符号传输信源信息。
香农第二定理(有噪信道编码定理)
有噪信道编码定理。当信道的信息传输率不超过信道容量时,采用合适的信道编码方法可以实现任意高的传输可靠性,但若信息传输率超过了信道容量,就不可能实现可靠的传输。
设某信道有r个输入符号,s个输出符号,信道容量为C,当信道的信息传输率R<C,码长N足够长时,总可以在输入的集合中(含有r^N个长度为N的码符号序列),找到M ((M<=2^(N(C-a))),a为任意小的正数)个码字,分别代表M个等可能性的消息,组成一个码以及相应的译码规则,使信道输出端的最小平均错误译码概率Pmin达到任意小。
公式:
注:B为信道带宽;S/N为信噪比,通常用分贝(dB)表示。
香农第三定理(保失真度准则下的有失真信源编码定理)
保真度准则下的信源编码定理,或称有损信源编码定理。只要码长足够长,总可以找到一种信源编码,使编码后的信息传输率略大于率失真函数,而码的平均失真度不大于给定的允许失真度,即D'<=D.
设R(D)为一离散无记忆信源的信息率失真函数,并且选定有限的失真函数,对于任意允许平均失真度D>=0,和任意小的a>0,以及任意足够长的码长N,则一定存在一种信源编码W,其码字个数为M<=EXP{N[R(D)+a]},而编码后码的平均失真度D'(W)<=D+a。
固定算术编码与译码代码解析
由于C++小数位的精度有限,在信源符号过长时容易因为精度不够导致编码错误,固对一段信源采用分组编译码(每组8个)。
算术编码是将一个符号序列表示成0和1之间的一个间隔(Interval),并用该间隔内的一个浮点小数表示,再将该小数转换成二进制数。符号序列越长,对应的间隔越小,表示这一间隔的二进制位数就越多。固定算术编码又称静态算术编码。下面以如下习题为例编写代码:
题目1:设信源可能输出的符号是26个字母,且每个字母出现的概率为:a, b, c, d, e, f 均为0.1,其它是等概的,试编写程序可以对任意字母序列(如presentation)进行固定模式的算术编码,并进行相应的译码。
定义的全局变量如下:
#include<iostream.h>
#include<iomanip.h>
#include<string.h>
#include<math.h>
#define N 26
char str[1000]; //输入字符串数组
char decode_str[1000]; //译出字符串数组
char string[N]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
double probability[N]={0.1,0.1,0.1,0.1,0.1,0.1,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02};
double result,interval_begin,interval_end;
int cord[1000],cordlength=0,cordlength_temp[1000];
int strlength=0; //输入字符串的长度
result存储每一分组中最终区间所选择出的数;interval_begin为区间下限,interval_end为区间上限;cord[1000]数组存储编码的码字,每一分组编码之后放入其中;cordlength为已经编码的长度,配合每一分组编码之后存入cord[1000]的操作;cordlength_temp数组存储每一分组中编码的长度,同样起配合每一分组编码之后存入cord[1000]的操作的作用,同时也为译码作准备。即cordlength_temp数组中的数据由编码产生,为译码服务。
读数据程序如下:
bool readdata(){
int i;
cout<<"***********固定模式算术编码***********"<<endl;
cout<<"请输入字符串(a-z):"<<endl;
cin>>str;
while(str[strlength]!='\0'){
strlength++;
}
for(i=0;i<strlength;i++) //判断输入是否合法
if(str[i]>'z'||str[i]<'a')
return 1;
cout<<"输入字符串长度为:"<<strlength<<endl<<endl;
return 0;
}
编码程序如下:
void encode(){
int m=1;
double w,length;
cout<<"编码:"<<endl<<endl;
for(int k=0;k<strlength;k=k+8){
cout<<"第"<<m<<"组编码:"<<endl;
w=0.0;
interval_begin=0.0;
interval_end=1.0;
for(int i=k;i<k+8;i++){
if(str[i]=='\0'){
break;
}
int n=str[i]-'a';
w=0.0;
for(int j=0;j<n;j++)
w+=probability[j]; //计算所在区间
length=interval_end-interval_begin; //计算当前区间长度
interval_begin+=length*w; //计算新的区间下限
interval_end=interval_begin+length*probability[j]; //计算新的区间上限
}
result=interval_begin*0.01+interval_end*0.99; //选择适当的点
cordlength=cordlength+int(-log(interval_end-interval_begin)/log(2))+1;
cordlength_temp[m-1]=int(-log(interval_end-interval_begin)/log(2))+1;
//将临时片段编码长度存入cordlength_temp数组
cout<<"编码选取的数为:"<<setprecision(16)<<result<<endl;
cout<<"编码位数为:"<<cordlength_temp[m-1]<<endl;
cout<<"编码结果为:"<<endl;
for(int j=0;j<cordlength_temp[m-1];j++){ //十进制转换成二进制
cord[j+cordlength-cordlength_temp[m-1]]=int(result*2);
//将每一位编码利用cordlength_temp[m-1]长度值以及已编码cordlength值存储在cord数组中,每次循环跨越前面一组的编码长度
result=result*2-cord[j+cordlength-cordlength_temp[m-1]];
cout<<cord[j+cordlength-cordlength_temp[m-1]];
}
cout<<endl<<endl;
m++;
}
cout<<"总编码位数为:"<<cordlength<<endl;
for(int i=0;i<cordlength;i++)
cout<<cord[i];
cout<<endl<<endl;
}
译码程序如下(此时需要用到编码产生的cordlength_temp数组):
void decode(){
int m=1;
int temp,number=0;
double length,wei;
cordlength=0;
cout<<"译码:"<<endl<<endl;
for(int k=0;k<strlength;k=k+8){
cout<<"第"<<m<<"组译码:"<<endl;
wei=0.5;
result=0.0;
for(int i=0;i<cordlength_temp[m-1];i++){ //二进制转换成十进制
result+=wei*cord[i+cordlength];
wei*=0.5;
}
cordlength+=cordlength_temp[m-1];//得出已经译码的长度
cout<<"译码选取的数为:"<<setprecision(16)<<result<<endl;
interval_begin=0.0;
interval_end=1.0;
wei=0.0;
cout<<"译码结果为:";
for(i=k;i<k+8;i++){
if(str[i]=='\0'){
break;
}
temp=0;
wei=0.0;
length=interval_end-interval_begin;
while(result-interval_begin>wei*length){
wei+=probability[temp++];
} //搜索所在区间
temp--;
interval_end=interval_begin+wei*length; //计算新的区间上限
interval_begin=interval_end-probability[temp]*length; //计算新的区间下限
cout<<string[temp];
decode_str[number++]=string[temp];
}
m++;
cout<<endl<<endl;
}
cout<<"总译码结果为:";
for(int i=0;i<number;i++)
cout<<decode_str[i];
cout<<endl;
}
调用主函数:
void main(){
if(readdata())
cout<<"字符输入错误!";
else{
encode();
decode();
}
}
结果为: