三种变长编码方法的 C 实现
信息编码是数据压缩的的基础理论。常用的变长编码法有三种:香农(Shannon)编码,费诺(Fano)编码,霍夫曼(Huffman)编码。
通常情况下,霍夫曼编码法的编码效率最优。
1.香农编码法
香农编码法是一种很基础的编码方法,效率很低。
方法如下:
将M个信源按其概率递减顺序排列
计算各个消息的计算码字长度:
-log q(Xm)向上取整计算地 m 个消息的累加概率并转换成二进制
取小数点后第 m 个消息码字长度位即为该消息码字
香农编码法的C语言实现:
// shannon.c
// 编译指令:gcc shannon.c -lm
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main() {
int i, j, n;
printf("请输入信源符号个数:");
scanf("%d", &n);
printf("请输入各符号的概率:");
double x[n];
for (i = 0; i < n; i++) {
printf("x[%d]= ", i);
scanf("%lf", &x[i]);
}
// 选择排序(降序)
for (i = 0; i < n-1; i++) {
double v;
for (j = i+1, v = x[j]; v > x[j-1] && j >= 1; j--) {
x[j] = x[j-1];
}
x[j] = v;
}
// 计算码长:1-log2(p(xi))向上取整
int k[n];
for (i = 0; i < n; i++) {
k[i] = -log(x[i]) / log(2) + 1;
if (k[i] == (-log(x[i]) / log(2) + 1)) k[i] -= 1;
}
// 累加概率
double pa[n];
pa[0] = 0.0;
for (i = 1; i < n; i++) {
pa[i] = pa[i-1] + x[i-1];
}
// 将累加概率转换为二进制
char code[n][n];
for (i = 0; i < n; i++) {
double t = pa[i];
for (j = 0; j < k[i]; j++) {
double temp = 2*t;
if (temp > 1) {
code[i][j] = '1';
t = 2*t - 1;
} else {
code[i][j] = '0';
t = 2*t;
}
}
}
// 输出结果
printf("%16s %12s %16s %4s %4c%s\n", "信源", "概率p(x)", "累加概率", "码长", ' ', "码字code");
for (i = 0; i < n; i++) {
printf("%12d %12lf %12lf %4d %4c", i+1, x[i], pa[i], k[i], ' ');
for (j = 0; j < k[i]; j++) {
printf("%c", code[i][j]);
}
printf("\n");
}
return 0;
}2.费诺编码法
这个编码方法准确说应该叫做 Shannon-Fano 编码法。这项技术是香农于1948年,在他介绍信息理论的文章“通信数学理论”中被提出的,归功于费诺,是由于他在不久以后以技术报告发布了它。
方法如下:
将M个信源按其概率递减顺序排列
对消息集按概率大小分解成两个子集,使两子集概率之和尽可能相等
将第一个子集编码为“0,第二个编码为”1“
对子集进行递归操作,知道子集仅含一个消息
将逐次分解过程中的码元排列起来即为各消息码字
香农-费诺编码法的C语言实现:
3.霍夫曼编码法
香农当然知道 Shannon-Fano 编码法不是最优的,果然没太久,费诺的学生霍夫曼就找到一种更优的编码方法。
有过算法课程上霍夫曼树的经验,霍夫曼编码法就比较容易理解。每次选取最小的节点构造霍夫曼树,各消息的码字即为从根节点到该消息节点的码元组合。
霍夫曼编码法的C语言实现:
Last updated