什么是算术编码

文章对比了哈夫曼编码和算术编码两种数据压缩方法。哈夫曼编码通过构建哈夫曼树为高频字符分配短编码,但可能存在编码长度不精确的问题。算术编码则是通过概率区间划分实现更精确的压缩,尤其在处理连续字符的概率分布时更有效。算术编码能更接近信息熵的压缩极限,从而提供更高效的压缩效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前在学习数据结构的时候有学习过哈夫曼编码,最近看到算术编码,于是了解了一下。

很多压缩算法的思想其实都是和哈夫曼编码是一致的,都是通过VLC(可变长编码)来实现的,算术编码的编码方式则更加精妙。

1.哈夫曼编码

哈夫曼编码的思想就是:对于出现频率更高的符号,使用更短的编码,这样就可以进行大幅度的压缩。

比如对于一段英文字符:AABABCABAB,A出现5次,B出现4次,C出现1次。

如果使用定长编码来进行编码的话,每个符号至少需要2bit来表示(三个字符,需要两位二进制)

如 A:01,B:10,C:11

那么整段的编码长度则为5*2+4*2+1*2=20bit

如果采用哈夫曼编码的话,对于出现频次更高的A,可以使用更短的编码。通过构造哈夫曼树来生成哈夫曼编码:

 则 A:0, B:10, C:11

整段字符的编码长度为:5*1 + 4*2 + 1*2 = 15 bit。可见提高了压缩的效率。

不足:

根据香农的熵计算公式,整个信息序列的熵为:

H(X) = -( 0.5 * log2(0.5) + 0.4 * log2(0.4) + 0.1 * log2(0.1) ) = 1.361

也就是说这段字符的压缩极限是:平均每个符号用 1.361  bit 表示。整段字符一共 10 个字母,压缩极限为 10 * 1.361 = 13.61  bit,而采用哈夫曼编码时,只压缩到了 15  bit。

原因:

主要是由于哈夫曼编码是采用整数进行符号编码的,不够精准,对于B和C这两个字符,它们出现的频率分别为0.4和0.1,但是采用相同的长度进行编码。

2.算术编码

算术编码的本质,也是对于高频的字符进行短编码,但是它的实现方式,和哈夫曼编码完全不同。

对于上文提到的同样的序列:AABABCABAB

算术编码会对 0 -1 这个区间进行划分。

A : [0, 0.5) , B : [0.5, 0.9) , C : [0.9, 1)

AABABCABAB 的第 1 个字符为:A,那么我们选中了 A 的区间 [0, 0.5) 作为新的目标区间。

对新目标区间,再按照 ABC 的概率占比进行划分。

A : [0, 0.25) , B : [0.25, 0.45) , C : [0.45, 0.5)

AABABCABAB 的第 2 个字符仍然为:A,那么我们再选中了 A 的区间 [0, 0.25) 作为新的目标区间。

对新目标区间,再按照 ABC 的概率占比进行划分。

A : [0, 0.125) , B : [0.125, 0.225) , C : [0.225, 0.25)

AABABCABAB 的第 3 个字符为:B,那么这次我们选中了 B 的区间 [0.125, 0.225) 作为新的目标区间。

对新目标区间,再按照 ABC 的概率占比进行划分。

A : [0.125, 0.175) , B : [0.175, 0.215) , C : [0.215, 0.225)

AABABCABAB 的第 4 个字符为:A,那么我们再选中 A 的区间 [0.125, 0.175) 作为新的目标区间。

重复上面的操作,一直到最后一个字符。

 完成上面的操作后,最终的目标区间为:[0.1686, 0.16868),我们在这个区间内,任意选一个小数,便可以作为最终的编码小数。但是计算机只能识别 0 和 1,所以我们再将小数转成二进制。

因为是最短压缩,所以要从 [0.1686, 0.16868) 选一个二进制表示最短的小数。这里我们选定 0.16864013671875,二进制为:0.00101011001011,去掉整数位 0 以及小数点后,最终的二进制编码为 00101011001011,bit 长度为 14 位,比哈夫曼编码要更短 1 位。

关于选择0.16864013671875

在结果区间里找“最短”的二进制小数,具体如下,区间转换为二进制

[0.0010101100101001010111101001111000011011000010001001101

,0.001010110010111010011100110010110111110101000001011101)

取相同部分,直到不相同为止,0.0010101100101 结尾补1(一定会在区间)就是这个值0.16864013671875

3.解码过程

理解了编码过程,那么解码过程也就很容易理解。比如上面我们最终二进制编码为 00101011001011,加上小数点后还原为 0.00101011001011,对应的十进制编码小数是 0.16864013671875。

我们先从初始区间中定位第一个字符:

A : [0, 0.5) , B : [0.5, 0.9), C : [0.9, 1)

0.16864013671875 位于 A 区间,所以第一个字符为 A。我们接着对 A:[0, 0.5) 再进行划分。

A : [0, 0.25) , B : [0.25, 0.45), C : [0.45, 0.5)

0.16864013671875 仍然位于 A 区间,所以第二个字符仍然为 A。我们接着对 A:[0, 0.25) 再进行划分。

A : [0, 0.125) , B : [0.125, 0.225), C : [0.225, 0.25)

0.16864013671875 位于 B 区间,所以第三个字符为 B。我们接着对 B:[0.125, 0.225)再进行划分。

A : [0.125, 0.175) , B : [0.175, 0.215) , C : [0.215, 0.225)

0.16864013671875 位于 A 区间,所以第四个字符为 A。

依次类推,我们可以从 0.16864013671875 将整个字符解码出来。

4.算术编码为什么可以压缩数据

算术编码的压缩本质,就是在保留字符排列顺序的同时,对于更高频出现的字符,也就是概率更大的字符,赋予更大的小数区间。

最终目标区间的范围更大,可容纳的小数精度就越低,意味者我们最终的二进制编码更短。比如在低精度的 [0.1, 0.2) 和 高精度的 [0.1111111111, 0.1111111112) 之间各找一个最短编码的二进制进行比较,肯定是 [0.1, 0.2) 中找到的的最短二进制编码更短。

所以算术编码的实现途径就是:尽量使最终目标区间的范围更大。

由于高频字符出现次数多,区间较大,而低频字符出现次数少,区间小。所以在遍历完所有字符之后,我们最终得到目标区间就更大,也就是小数精度更低。

5.总结

通过上面的例子,清楚了算术编码的原理:

  1. 为了使最终二进制编码更短,就需要使得最终目标区间的范围更大。
  2. 为了使最终目标区间的范围更大,就需要赋予高频字符更大的区间,低频字符更小的区间。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值