Huffman编码实验,原理弄懂了,源代码不是很懂,挖个坑,以后用到再细读。
实验原理
在众多的无失真信道编码技术中,Huffman编码是一种有效的获得最佳码的编码技术。它能够充分利用短码,大幅度降低码字的平均码长,从而获得较高的编码效率,在保证码字的可分离性的同时,有效的提高了通信系统的有效性。也正是由于Huffman编码技术的优越性,目前在有关信源编码的许多领域中,Huffman编码作为一项基本技术,得到了极为广泛的应用。
(一)Huffman编码方法
由于目前数字通信中一般都使用二进制符号,因此二进制的Huffman编码技术最为普遍,其编码步骤如下:
1、将信源符号按概率从大到小进行排列;
2、给两个概率最小的信源符号各分配一个码元“0”和“1”,然后将这两个信源符号合并成一个新符号,并用这两个最小的概率之和作为新符号的概率,结果得到一个只有(n-1)个信源符号的新信源(假设原来所需编码的符号数为n),称为信源的第一次缩减信源S1;
3、将缩减信源S1的符号仍按概率从大到小的顺序进行排列,重复步骤2,得到只含(n-2)个符号的缩减信源S2;
4、重复上述步骤,直至缩减信源只剩两个符号为止,此时所剩两个符号的概率之和必为1,将这两个符号各分配一个码元“0”和“1”后,从最后一级缩减信源开始,依编码路径向前返回,就得到各信源符号所对应的Huffman码字。
(二)方差最小的Huffman码字
缩减信源时,若合并后的新符号概率与其他符号的概率相等,从编码方法上来讲,这几个符号的次序可任意排列,编出的码字都是正确的,但不同的编法得到的码字不同,码字长度也不尽相同,从而码长的方差也会不同。若码长的方差较小,则意味着码长变化较小,各个码长都比较接近于平均码长,这样对于信息传输的实现,特别是对传输有实时性要求的时候,会带来很大的便利,因此也就更好一些。
为了得到方差最小的Huffman码字,在对缩减信源按概率从大到小重新排列时,应使合并后的新符号尽可能地排在靠前的位置,这样可使合并后的新符号重复编码次数减少,使短码得到充分利用,降低码长的方差。
(三)递归算法实现Huffman编码
由于在Huffman编码过程中,每次都是对减少一个符号数的缩减信源进行同样的操作——排序与合并,且最终的码字是按照编码路径逆序返回而得到,因此可利用递归的思想实现Huffman的编码程序。
假设对n个符号、概率为pi的信源进行Huffman编码,则递归实现算法的主要思想可简单描述为:
procedure Huffman(n,{pi})
if n= = 2 then
对两个符号各分配一个码元“0”和“1”;
else
降序排序;
合并符号,并得到新的概率分布pi′;
Huffman(n-1,{ pi′});
对被合并的两个符号各分配一个码元“0”和“1”;
endprocedure
(四)动态分配内存
由于本程序设计要求根据所输入的信源符号数与概率分布进行Huffman编码,在输入之前并不确定所要编码的信源符号数,而且编得的码字长度也不能确定,因此对各符号以及相应码字的存储使用固定长度的数组显然不太合适,而在C语言中使用动态分配内存技术则可以根据需要灵活分配存储空间,从而有效的解决这个问题。
程序代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#definedelta 1.0e-6
voidsort(float *,char * *,int *,int);
voidcode(float *,char * *,int *,int);
voidmain()
{
float *p,*p_i;
float sum;
char * * c;
int *idx;
int q;
int i,j;
int l1,l2;
char * s;
float t;
int idx_t;
printf("input the number of thesymbols:\n");
scanf("%d",&q);
idx=(int *)calloc(q,sizeof(int));
p=(float *)calloc(q,sizeof(float));
p_i=(float *)calloc(q,sizeof(float));
c=(char * *)calloc(q,sizeof(char *));
for(i=0;i<q;i++)
{
c[i]=(char *)calloc(1,sizeof(char));
c[i][0]='\0';
}
sum=0.0;
for(i=0;i<q;i++)
{
printf("input the probabilityof x[%d]:\n",i);
scanf("%f",&p[i]);
p_i[i]=p[i];
idx[i]=i;
sum+=p[i];
}
if((sum-1.0)>delta||(1.0-sum)>delta)
{
printf("the probabilities error!!\n");
exit(-1);
}
for (i=0;i<q;i++)
{
for (j=0;j<q-i;j++)
{
if(p[j]<p[j+1])
{
t=p[j];
p[j]=p[j+1];
p[j+1]=t;
idx_t=idx[j];
idx[j]=idx[j+1];
idx[j+1]=idx_t;
l1=strlen(c[j]);
l2=strlen(c[j+1]);
s=(char *)calloc(l1+1,sizeof(char));
strcpy(s,c[j]);
realloc(c[j],l2+1);
strcpy(c[j],c[j+1]);
realloc(c[j+1],l1+1);
strcpy(c[j+1],s);
free(s);
}
}
}
code(p,c,idx,q);
for(i=0;i<q;i++)
{
printf("x[%d]:probability=%.6f code=%s\n",idx[i],p_i[idx[i]],c[i]);
}
free(c);
free(p);
free(p_i);
free(idx);
}
voidsort(float * p,char * * c,int * idx,int q)
{
int i,j;
int l1,l2;
char * s;
float t;
t=p[q-1];
j=idx[q-1];
l1=strlen(c[q-1]);
s=(char *)calloc(l1+1,sizeof(char));
strcpy(s,c[q-1]);
i=q-2;
while((i>=0)&&(t>=p[i]))
{
p[i+1]=p[i];
idx[i+1]=idx[i];
l2=strlen(c[i]);
realloc(c[i+1],l2+1);
strcpy(c[i+1],c[i]);
i--;
}
p[i+1]=t;
idx[i+1]=j;
realloc(c[i+1],l1+1);
strcpy(c[i+1],s);
free(s);
}
voidcode(float * p,char * * c,int * idx, int q)
{
int l1,l2;
int idx1,idx2;
char * s;
if(q==2)
{
l1=strlen(c[0]);
l2=strlen(c[1]);
realloc(c[0],l1+2);
realloc(c[1],l2+2);
strcat(c[0],"0");
strcat(c[1],"1");
}
else
{
p[q-2]=p[q-1]+p[q-2];
idx1=idx[q-2];
idx2=idx[q-1];
sort(p,c,idx,q-1);
code(p,c,idx,q-1);
for (int i=0;i<q;i++)
{
if(idx[i]==idx1)
{
idx1=i;
break;
}
}
for (int j=0;j<q;j++)
{
if (idx[j]==idx2)
{
idx2=j;
break;
}
}
l1=strlen(c[idx1]);
l2=strlen(c[idx2]);
s=(char *)calloc(l1+2,sizeof(char*));
strcpy(s,c[idx1]);
realloc(c[idx1],l1+2);
strcpy(c[idx1],s);
strcat(c[idx1],"0");
realloc(c[idx2],l2+2);
strcpy(c[idx2],s);
strcat(c[idx2],"1");
free(s);
}
}