一、香农编码的概念

概念:

香农编码是是采用信源符号的累计概率分布函数来分配字码的。香农编码是根据香农第一定理直接得出的,指出了平均码长与信息之间的关系,同时也指出了可以通过编码使平均码长达到极限值。香农第一定理是将原始信源符号转化为新的码符号,使码符号尽量服从等概分布,从而每个码符号所携带的信息量达到最大,进而可以用尽量少的码符号传输信源信息。

香农编码属于不等长编码,通常将经常出现的消息变成短码,不经常出现的消息编成长码,从而提高通信效率。

二、问题分析

① 由于总是进一取整,香浓编码方法不一定是最佳的。

② 由于第一个消息符号的累加概率总是0,故它对应得分码字总是0、00、000、0……0的式样。

③ 码字集合是唯一的,且为即时码。

④ 先有码长再有码字。

⑤ 对于一些信源,编码效率不高,信源冗余度稍大,因此其实用性受到较大限制。

三、算法实现及流程图

算法基本步骤:

1) 将q个信源符号按概率递减的方式进行排序:P1≥P2≥……Pq。

2) 按式-logP(Si)≤li≤1-logP(Si)(i=1,2,……q),计算出每个信源符号的码长li。  

3) 为编成唯一可译码,计算第i个信源符号的累加概率:。  

4) 将累加概率Gi用二进制表示。  

5) 取Gi对应二进制数的小数点后li位构成该信源符号的二进制码字。

 

四、运行结果

 

五、学习心得

香农编码本质上就是一个信源概率组进行一系列的规则运算后得到的二进制数组。

在用C语言编写的香农编码程序中,对于计算编码时用到的各种概率值要有具体的定义,由于进行香农编码时,首先要进行从大到下的概率排序,所以,程序中编辑的降序的部分是必须的,运行结果不仅要求有编码结果,还要有信源熵,平均码长,所以在程序中也应定义这两个值。通过运用香农编码方法进行计算和对香农编码程序的运行,可知,香农编码方法多余度稍大,相较于其他编码方法实用性不大,但香农编码法有重要的理论意义。


package cn.com;
 


 import java.io.Externalizable; 

 import java.util.ArrayList; 

 import java.util.Arrays; 

 import java.util.Collections; 

 import java.util.HashMap; 

 import java.util.List; 

 import java.util.Map; 

 import java.util.Scanner; 



 public class Shannon { 

/** 

* 将概率从大到小排序 

* @param p 输入的概率数组 

* @return  

*/ 

public double[] Sort(double[] p){ 

double[] p1 = new double[p.length]; 

for(int i = 0; i < p1.length; i++){ 

p1[i] = p[i]; 

} 

Arrays.sort(p1); 

for(int i = 0, j = p.length-1; i < p.length/2; i++, j--){ 

double temp = p1[i]; 

p1[i] = p1[j]; 

p1[j] = temp; 

} 

return p1; 

} 

 

/** 

* 验证概率和是否为1,合格为true,否则false 

* @param p 输入的概率数组 

* @return 

*/ 

public boolean varification(double[] p){ 

boolean bool = false; 

double d = 0; 

for(int i = 0; i < p.length; i++){ 

d+=p[i]; 

} 

if(d - 1.0 < 0.000001){ 

return true; 

} 

return bool; 

} 

 

/** 

* 计算每个信源的码长 

* @param p 信源的概率 

* @return 

*/ 

public int[] codeLength(double[] p){ 

int[] Li = new int[p.length]; 

double temp = 0; 

for(int i = 0; i < Li.length; i++){ 

temp = - (Math.log(p[i]) / Math.log(2.0)); 

Li[i] = (int)Math.ceil(temp); 

} 

return Li; 

} 

 

/** 

* 计算信源的熵 

* @param p 信源的概率数组 

* @return 

*/ 

public double entropyAll(double[] p){ 

double H = 0; 

for(int i = 0; i < p.length; i++){ 

H-=(p[i] * (Math.log(p[i]) / Math.log(2.0))); 

} 

 

return H; 

} 

 

/** 

* 计算平均码长 

* @param p 信源的概率数组 

* @param Li 每个信源的码长数组 

* @return 

*/ 

public double averageCodeLength(double[] p, int[] Li){ 

double n = 0; 

for(int i = 0; i < p.length; i++){ 

n+=p[i] * Li[i]; 

} 

 

return n; 

} 

 

/** 

* 计算累加概率 

* @param p 信源的概率数组 

* @return 

*/ 

public double[] accumulation(double[] p){ 

double[] q = new double[p.length]; 

q[0] = 0; 

 

for(int i = 1; i < p.length; i++){ 

q[i] = q[i-1] + p[i-1]; 

} 

return q; 

} 

 

/** 

* 计算二进制编码 

* @param acl 累加概率数组 

* @param cl 每个信源的码长数组 

* @return 

*/ 

public int[][] binaryMode(double[] acl, int[] cl){ 

int[][] c = new int[acl.length][]; 

for(int i = 0; i < acl.length; i++){ 

c[i] = new int[cl[i]]; 

} 

 

double[] a = new double[acl.length+1]; 

for(int i = 0; i < acl.length; i++){ 

for(int j = 0; j < cl[i]; j++){ 

if(j == 0){ 

a[j] = acl[i] * 2; 

}else{ 

a[j] = a[j-1] * 2; 

} 

if(a[j] > 1){ 

c[i][j] = 1; 

a[j] = a[j] - 1; 

}else{ 

c[i][j] = 0; 

} 

} 

} 

 

return c; 

} 

 

 

public static void main(String[] args) { 

Scanner sc = new Scanner(System.in); 

Shannon t = new Shannon(); 

System.out.println("输入信源的个数:"); 

int num = sc.nextInt(); 

double[] p = new double[num]; 

System.out.println("输入概率:"); 

for(int i = 0; i < p.length; i++){ 

p[i] = sc.nextDouble(); 

} 

if(!t.varification(p)){ 

System.out.println("输入的概率有误!"); 

return; 

} 

double[] q = t.Sort(p); 

 

int[] cl = t.codeLength(p); 

 

double[] acl = t.accumulation(p); 

int[][] c = t.binaryMode(acl, cl); 

System.out.println("概率              香农编码"); 

for(int i = 0; i < p.length; i++){ 

System.out.print(q[i]+"--->"); 

for(int j = 0; j < cl[i]; j++){ 

System.out.print(c[i][j]); 

} 

System.out.println(); 

} 

double avg = t.averageCodeLength(p, cl); 

double ea = t.entropyAll(p); 

System.out.println("平均码长为:"+avg); 

System.out.println("信源的信息熵为:"+ea); 

System.out.println("编码效率为"+ea/avg); 

 

} 

 }