三种变长编码方法的 C 实现

信息编码是数据压缩的的基础理论。常用的变长编码法有三种:香农(Shannon)编码,费诺(Fano)编码,霍夫曼(Huffman)编码。

通常情况下,霍夫曼编码法的编码效率最优。

1.香农编码法

香农编码法是一种很基础的编码方法,效率很低。

方法如下:

  1. 将M个信源按其概率递减顺序排列

  2. 计算各个消息的计算码字长度:-log q(Xm)向上取整

  3. 计算地 m 个消息的累加概率并转换成二进制

  4. 取小数点后第 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年,在他介绍信息理论的文章“通信数学理论”中被提出的,归功于费诺,是由于他在不久以后以技术报告发布了它。

方法如下:

  1. 将M个信源按其概率递减顺序排列

  2. 对消息集按概率大小分解成两个子集,使两子集概率之和尽可能相等

  3. 将第一个子集编码为“0,第二个编码为”1“

  4. 对子集进行递归操作,知道子集仅含一个消息

  5. 将逐次分解过程中的码元排列起来即为各消息码字

香农-费诺编码法的C语言实现:

3.霍夫曼编码法

香农当然知道 Shannon-Fano 编码法不是最优的,果然没太久,费诺的学生霍夫曼就找到一种更优的编码方法。

有过算法课程上霍夫曼树的经验,霍夫曼编码法就比较容易理解。每次选取最小的节点构造霍夫曼树,各消息的码字即为从根节点到该消息节点的码元组合。

霍夫曼编码法的C语言实现:

Last updated