让我们看一下关于霍夫曼编码的稍微不同的方式。
假设您有一个由三个符号A,B和C组成的字母,概率分别为0.5、0.25和0.25。因为概率都是2的反幂,所以它具有最佳的霍夫曼码(即与算术编码相同)。在此示例中,我们将使用规范代码0、10、11。
假设我们的国家是一个大的整数,我们称之为。您可以将编码视为一个接受当前状态,一个要编码的符号并返回新状态的函数:s
encode(s,A)encode(s,B)encode(s,C)=2s=4s+2=4s+3
因此,让我们从状态11(二进制为1011)开始,对符号B进行编码。新状态为46(二进制为101110)。如您所见,这是“旧”状态,序列10添加到末尾。我们基本上已经“输出”了位序列10。
到目前为止,一切都很好。
现在考虑一下算术编码是如何工作的。如果将概率放在一个公分母上,则符号A实际上表示范围,符号B表示范围[2[04,24),符号C表示范围[3[24,34)。[34,44)
基本上,我们在这里所做的就是将所有内容都乘以公分母。想象一下,状态实际上是在基数4中。对符号B进行编码实际上是在该基数中输出数字2,对符号C进行编码实际上是在该基数中输出数字3。
但是,符号A有所不同,因为它不是以4为底的整数。
相反,我们可以将字母视为具有相等概率的符号A_0,A_1,B,C的集合。再次具有最佳霍夫曼代码00、01、10、11。或者再次,我们可以在基数4中考虑这一点。要对符号进行编码,我们只需执行以下操作:
encode(s,A0)encode(s,A1)encode(s,B)encode(s,C)=4s+0=4s+1=4s+2=4s+3
因此,现在很清楚如何对符号B和C进行编码,但是对于编码符号A,我们可以选择。我们应该使用和A 1中的哪个?A0A1
现在这是一个聪明的主意:我们从状态窃取一点信息:s
我=小号模2
s′=⌊s2⌋
i=smod2
然后。encode(s′,Ai)
s=11s′=5i=1encode(5,A1)=4×5+1=21
现在,它不会产生与霍夫曼编码完全相同的位输出,但是会产生具有相同长度的输出。我希望您能看到的是,它也是唯一可解码的。为了解码符号,我们将除以4的余数取整。如果值为2或3,则符号分别为B或C。如果它是0或1,则符号是A,然后我们可以通过将状态乘以2并加上0或1来放回信息位。
3525
encode(s,A0)encode(s,A1)encode(s,A2)encode(s,B0)encode(s,B1)=5s+0=5s+1=5s+2=5s+3=5s+4
s′=⌊s3⌋i=smod3encode(s′,Ai)
这等效于算术编码。它实际上是称为非对称数字系统的一系列方法,由Jarek Duda在过去几年中开发。名称的含义应该很明显:以概率编码符号pq
之所以使用一系列编码方法,是因为我们在这里看到的内容本身是不切实际的。它需要进行一些修改以处理以下事实:您可能没有无限精度的整数来有效地操作状态变量,并且可以通过多种方法来实现。当然,算术编码在状态上也有一个类似的问题。
实用的变体包括rANS(“ r”表示“比率”)和tANS(“表驱动”)。
与算术编码相比,ANS在实用和理论上都具有一些有趣的优点:
- 与算术编码不同,“状态”是一个单词,而不是一对单词。
- 不仅如此,ANS编码器及其相应的解码器具有相同的状态,并且它们的操作是完全对称的。这带来了一些有趣的可能性,例如您可以交错不同的编码符号流,并使所有内容完美同步。
- 当然,实际的实现需要在输出时“输出”信息,而不只是将其收集为大整数以在末尾写入。但是,可以配置“输出”的大小,以换取(通常适度的)压缩损失。因此,在算术编码器必须一次输出一位的情况下,ANS可以一次输出一个字节或一个半字节。这使您可以直接在速度和压缩之间进行权衡。
- 在当前的硬件上,它看起来与二进制算术编码一样快,因此与霍夫曼编码具有竞争优势。这使其比大字母算术编码及其变体(例如范围编码)快得多。
- 它似乎是无专利的。
我认为我再也不会做算术编码了。