课设复习之信息论固定算术编码与译码

 

香农三大定理复习,转自https://baike.baidu.com/item/%E9%A6%99%E5%86%9C%E4%B8%89%E5%A4%A7%E5%AE%9A%E7%90%86/9029931?fr=aladdin

香农三大定理是信息论的基础理论。香农三大定理是存在性定理,虽然并没有提供具体的编码实现方法,但为通信信息的研究指明了方向。香农第一定理是可变长无失真信源编码定理。香农第二定理是有噪信道编码定理。香农第三定理是保失真度准则下的有失真信源编码定理。

 

香农第一定理(可变长无失真信源编码定理)

设离散无记忆信源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();
	}
}

结果为:

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值