算术编码工作原理.(优选)
算术编码
算术编码+ 统计模型= 数据压缩- 第一部分:算术编码作者:Mark Nelson现在通用的大多数数据压缩方法都属于两大阵营之一:基于字典的方案和统计方法。
在小系统世界中,基于字典的数据压缩技术此时似乎更加流行。
不过,通过将算术编码与强大的模型技术结合在一起,数据压缩的统计方法可以真正达到更好的性能。
这篇分成两部分的文章讨论了如何用几个不同的模型方法与算术编码组合以达到一些重大的压缩率。
本文的第一部分详细说明算术编码是如何工作的。
第二部分说明如何开发一些可以使用算术编码的有效模型以生成高性能压缩程序。
钟爱的术语数据压缩通常通过从输入“文本”获取“符号”、处理它们,并将“代码”写入到压缩后的文件来进行运作。
对于本文来说,符号通常是字节,但是他们很可能只是像素、80 位的浮点数或者EBCDIC 字符。
数据压缩方案需要能够将已压缩的文件转换回到与输入文本的一样的拷贝才是有效的。
如果已压缩的文件比输入文本更小,那么不必说,它也是有用的。
基于字典的压缩系统通过用固定长度码来代替输入文本中的一组符号来进行运作。
字典技术的一个众所周知的例子是LZW 数据压缩。
(请参见DDJ 的89 年第10 期中的“LZW 数据压缩”一文)。
LZW 通过通常从9 到16 位大小范围的码来取代本来无限长的字符串来进行运作。
数据压缩的统计方法采取一种完全不同的方法。
它们通过一次编码多个符号来运作。
将符号编码到可变长的输出码中。
输出码的长度根据符号的概率或者频率进行变化。
低概率的符号用较多的位进行编码,并且高概率符号用较少的位进行编码。
实践中,统计和字典方法之间的分界线并不总是那么清晰。
一些方案并不能明显地归为某一个阵营或者另一个,并且总是有一些使用来自两种技术特性的混合方案。
不过,在本文中讨论的方法使用算术编码来实现纯粹的统计压缩方案。
霍夫曼(Huffman)编码:退役的冠军在数据流中只是能够精确地计算字符的概率还不够,我们也需要能有效地利用这个知识的编码方法。
算术编码工作原理
算术编码工作原理在给定符号集和符号概率的情况下,算术编码可以给出接近最优的编码结果。
使用算术编码的压缩算法通常先要对输入符号的概率进行估计,然后再编码。
这个估计越准,编码结果就越接近最优的结果。
例: 对一个简单的信号源进行观察,得到的统计模型如下:∙60% 的机会出现符号中性∙20% 的机会出现符号阳性∙10% 的机会出现符号阴性∙10% 的机会出现符号数据结束符. (出现这个符号的意思是该信号源'内部中止',在进行数据压缩时这样的情况是很常见的。
当第一次也是唯一的一次看到这个符号时,解码器就知道整个信号流都被解码完成了。
)算术编码可以处理的例子不止是这种只有四种符号的情况,更复杂的情况也可以处理,包括高阶的情况。
所谓高阶的情况是指当前符号出现的概率受之前出现符号的影响,这时候之前出现的符号,也被称为上下文。
比如在英文文档编码的时候,例如,在字母Q 或者q出现之后,字母u出现的概率就大大提高了。
这种模型还可以进行自适应的变化,即在某种上下文下出现的概率分布的估计随着每次这种上下文出现时的符号而自适应更新,从而更加符合实际的概率分布。
不管编码器使用怎样的模型,解码器也必须使用同样的模型。
一个简单的例子以下用一个符号串行怎样被编码来作一个例子:假如有一个以A、B、C三个出现机会均等的符号组成的串行。
若以简单的分组编码会十分浪费地用2 bits来表示一个符号:其中一个符号是可以不用传的(下面可以见到符号B正是如此)。
为此,这个串行可以三进制的0和2之间的有理数表示,而且每位数表示一个符号。
例如,“ABBCAB”这个串行可以变成0.011201(base3)(即0为A, 1为B, 2为C)。
用一个定点二进制数字去对这个数编码使之在恢复符号表示时有足够的精度,譬如0.001011001(base2) –只用了9个bit,比起简单的分组编码少(1 – 9/12)x100% = 25%。
这对于长串行是可行的因为有高效的、适当的算法去精确地转换任意进制的数字。
算数编码的原理
算数编码的原理
算术编码是一种无损数据压缩算法,它通过将整个数据序列映射到一个连续的数值区间来实现压缩。
算术编码的原理可以概括为以下几个步骤:
确定符号集:确定待编码的符号集,这可以是字符、像素值或其他离散符号的集合。
计算符号概率:对于每个符号,计算其在待编码数据中出现的概率。
通常使用统计方法或概率模型进行估计。
构建累积概率表:根据符号概率计算符号的累积概率。
累积概率表示为每个符号之前的概率总和。
映射到区间:将待编码数据序列中的每个符号映射到一个区间,该区间是 [0, 1) 上的一个子区间。
初始区间为整个区间 [0, 1)。
缩小区间:根据每个符号的累积概率表和当前区间,将当前区间缩小为表示下一个符号的子区间。
缩小区间的过程可以通过二分搜索或线性插值来实现。
重复步骤 5:对于待编码数据序列中的每个符号,重复步骤 5,不断缩小当前区间。
输出编码结果:最终,将最后一个符号所对应的子区间输出为编码结果。
这个子区间可以用一个二进制码或其他形式的码字来表示。
解码过程与编码过程相反,它将编码结果映射回原始数据序列。
解码过程需要使用与编码过程相同的符号概率和累积概率表。
算术编码的优势在于它可以实现较高的压缩比,因为它能够有效地利用符号的概率信息。
然而,算术编码的实现相对复杂,需要对概率进行准确的估计,并且在解码过程中需要高精度的计算。
二进制算术编码 原理
二进制算术编码原理
二进制算术编码是一种无损数据压缩算法,它可以用来压缩离散符号序列。
其原理如下:
1. 编码器使用一个当前编码范围来表示待编码的符号序列。
初始时,该范围是[0, 1),表示整个编码空间。
2. 对于每个输入符号,编码器将当前编码范围按照符号的概率划分为不重叠的子范围。
概率较大的符号对应的子范围会占据较大的编码范围。
3. 编码器将当前编码范围缩小为对应子范围,并重复步骤2,
直到处理完输入符号序列。
4. 最后,编码器输出编码范围的任意点作为压缩后的二进制码。
解码时,解码器依照与编码器相同的原理,将输入的二进制码逐步解码为符号序列。
解码过程中,解码器根据已解码的前缀确定符号范围,并将该范围划分为对应的子范围。
最终,解码器输出解码结果。
二进制算术编码的优点是可以实现接近于香农定理的压缩率,即接近于输入数据的信息熵。
然而,二进制算术编码的实现较为复杂,需要进行大量的浮点数计算,因此在实际应用中可能会选择其他更简单的压缩算法。
二进制算术编码
二进制算术编码一、引言二进制算术编码是一种数字编码方式,它将数字转换为二进制数,并通过算术运算来表示数字。
这种编码方式在计算机科学中得到广泛应用,因为计算机只能处理二进制数。
本文将介绍二进制算术编码的原理、应用和优缺点。
二、原理二进制算术编码的原理是将数字转换为二进制数,并通过算术运算来表示数字。
例如,将数字5转换为二进制数101,将数字7转换为二进制数111。
然后,通过加、减、乘、除等算术运算来表示数字。
例如,将数字5和数字7相加,可以得到二进制数1010,表示数字12。
三、应用二进制算术编码在计算机科学中得到广泛应用。
它可以用于数据压缩、加密、错误检测和纠正等方面。
例如,压缩数据时,可以使用二进制算术编码来表示数字,从而减少数据的存储空间。
加密数据时,可以使用二进制算术编码来表示数字,从而增加数据的安全性。
错误检测和纠正时,可以使用二进制算术编码来表示数字,从而检测和纠正数据传输中的错误。
四、优缺点二进制算术编码的优点是可以表示任意精度的数字,而且可以进行高精度的算术运算。
它还可以用于数据压缩、加密、错误检测和纠正等方面。
但是,二进制算术编码的缺点是计算复杂度较高,需要进行大量的算术运算。
此外,它还需要占用较大的存储空间。
五、结论二进制算术编码是一种数字编码方式,它将数字转换为二进制数,并通过算术运算来表示数字。
它在计算机科学中得到广泛应用,可以用于数据压缩、加密、错误检测和纠正等方面。
虽然它有一些缺点,但是它的优点远远超过了缺点。
因此,二进制算术编码是一种非常有用的数字编码方式。
python算术编码
python算术编码算术编码是一种常用的数据压缩算法,其主要原理是将数据转化为一个数值范围内的小数,然后将其储存下来。
在数据解压时,根据压缩时使用的转化方法,就能将原始数据准确地还原出来。
算术编码在实际应用中已经被广泛地运用在文件压缩、图像压缩和语音压缩等方面。
本文将从算术编码的原理、实现方式以及应用场景三个层面进行详细介绍。
一、算术编码的原理算术编码的原理是将一个字符串转化为一个小数,该小数对应的是一个数值范围内的一段连续区间。
这个小数的值越接近1,表示原始字符串中的内容就越靠近该区间的顶端,而值越接近0,表示原始字符串的内容越靠近该区间的底端。
在解码时,将该小数从第一位开始与不同的区间进行匹配,就能够还原出原始的字符串。
二、算术编码的实现方式算术编码是一种非常灵活的编码方式,因此在实现方式上也存在多种不同的方法。
其中一种常见的实现方法是基于概率模型的顺序算术编码。
它的具体流程如下:1. 对于每一个字符,统计其在原始字符串中出现的概率。
2. 将每一个字符的概率映射到数值范围内的一个小数区间。
3. 依次将每个字符的小数区间叠加起来,形成一个新的数值范围。
4. 当一个字符对应的小数区间完全包含在新的数值范围内时,就将新的数值范围作为编码结果储存。
5. 重复步骤4,直到所有字符都被编码。
6. 解码时,根据编码结果以及字符串中每个字符对应的概率,重新定位原始字符串中的每个字符。
三、算术编码的应用场景算术编码在实际的应用场景中已经被广泛地使用,其主要优点是可以实现更高的压缩比,以及更加精确的拟合,从而能够表现出更好的压缩效果。
在文件压缩方面,算术编码可以实现更好的压缩效果,并且不需要占用太多的存储空间。
在图像压缩方面,算术编码能够准确地描述图像的数据内容,从而实现更好的压缩效果。
在语音压缩方面,算术编码的灵活性和高效性使其成为了一种不可替代的压缩方式。
总之,算术编码作为一种常用的数据压缩算法,其原理清晰、实现方式多样,并且拥有广泛的应用场景。
二元序列算术编码
二元序列算术编码是一种可逆的映射方法,它将信源序列中的每个符号序列编码成二进制序列。
算术编码的基本原理是将信源符号的概率分布与二进制小数点后L位相对应,利用概率分布信息将信源符号序列编码成二进制小数。
在二元序列算术编码中,首先需要确定二进制小数点后的位数L,然后根据信源符号的概率分布信息,将每个信源符号编码成相应的二进制小数。
具体而言,对于具有m个取值的信源符号,每个符号对应的概率为P(X=x),其中x为信源符号的取值。
根据这些概率值,可以确定每个符号对应的二进制小数范围。
例如,如果P(X=0)=0.7,则二进制小数0.0-0.6对应的符号为0,而二进制小数0.7-1.0对应的符号为1。
完成编码后,可以将得到的二进制小数转换为二进制序列。
需要注意的是,在算术编码中,二进制序列的长度是不确定的,它取决于信源符号的概率分布。
因此,在实际应用中,需要根据具体需求和信源概率分布来确定二进制序列的长度。
总之,二元序列算术编码是一种有效的数据压缩方法,它利用了信源符号的概率分布信息,将信源符号序列编码成二进制序列。
通过算术编码,可以有效地减少数据冗余,提高数据压缩比。
算术编码的原理
算术编码的原理算术编码是一种数据压缩算法,它可以将一个长字符串压缩成一个更短的数值。
它与其他数据压缩算法不同,它不是将整个字符串划分成固定长度的块,而是将每个字符映射为一个数字,再将这些数字压缩成一个数值。
算术编码的原理可以简单地概括为以下几点:1. 确定字符集在压缩之前,必须先确定字符集。
字符集包括所有可能出现的字符。
例如,在英语中,字符集包括所有字母、数字以及其他符号。
2. 计算每个字符的概率通过预处理或对大量数据的统计,可以计算每个字符在字符串中出现的概率。
3. 对每个字符进行编码编码的过程是将每个字符映射为一个数字。
这个数字必须能唯一地表示每个字符,并且尽可能不会出现冲突。
编码的方式可以根据具体情况进行选择,例如 ASCII 码就是一种常见的字符编码方式。
4. 计算每个字符的编码区间每个字符根据其在字符串中出现的概率,可以确定一个编码区间。
例如,一个字符在字符串中出现的概率为 0.25,则其编码区间为 0-0.25。
5. 压缩数据将每个字符的编码区间连续地组成一个区间,最终压缩成一个数值。
如果字符集很大,压缩后得到的数值可能非常大,因此需要使用高精度运算来处理。
6. 解压数据解压数据的过程就是将压缩后的数值还原为原始字符串的过程。
解压的过程需要根据先前编码的字符集和编码区间进行计算,从而还原字符串。
总之,算术编码的原理可以简单概括为确定字符集、计算每个字符的概率、为每个字符编码、计算每个字符的编码区间、压缩数据和解压数据。
虽然算术编码的实现比较复杂,但它可以很好地压缩数据,并且是一种通用的数据压缩算法。
算术编码原理
算术编码原理算术编码是一种将字符序列压缩成一个浮点数的方法,它的压缩效率比传统的哈夫曼编码更高,因为它可以使用原本不是整数的浮点数表示更多的信息。
本文将介绍算术编码的原理,分为以下几个部分:一、概念解释1.1 算术编码的基本概念算术编码是在一个有限长度的区间内对字符序列进行编码的方式,其中每个字符对应的编码值是一个实数。
编码值在编码区间内是唯一的,且编码值可以通过解码得到压缩前的字符序列。
1.2 字符频率在进行算术编码时,需要知道每个字符在字符序列中出现的频率,频率可以是小数或整数,且每个字符的频率之和必须为1。
二、算法过程2.1 编码过程算术编码主要分为以下几个步骤:(1)定义初始编码区间根据字符频率,可以计算出每个字符的编码区间,例如字符A的编码区间是[0,0.3),字符B的编码区间是[0.3,0.5),字符C的编码区间是[0.5,0.6),字符D的编码区间是[0.6,1]。
(2)收缩编码区间根据每个字符的频率,计算每次的编码区间长度。
例如,如果字符序列是ABCD,且字符频率分别为0.3、0.2、0.1和0.4,那么初始编码区间的长度为1,第一次收缩后,区间缩小到0.3,表示字符A出现的概率为0.3。
第二次收缩后,区间缩小到0.06,表示字符AB出现的概率为0.06。
一直推进到最后,得到压缩后的编码值。
2.2 解码过程解码过程需要使用压缩后的编码值和字符频率进行计算。
例如,解码一个值为0.2的字符时,需要找到0.2所在的字符区间,然后计算该区间对应的字符。
三、算法特点3.1 压缩率高由于算术编码可以对每个字符对应的区间进行无限分割,因此可以表示更多的信息。
相比于传统的哈夫曼编码,算术编码可以达到更高的压缩率。
3.2 复杂度高虽然算术编码的压缩效果好,但是编码和解码的计算复杂度非常高,因此需要配合分治法、搜索算法等其它算法来减少计算量。
3.3 广泛应用算术编码在数据压缩、无损图像压缩、音频压缩、视频压缩等多个领域都有广泛应用。
【MQ算术编码原理及实现】算术编码原理
【MQ算术编码原理及实现】算术编码原理随着多媒体技术的不断运用发展,图像压缩要求更高的性能和新的特征。
为了满足静止图像在特殊领域编码的需求,*****0作为一个新的标准处于不断的发展中,这种新的标准更加注重图像的可伸缩表述。
算术编码是一种变长信源编码技术,其卓越性能使其在多媒体领域得到了越来越广泛的应用。
*****0标准中,提高图像压缩性能的关键技术之一就是MQ算术编码。
MQ算术编码器是一种基于上下文的自适应二进制算术编码器,它继承了IBM的ABIC(自适应双层图像压缩)中Q编码器无乘法的近似和位缓存的策略,增加了条件交换和概率估计状态机中的贝叶斯学习过程,是一种高效率物理可实现的压缩编码算法,非常具有研究价值。
2. 算术编码2.1编码原理简述算术编码是一种非分组码。
编码时信源符号序列连续的进入编码器,通过编码器的运算得到连续的输出。
通常算术编码是讲一条信源符号序列映射成一条码序列,这样的码序列有时也称为码字。
算术编码的实质就是,将一条信源信息序列映射到[0,1)区间中的一个子区间,这种映射是一种一一对应关系,以保证唯一译码,然后取这个子区间内的一点所代表的数值作为码字。
只要码长合适,就可以保证唯一可译。
而当信源序列长度足够大时,每信源符号的平均码长接近信源的熵。
虽然其编码效率很高,但仍然存在缺陷。
首先,其运算需要精确的实数加法和乘法,这些运算在有限精度的计算机上实现是非常困难的。
正是这个原因使得算术编码从提出到实际应用相差了近二十年之久。
直到Rissanen和Pasco分别提出了一个先进后出算法和一个先进先出算法,并由此证明了算术编码可以用有限精度处理技术逼近。
Rubin吸收了两个算法的精华,利用有限精度寄存器,讨论了一般算术编码的实现方法。
在此基础上,Witten,Neal和Cleary做了进一步地精细化,并给出了一个完整的C语言程序。
算术编码的另一缺陷是编码速度太低,这是因为编码迭代过程中含有整数乘除运算,这些运算对于软件执行和硬件设计是十分不利的。
python算术编码
python算术编码## 简介这篇原创文档将会介绍算术编码的基本原理和实现。
算术编码是一种无损数据压缩算法,在信息论领域得到广泛应用。
通过使用算术编码,可以将数据压缩成更短的编码序列,从而减少存储空间的需求。
## 基本原理算术编码的基本原理是将输入序列映射到一个实数区间中,并将该区间的特定部分表示为输出序列。
其实质是将较常见的序列映射为较短的编码,而较不常见的序列映射为较长的编码。
通过这种方式,可以实现数据的高度压缩。
具体而言,算术编码的过程如下:1. 将输入序列划分为不同的符号,并为每个符号分配一个概率。
2. 计算每个符号出现的概率区间(其实也可以是累积概率)。
3. 将整个区间根据各个符号的概率进行划分,并将输入序列映射到对应的子区间中。
4. 重复步骤3,直到得到一个唯一的编码序列。
## 实现步骤下面将介绍算术编码的实现步骤。
1. 初始化区间:将整个区间初始设置为[0, 1)。
2. 计算符号概率区间:针对每个输入符号,根据其出现的概率计算对应的区间。
可以通过累积概率或离散概率来计算。
3. 映射到区间:根据符号概率区间,将输入序列映射到对应的子区间中。
例如,如果输入序列为"ABBC",对应的符号概率区间为[0.2, 0.4),则将当前区间更新为子区间[0.2, 0.3)。
4. 缩小区间:重复步骤3,不断缩小区间,直到得到一个唯一的编码序列。
5. 输出编码序列:将最终的区间映射到一个编码序列中,并输出。
## 优缺点算术编码的优点在于其高度压缩的能力,能够将数据压缩到接近信息熵的程度。
另外,算术编码对于任意概率分布的符号集都能进行有效的编码。
然而,算术编码也存在一定的缺点。
首先,算术编码的实现相对复杂,需要进行精确的浮点数计算。
其次,算术编码对输入数据的顺序敏感,即输入序列的顺序不同,得到的编码序列也会不同。
## 总结算术编码是一种强大的数据压缩算法,其利用符号出现的概率将输入序列映射到一个实数区间中,并通过缩小区间来得到一个唯一的编码序列。
python算术编码
python算术编码算术编码是一种常用的无损压缩算法,可以对数据进行高效的编码和解码,以达到数据压缩的目的。
本文将介绍算术编码的原理和实现,以及其在实际应用中的一些注意事项。
1. 算术编码原理算术编码将数据编码为一个区间,该区间表示数据的概率分布。
编码过程中,每个符号根据其出现的概率被分配一个子区间。
编码最后输出的是包含所有符号的区间的编码值。
解码过程则是根据编码值将其映射回原始数据。
2. 算术编码步骤算术编码主要包含以下几个步骤:- 确定每个符号的概率分布。
- 计算每个符号所对应的区间。
- 编码:根据符号和区间,将数据编码为一个编码值。
- 解码:根据编码值和符号的概率分布,将编码值解码为原始数据。
3. 算术编码的实现算术编码的实现需要对概率进行建模,并进行区间的计算。
下面是一个简单的算术编码的实现示例:```pythondef arithmetic_encode(data, symbol_list, probability_list):low = 0high = 1result = 0for symbol in data:symbol_index = symbol_list.index(symbol)symbol_low = low + (high - low) *sum(probability_list[:symbol_index])symbol_high = low + (high - low) *sum(probability_list[:symbol_index + 1])low = symbol_lowhigh = symbol_highresult = (low + high) / 2return resultdef arithmetic_decode(encoded_data, symbol_list, probability_list, length):low = 0high = 1result = []for _ in range(length):for i in range(len(symbol_list)):symbol_low = low + (high - low) * sum(probability_list[:i]) symbol_high = low + (high - low) * sum(probability_list[:i + 1])if symbol_low <= encoded_data < symbol_high:result.append(symbol_list[i])low = symbol_lowhigh = symbol_highbreakreturn result```以上代码中,`arithmetic_encode`函数用于将数据编码为编码值,`arithmetic_decode`函数用于将编码值解码为原始数据。
算术编码
算术编码与译码原理:1、编码过程算术编码方法是将被编码的一则消息或符号串(序列)表示成0和1之间的一个间隔(Interval),即对一串符号直接编码成[0,1]区间上的一个浮点小数。
符号序列越长,编码表示它的间隔越小,表示这一间隔所需的位数就越多。
信源中的符号序列仍然要根据某种模式生成概率的大小来减少间隔。
可能出现的符号概率要比不太可能出现的符号减少范围小,因此,只正加较少的比特位。
在传输任何符号串之前,0符号串的完整范围设为[0,1]。
当一个符号被处理时,这一范围就依据分配给这一符号的那一范围变窄。
算术编码的过程,实际上就是依据信源符号的发生概率对码区间分割的过程。
举例说明如下:假设一则消息“static_tree”具有如下的概率分布:字符概率--------------------------------------------------------------- _(space) 0.1a 0.1e 0.3r 0.1s 0.1t 0.3下面用算术编码方法给该消息编码。
一旦字符的概率已知,就沿着“概率线”为每一个单独的符号设定一个范围,哪一个被设定到哪一段范围并不重要,只要编码和解码都以同样方式进行就可以,这里所用的6个字符被分配的范围(range)如下:字符概率范围_(space) 0.1 0≤r<0.1a 0.1 0.1≤r<0.2e 0.3 0.2≤r<0.5r 0.1 0.5≤r<0.6s 0.1 0.6≤r<0.7t 0.3 0.7≤r<1.0---------------------------------------------------------------- 对“state_tree”的算术编码过程为:(1)初始化时,被分割的范围range=high-low=[0,1),下一个范围的低、高端分别由下式计算:Low=low+range×range lowHigh=low+range×range high其中等号右边的low为上一个被编码字符的范围低;range low和range high 分别为被编码符号已给定的字符出现概率范围的low和high。
算术编码的基本原理
算术编码的基本原理算术编码是一种数据压缩和信息理论技术,用于将一个消息序列转换为一个紧凑的二进制编码。
它广泛应用于无损压缩算法中。
算术编码的基本原理是将每个符号映射到一个小数区间,然后根据这些区间的重叠来表示整个消息序列。
这个区间由两个数字表示,称为低值和高值。
初始时,低值为0,高值为1。
然后,将消息中的第一个符号映射到一个子区间,该子区间与原始区间重叠。
这样的重叠会产生精度损失,因此需要反复迭代此过程以提高编码效率。
算术编码的具体步骤如下:1. 确定符号集合:需要将消息编码为一系列符号。
符号可以是单个字符,也可以是由多个字符组成的字符串。
符号集合需要包括所有可能出现的符号。
2. 确定符号频率:对于每个符号,需要确定其在消息中出现的频率。
频率可以通过统计消息序列中每个符号的出现次数来获得。
3. 计算累积概率:对于每个符号,计算其累积概率。
累积概率是指该符号及其之前的所有符号出现的概率之和。
4. 划定区间:对于每个符号,根据其累积概率将区间划分为子区间。
每个符号对应的子区间的长度与其概率成比例。
5. 重新调整区间:由于划定的子区间与原始区间可能有重叠,需要对区间进行调整。
一种常见的方法是将区间乘以概率,使其适应于新的子区间。
6. 缩小区间:对于每个新的符号,重复以上过程,缩小当前区间范围。
7. 输出编码:对于最后一个符号,输出编码位于区间内的任意一点,这个点表示整个消息序列的二进制编码。
算术编码是一种非常灵活的压缩技术,因为它可以根据不同的符号频率分配不同长度的编码。
频率越高的符号将被分配较短的编码,从而实现更高的压缩率。
然而,算术编码也存在一些限制,比如需要大量的计算和存储资源,并且在传输过程中对于传输错误非常敏感。
尽管如此,算术编码在很多应用中得到了广泛的应用,比如无损图像和音频压缩中的JPEG和MP3算法。
它可以实现更高的压缩率,同时保持数据的完整性,是一种理论上更为高效的数据压缩方法。
算术编码——精选推荐
算术编码值得拥有的资料是来⾃平时学习积累总结的有问题的地⽅肯定有的还请⼤家批评指正!算术编码 + 统计模型 = 数据压缩 - 第⼀部分:算术编码作者:Mark Nelson现在通⽤的⼤多数数据压缩⽅法都属于两⼤阵营之⼀:基于字典的⽅案和统计⽅法在⼩系统世界中基于字典的数据压缩技术此时似乎更加流⾏不过通过将算术编码与强⼤的模型技术结合在⼀起数据压缩的统计⽅法可以真正达到更好的性能这篇分成两部分的⽂章讨论了如何⽤⼏个不同的模型⽅法与算术编码组合以达到⼀些重⼤的压缩率本⽂的第⼀部分详细说明算术编码是如何⼯作的第⼆部分说明如何开发⼀些可以使⽤算术编码的有效模型以⽣成⾼性能压缩程序钟爱的术语数据压缩通常通过从输⼊"⽂本"获取"符号"、处理它们并将"代码"写⼊到压缩后的⽂件来进⾏运作对于本⽂来说符号通常是字节但是他们很可能只是像素、80 位的浮点数或者 EBCDIC 字符数据压缩⽅案需要能够将已压缩的⽂件转换回到与输⼊⽂本的⼀样的拷贝才是有效的如果已压缩的⽂件⽐输⼊⽂本更⼩那么不必说它也是有⽤的基于字典的压缩系统通过⽤固定长度码来代替输⼊⽂本中的⼀组符号来进⾏运作字典技术的⼀个众所周知的例⼦是 LZW 数据压缩(请参见 DDJ 的 89 年第 10 期中的"LZW 数据压缩"⼀⽂)LZW 通过通常从 9 到 16 位⼤⼩范围的码来取代本来⽆限长的字符串来进⾏运作数据压缩的统计⽅法采取⼀种完全不同的⽅法它们通过⼀次编码多个符号来运作将符号编码到可变长的输出码中输出码的长度根据符号的概率或者频率进⾏变化低概率的符号⽤较多的位进⾏编码并且⾼概率符号⽤较少的位进⾏编码实践中统计和字典⽅法之间的分界线并不总是那么清晰⼀些⽅案并不能明显地归为某⼀个阵营或者另⼀个并且总是有⼀些使⽤来⾃两种技术特性的混合⽅案不过在本⽂中讨论的⽅法使⽤算术编码来实现纯粹的统计压缩⽅案霍夫曼(Huffman)编码:退役的冠军在数据流中只是能够精确地计算字符的概率还不够我们也需要能有效地利⽤这个知识的编码⽅法基于概率统计的最著名的编码⽅法可能是霍夫曼编码D.A.霍夫曼在 1952 年发表了⼀篇论⽂说明为已给定其概率的⼀组符号创建码表的⽅法当使⽤固定长度的编码时霍夫曼编码表保证产⽣最低可能的输出位来统计输⼊流中可能的符号霍夫曼称这些"最⼩冗余编码"但是这个⽅案现在统称为霍夫曼编码其它固定长度编码系统如⾹农-范诺(Shannon-Fano)编码通过霍夫曼证明不是最理想的霍夫曼编码为每个符号指定⼀个输出码输出码可以 1 位这么短也可以⽐输⼊符号长得多严格取决于它们的概率⽤于每个符号的优化位数是以 2 为底(1/p)的对数这⾥ p 是指定字符的概率因此例如如果可以在随机字节流中找到的字符概率是 1/256每个字符的最佳位数是以 2 为底的 256 的对数或者 8如果概率上升到 1/2编码字符所需要的最佳位数将降为 1这个⽅案放到现实中时的问题是霍夫曼编码的位长度必须是整数例如如果字符的概率是 1/3编码这个字符的最佳位数是⼤约 1.6霍夫曼编码⽅案必须要么指定 1 位要么指定 2 位给编码并且任何⼀种选择都导致可能⽐理论更长的压缩消息当字符的概率变得很⾼时这个⾮最佳编码也变成值得注意的问题如果能开发⼀种可以为给定的字符指定 90% 的概率的统计⽅法的话最佳编码码⼤⼩将是 0.15 个⽐特位霍夫曼编码系统只可能给符号指定⼀个 1 位的码这要⽐必需的码位长 6 倍⾃适应⽅案当尝试进⾏⾃适应数据压缩时产⽣的霍夫曼编码的第⼆个问题当进⾏⾮⾃适应数据压缩时压缩程序单程扫描⼀遍数据来收集统计信息然后使⽤在整个编码过程中都不能改变的统计信息来编码数据解码程序为了解码已压缩的数据流它⾸先需要⼀个统计信息的副本编码程序⼀般情况下将预先考虑的统计表放⼊已压缩的消息中以使解码程序可以在开始之前读⼊统计表这显然给消息增加了⼀定量的负担当使⽤很简单的统计模型压缩数据时编码表趋于更⼩例如每个字符的频率计数能以相当⾼的精确性存储在差不多 256 个字节⼤⼩的空间中它并不会给出真正最⼩的消息之外的其它任何消息增加明显的长度不过为了获得更好的压缩率统计模型在⼤⼩上有必要的增加如果消息的统计变得太⼤在压缩率中的任何改进被将需要预先放⼊已编码消息中的统计⽽增加的长度抵消了为了回避这个问题提出了⾃适应数据压缩在⾃适应数据压缩中编码程序和解码程序都以其处于相同状态的统计模型开始两者中的每⼀个⼀次处理单个字符并在字符读⼊后更新统计模型这⾮常类似于⼤多数像 LZW 编码这样基于字典⽅案的⼯作以⾮最佳模型开始的消息会有少量的效率损失但是它经常通过不必随消息传送任何统计信息⽽得到更好的弥补将⾃适应模型与霍夫曼编码结合起来的问题是重建霍夫曼树是⼀个⾮常昂贵的过程为了让⾃适应⽅案⾼效有必要在每⼀个字符出现之后调整霍夫曼编码直到霍夫曼编码第⼀次开发的 20 年之后执⾏⾃适应霍夫曼编码的算法都没有发布即使在现在最好的⾃适应霍夫曼编码算法仍然相当耗时间和⾦钱算术编码(Arithmetic Coding):它如何⼯作⼀个替代霍夫曼编码的可观候选⽅案已在仅近⼗年得到证实:算术编码(Arithmetic Coding)算术编码完全放弃了⽤指定的编码取代输⼊符号的主张取⽽代之的是它接受输⼊符号流并⽤单个浮点输出数来代替它消息越长(并且越复杂)在输出数中就需要越多的位直到最近仍然没有发现在计算机上⽤固定⼤⼩的寄存器来实现这⼀思想的实践⽅法算术编码过程产⽣的输出是单个⼩于 1 并⼤于等于 0 的数这个单个的数可以唯⼀地解码以创建参与其构造的精确字符流为了构造输出数符号的编码必须有⼀组指定给它们的概率例如如果我要编码随机消息"BILL GATES"我会得到看起来如下的概率分布:Character Probability-------------- --------------SPACE 1/10A 1/10B 1/10E 1/10G 1/10I 1/10L 2/10S 1/10T 1/10⼀旦知道了字符的概率需要按"概率线"为单个符号指定⼀个范围这个"概率线"名义上是从 0 到 1为哪个字符指定哪⼀段范围并不⿇烦只要编码程序和解码程序都采⽤相同的⽅式来指定范围我们在这⾥使⽤的这⼀组九个字符符号将看起来如下:Character Probability Range--------------- ---------------- ----------------SPACE 1/10 0.00 - 0.10A 1/10 0.10 - 0.20B 1/10 0.20 - 0.30E 1/10 0.30 - 0.40G 1/10 0.40 - 0.50I 1/10 0.50 - 0.60L 2/10 0.60 - 0.80S 1/10 0.80 - 0.90T 1/10 0.90 - 1.00在 0 - 1 范围内为每⼀个字符指定⼀个与其出现的概率对应的的⽚断当编码消息"BILL GATES"时第⼀个符号是"B"要正确编码第⼀个字符最终编码后的消息必须是⼤于或者等于 0.20 并⼩于 0.30 的⼀个数要编码这个数我们所要做的是明了这个数会落⼊的范围因此在编码完第⼀个字符后这个范围的最底端是 0.20并且范围的最顶端是 0.30在编码完第⼀个字符之后我们知道对于我们的输出数来说我们的范围现在就以这个底数和这个顶数为边界在剩余的编码过程期间发⽣的事情是每⼀个要编码的新符号将更进⼀步地限制输出数的可能范围下⼀个要编码的字符"I"拥有范围 0.50 到 0.60如果它是我们消息中的第⼀个数我们可以直接将我们的底和顶的范围设为这两个值但是"I"是第⼆个数因此我们不能直接设定⽽是应该说"I"在 0.2 - 0.3 的⼦范围中拥有与 0.50 - 0.60 对应的范围这意味着新编码的数将必然落⼊当前已建⽴范围的第 50 ⾄第 60 个百分点之间的位置按照这个逻辑将我们的数进⼀步限制到 0.25 ⾄ 0.26 之间的范围对任意长度的消息按照这种思路完成编码的算法说明如下:Set low to 0.0Set high to 1.0While there are still input symbols doget an input symbolcode_range = high - low.high = low + range*high_range(symbol)low = low + range*low_range(symbol)End of Whileoutput low按照这个流程可以得出对我们所选择的消息的⾃然⽽简单的总结看起来如下:New Character Low Value High Value-------------------- --------------- ---------------0.0 1.0B 0.2 0.3I 0.25 0.26L 0.256 0.258L 0.2572 0.2576SPACE 0.25720 0.25724G 0.257216 0.257220A 0.2572164 0.2572168T 0.25721676 0.2572168E 0.257216772 0.257216776S 0.2572167752 0.2572167756因此最终的底值0.2572167752 将使⽤我们介绍的编码⽅案唯⼀编码消息"BILL GATES"有了这个编码⽅案明⽩解码过程如何运作相对⽐较容易我们通过看哪⼀个符号拥有我们已编码消息所落在的码空间来查找到第⼀个符号因为数 0.2572167752 落在 0.2 和 0.3 之间我们知道第⼀个字符必然是"B"然后我们需要将"B"从已编码的数中移除因为我们知道 B 的底和顶范围我们可以⽤与将它们放⼊过程相反的过程来消除它们的影响⾸先我们从数中减去 B 的底值得到 0.0572167752然后我们除以 B 的范围即 0.1所得的值为 0.572167752然后我们可以计算出在哪⾥停⽌这个停⽌的位置正是在下⼀个字母"I"的范围之内解码输⼊数的算法看上去如下:get encoded numberDofind symbol whose range straddles the encoded numberoutput the symbolrange = symbol low value - symbol high valuesubtract symbol low value from encoded numberdivide encoded number by rangeuntil no more symbols注意我为了⽅便起见忽略了判断什么时候才没有更多的符号需要解码的问题可以通过将⼀个特殊 EOF 符号编码进消息也可以随已编码的消息⼀同传送流的长度来解决这个问题"BILL GATES"消息的解码算法的处理过程如下:Encoded Number Output Symbol Low High Range----------------------- -------------------- ------ ------ ---------0.2572167752 B 0.2 0.3 0.10.5721677752 I 0.5 0.6 0.10.72167752 L 0.6 0.8 0.20.6083876 L 0.6 0.8 0.20.041938 SPACE 0.0 0.1 0.10.41938 G 0.4 0.5 0.10.1938 A 0.2 0.3 0.10.938 T 0.9 1.0 0.10.38 E 0.3 0.4 0.10.8 S 0.8 0.9 0.10.0概括来说编码过程就是⼀个简单地使⽤每⼀个新的符号来收缩数的可能范围这样⼀个过程新的范围与附加给这个符号的预先定义的概率成⽐例解码是逆向过程其中展开的范围与抽取出的每⼀个字符的概率成⽐例实践中的问题使⽤算术编码编/解码符号流不是太复杂但是初看上去它似乎完全⽆法实现⼤多数计算机最多⽀持的 80 位或差不多的浮点数这是不是就意味着你必须在每次完成 10 到 15 个符号以后重新开始?你需要浮点处理器?不同浮点格式的机器可以使⽤算术编码来通信吗?如它所展现的算术编码最好使⽤标准 16 位和 32 位整数来完成不必⾮要要求浮点数也不必要求其帮助才能使⽤算术编码代替浮点的⽅法是增量转换⽅案在⽅案中声明为定点整数的变量接收在底端的新位并将这些位转移到顶端最终形成⼀个⼤的单数这个数的位只取决于计算机的存储媒介媒介能存储多长这个数就有多⼤在前⾯的部分我通过明了括起可能输出数的范围的顶端数和底端数来说明算法如何⼯作当算法第⼀次启动时底端数设为 0.0并且顶端数设为 1.0使⽤整数⾸先进⾏的简化是以⼆进制的形式改变 1.0 ⾄ 0.999......或者.111......为了在整数寄存器中存储这些数我们⾸先调整它们以让上⾯这个隐含的⼩数点是在词的左边然后我们载⼊与适合我们要处理的单词所需要⼤⼩差不多⼀样的初始顶值和底值我的实现使⽤ 16 位⽆符号数因此初始的顶值是 0xFFFF并且底值是 0我们知道顶值从 FF 开始⽆限增长并且底值从零开始⽆限增长因此我们可以在需要这些剩余位(也就是最后两位(在 0x00 与 0xFF 之间))时我们⽆需再附加顶值和底值⽽是直接将它们转移出来如果你想像我们的"BILL GATES"例⼦在⼀个具有 5 个数的寄存器中我们设置与上述相当的这个数看起来应当如下:顶值:99999底值:00000为了找到我们新的范围数我们需要应⽤前⾯部分说明的编码算法我们⾸先需要计算底值和顶值之间的范围两个寄存器之间的差值将是 100000⽽不是 99999这是因为我们假设有⽆穷多个的 9(实际上只受限于我们要处理的单词数量)被加到了存储顶值的寄存器上因此我们需要增加计算出来的差值那么我们使⽤来⾃前⾯部分的⽅程来计算新的顶值:high = low + high_range(symbol)在这种情况下顶值范围是 .30这个范围给出了⼀个新的顶值 30000存储新的顶值之前我们需要先将它减 1再次强调因为有那个隐藏的数字加到了整数值上因此新的顶值是 29999底端数的计算遵循相同的⽅法结果得到新值为 20000因此现在顶值和底值看起来如下:顶端值: 29999 (999...)底端值: 20000 (000...)从这⼀点来说顶端和底端的最⾼位的数字应该匹配因为我们的算法本质顶端和底端可以继续增长接近⾄另⼀个完全没有匹配过的数这意味着⼀旦它们在最⾼位的数字上匹配时这个数字就不再改变因此我们现在可以输出这个数字作为我们编码数的第⼀个数字这可以通过将顶端和底端向左移动⼀个数字来完成并且在顶端的最低位数字后移⼊⼀个 9 进来在这个算法的 C 实现中是以⼆进制的形式执⾏相当的操作随着这个过程的进⾏顶端和底端继续向着接近碰头的⽅向增长然后将转移出的数字编码进单词中我们的"BILL GATES"消息的过程看起来如下:HIGH LOW RANGE CUMULATIVE OUTPUTInitial state 99999 00000 100000Encode B (0.2 - 0.3) 29999 20000Shift out 2 99999 00000 100000 .2Encode I (0.5 - 0.6) 59999 50000 .2Shift out 5 99999 00000 100000 .25Encode L (0.6 - 0.8) 79999 60000 20000 .25Encode L (0.6 - 0.8) 75999 72000 .25Shift out 7 59999 20000 40000 .257Encode SPACE (0.0 - 0.1) 23999 20000 .257Shift out 2 39999 00000 40000 .2572Encode G (0.4 - 0.5) 19999 16000 .2572Shift out 1 99999 60000 40000 .25721Encode A (0.1 - 0.2) 67999 64000 .25721Shift out 6 79999 40000 40000 .257216Encode T (0.9 - 1.0) 79999 76000 .257216Shift out 7 99999 60000 40000 .2572167Encode E (0.3 - 0.4) 75999 72000 .2572167Shift out 7 59999 20000 40000 .25721677Encode S (0.8 - 0.9) 55999 52000 .25721677Shift out 5 59999 20000 .257216775Shift out 2 .2572167752Shift out 0 .25721677520注意在所有的字母都解决完后两个额外的数字需要转移出顶端值或者底端值来结束输出单词复杂性这个⽅案对于增量编码⼀条消息来说⾮常适合在使⽤双精度整数计算以确保消息可以精确地编码的期间精确度是有保证的不过在某些情形下精确度会有⼀定潜在的损失如果已编码的词其中有 0 个或 9 个字符串顶端值和底端值将慢慢地会聚到⼀个值但是可能不是⽴即看它们的最⾼位数字的匹配例如顶值和底值可能看起来如下:High: 700004Low: 699995此时计算的范围的长度将只是⼀个单个数字这意味我们会没有⾜够的精度来精确地编码这个输出词甚⾄更糟在⼏次更多的迭代后顶端数和底端数可能会看起来如下:High: 70000Low: 69999此时这两个值永远也粘不到⼀块了顶端值和底端值变得如此⼩以⾄于任何计算总是返回相同的值但是因为两个词的最⾼位的数字并不相等算法并不能输出数字并进⾏转移它似乎更像是⼀个僵局战胜这个下溢问题的办法是防⽌事情变得这么糟最初的算法说过⼀些像"如果顶值和底值的最⾼位的数字匹配的话将它转移出来"这样的事情如果两个数字不匹配⽽它们现在是很邻近的数就需要应⽤第⼆次测试如果顶端数和底端数还是分开的(也就是还没有碰上)那么我们测试看顶端中第⼆⾼位的数字是否是 0以及底端第⼆⾼位的数字是否是 9如果是它意味着我们正在接近下溢并且需要采取⾏动当下溢出现我们采⽤⼀个些微转移操作来阻⽌它不是将最⾼位的数字转移出这个词⽽是从顶端值和底端值中删除第⼆⾼位的数字并将剩余的数字左移以填充空⽩最⾼位的数字仍留在原位然后我们需要调置⼀个下溢计数器来记住我们扔掉了⼀个数字并且我们不能完全确信它是否就会是以 0 或者 9 结束操作看起来如下:Before After--------- --------High: 40344 43449Low: 39810 38100Underflow: 0 1在每⼀次重新计算操作之后如果最⾼位的数字并不能匹配上我们可以再次检查下溢数字如果下溢仍然出现我们将它们转移出来并增加计算机器当最⾼位的数字最终会聚到⼀个单数值时我们⾸先输出这个值然后我们输出所有在前⾯丢弃的"下溢"数字下溢数字将全部是 9 或者全部是 0取决于顶端数和底端数是否会聚到⼀个较⾼的值还是⼀个较低的值在这个算法的 C 实现中下溢计数明了输出了多少个 9 或者 0解码在"理想的"解码过程中我们使⽤整个输⼊数进⾏⼯作因此算法要求我们做⼀些像"通过符号概率划分已编码的数"在实践中我们不能在⼀个可能有⼗亿个字节那么长的数上执⾏操作正如编码过程解确程序通过将 16 和 32 位整数⽤于计算来运作取代维护两个数顶端数和底端数解码程序必须维护 3 个整数前两个是顶端数严格对应到由编码程序维护的顶端数和底端数第三个数编码包含正在从输⼊位流中读⼊的当前的位编码值将总是处于顶端值和底端值之间像它们越来越接近⼀样将发⽣新的转移操作并且顶端数和底端数将从编码退出在解码程序中顶端值和底端值严格对应于编码程序所使⽤的顶端值和底端值这两个值正如它们在编码程序中⼀样在每⼀个符号之后都将被更新并且应该有精确的相同值通过在顶端数和底端数的第⼀个的数字上执⾏相同的⽐较测试解码程序知道何时是将⼀个新的数字转移到输⼊编码中的时间也与编码程序步调⼀致执⾏相同的下溢测试在理想的算法中通过查找其概率接近编码的当前值的符号来判断当前已编码的符号是什么是可⾏的在整数算法中事情些微更复杂⼀些在这种情况下概率缩放因⼦由顶端数和底端数之间的差来决定因此将使⽤两个 16 位的正整数之间来计算范围来代替 0.1 和 1.0 之间的范围计算当前的概率由落⼊这个范围的当前编码所决定如果你⽤(high-low+1)除(value-low)你会得到当前符号的实际概率哪⼉有⽜⾁?到⽬前为⽌这个编码过程可能看起来很有意思但是为什么说它在霍夫曼编码的基础上有所改进看起来并不显然当我们测试⼀个概率有⼀点不同的案例时答案就变得很清晰了让我们测试⼀个我们必须编码流"AAAAAAA"的案例并且"A"的概率已知是 0.90这意味着任何输⼊字符有 90% 的机会是字母"A"我们建⽴起我们的概率表以使字母"A"占有范围 0.0 到 0.9并且消息符号的结束占有 0.9 到 1.0 的范围那么编码过程看起来如下:New Character Low value High value-------------------- -------------- --------------0.0 1.0A 0.0 0.9A 0.0 0.81A 0.0 0.729A 0.0 0.6561A 0.0 0.59049A 0.0 0.531441A 0.0 0.4782969END 0.43046721 0.4782969现在我们知道底端值和顶端值是什么剩下的所有事情就是拿⼀个数来编码这个消息数"0.45"将可以使这条消息唯⼀解码成"AAAAAAA"这两个数字⽤了⽐ 7 位还些微少点空间来定义这意味着我们⽤少于 8 位的空间编码了 8 个符号最佳的霍夫曼使⽤这个⽅案来编码上⾯的消息最少也要 9 位进⼀步延伸这个观点我设置⼀个只定义了两个符号的测试字节值"0"有 16382/16383 的概率并且 EOF 符号有 1/16383 的出现概率然后我创建⼀个测试⽂件其中填上⼀百万(100,000)个"0"使⽤这个模型压缩之后输出⽂件只有 3 个字节长!使⽤霍夫曼编码的最⼩的长度也有 12,501 个字节这显然是⼀个⼈为的例⼦但是它论证了当符号的概率正确的时候算术编码能够以⽐每个字节只占 1 位还好的⽐率压缩数据编码⼀个⽤ C 编写的编码过程放在列表1 和 2 中它包含两个部分⼀个初始化过程和编码程序本⾝⽤于编码程序以及解码程序的编码第⼀次由 IanH.Witten、Radford Neal 和 John Cleary 发表在"Communication of the ACM"的 1987 年 2 ⽉期的⼀篇名为"Arithmetic Coding for Data Compression"的论⽂中并且经过作者的允许在此公开我稍微修改了这个编码以使程序的建模部分和编码部分进⼀步地隔离开先前说明的两个算法之间有两个主要的差异以及代码⼊在列表 1-2第⼀个不同是在转换概率的⽅法中在上⾯说明的算法中概率保持为⼀对范围为 0.0 ⾄ 1.0 的浮点数每⼀个符号有其⾃⼰的范围部分在我们在这⾥说明的程序中符号有稍微不同的定义符号的范围定义为两个经过缩放因⼦计算得出的整数⽽不是两个浮点数这个缩放因⼦也作为符号定义的⼀部分包括其中因此对于"BILL GATES"的例⼦字母 L 在前⾯定义为⼀对值为 0.60 和 0.80 的顶/底端数在此处使⽤的编码中⽤值为 10 的确良符号缩放因⼦计算出底和顶端数分别为 6 和 8 来定义"B"采⽤这种⽅法来完成定义的原因是因为它在建模这种类型的数据时能很好地与我们保持统计的办法相适应这两种⽅法是相当的只是保持⽤整数进⾏运算减少了许多需要完成的⼯作注意字符实际上⼀直拥有范围但不包括顶端值在这个算法中的第⼆个不同之处是所有⽐较和转移操作都是以⼆进制的形式完成的⽽不是⼗进制前⾯给出的说明是基于⼗进制数字进⾏的以使算法更好理解⼀点算法在⼗进制下可以正常⼯作但是在⼤多数计算机上进⾏⼗进制数的屏蔽数字和移位很困难因此我们现在⽐较最开始的两个位⽽不是⽐较最开始的两个数字我们丢失了为了使⽤编码和解码算法所需要的两件事情第⼀件是⼀组⾯向位的输⼊和输出程序这放在了列表 3 和 4 中并且在代码中没有注释建模代码负责跟踪每⼀个字符的概率并执⾏两个不同的转换在编码过程期间建模代码取将要被编码的字符并将其转换成概率范围概率范围定义为⼀个底数、⼀个顶数和⼀个总范围在解码过程期间建模代码必须取⼀个来⾃输⼊位流的计数并将其转换成⼀个字符进⾏输出测试代码⼀个短的测试程序放在列表 3 中它实现⼀个使⽤前⾯讨论的"BILL GATES"模型的压缩/展开程序在⼏个需要判断的地⽅加⼊了打印语句以让你准确地跟踪在这个程序中进⾏的处理BILL.C 有其⾃⼰⾮常简单的模型单元这个模型对每⼀个消息中可能的字母都有⼀组因定的概率我加⼊了⼀个新字符即空字符串结束符以知道何时停⽌解码这条消息BILL.C 编码⼀个随便定义的输⼊字符串并将其写出到⼀个叫作 TEST.CMP 的⽂件中然后它解码这条消息将其打印在屏幕上进⾏验证在 BILL.C 中有⼏个仍然没有讨论过的建模函数在编码过程期间调⽤了⼀个称为 convert_int_to_symbol 程序这个程序获取⼀个给定的输⼊字符并将其转换成⼀个底值、顶值和⽤在此模型中的缩放因⼦因为我们⽤于 BILL.C 的模型是⼀组固定的概率这就意味着在这个表中查询概率这些⼀旦定义完毕就可以调⽤编码程序在解码过程期间有两个与建模有关的函数为了决定输⼊流中什么字符正在等待被解码模型需要询问模型来判断当前的缩放因⼦是多少在我们的例⼦中缩放因⼦(或者是计算范围)是固定在 11因为在我们的模型中总是有 11 次计算这个数被传送给解码程序并且它为使⽤这个缩放因⼦的给定符号返回⼀个计数然后调⽤⼀个称为 convert_symbol_to_int 的建模函数调⽤它获取给定的这个计数并判断什么字符与这个计数相匹配最终再次调⽤解码程序来解出输⼊流中的那个字符下⼀次⼀旦你成功地理解了如何使⽤算术编码来编码/解码符号那么你可以开始尝试创建将具有超级压缩性能的模型下⼀个⽉的总结⽂章讨论⼀些你可以尝试的⾃适应压缩⽅法⼀个相当简单的统计模型程序能够提供超过备受尊敬的程序如 PKZIP 或者 COMPRESS 的压缩参考资源如我在前⾯提到在 1987 年 7 ⽉的 "Communication of the ACM"(ACM 通信)提供权威的算术编码的概论这篇⽂章的⼤部分在由 Timothy C.Bell、John G. Cleary 和 Ian H.Witten 所著的"Text Compression"(⽂本压缩)⼀书中重印这本书为基于统计和基于字典的压缩两种技术提供了出⾊的概论另⼀本好书是由 James Storer 所著的"Data Compression"(数据压缩)Bell, Timothy C., Cleary, John G., Witten, Ian H,(1990) "Text Compression",Prentice Hall, Englewood NJNelson, Mark(1989) "LZW Data Compression", Doctor Dobb's Journal, October, pp 29-37Storer, J.A.,(1988) "Data Compression", Computer Science Press, Rockville, MDWitten, Ian H., Neal, Radford M., and Cleary, John G.(1987)"Arithmetic Coding for Data Compression", Communications of the ACM, June, pp 520-540.算术编码 + 统计模型 = 数据压缩 - 第⼆部分:统计模型作者:Mark Nelson这两部分系列⽂章的第⼀篇以⼀些细节准确地解释了什么是算术编码概括地讲算术编码提供使⽤最佳位数⽬编码符号的⽅法由每个符号所使⽤的位数不⼀定像霍夫曼编码(Huffman coding)的情况下⼀样是⼀个整数为了使⽤算术编码压缩数据需要给数据⼀个模型这个模型需要能够完成两件事来有效地压缩数据:模型需要精确地预计输⼊数据流中符号的频率/概率* 由模型⽣成的符号概率需要脱离均匀分布本⽂将讨论有效达成这些⽬标获取⾼性能压缩的⼀些⽅法建模需要精确地预计输⼊数据中符号的概率是算术编码中固有的本质这类编码⽅法的原理是随着字符出现概率的增加⽽减少编码这个字符所需要的位数⽬因此如果字母"e"出现在输⼊数据的概率是 25%它将只使⽤ 2 位来编码如果字母"Z"在输⼊数据中出现的概率只有 1%它可能要⽤ 10 位来编码如果模型没有精确地⽣成概率它可能⽤ 10 位来表⽰"e"并且⽤ 2 位来表⽰"Z"。
4.3.3________算术编码
当低概率符号到来时, C C A A Qe 当高概率符号到来时, C C + A Qe A A Pe
新的子区间为[C, C+A], ……, 以此类推,直 到一组信源符号结束为止。算术编码的结 果落在最后的子区间之内,为子区间头、 尾之间的取值。
[ 题 ] 己知信源 X =
0
1
1/4 3/
试对 1011 进行算术编码。
19/64 85/256 112/256
最后的子区间左端(起始位置) C = ( 85/256)d = (0.01010101)b 最后的子区间右端(终止位置) C+A = (112/256) d = (0.01110000) b 编码结果为子区间头、尾之间取值,其 值为0.011, 可编码为011,原来4个符号 1011现被压缩为三个符号011。
算术编码的编码方法
Qe 0 Pe 1
初始化子区间为 [0,1], 预设一个大概率 Pe 和小概率 Qe , 信源中的每个符号(0或1)对应一个概率,然后 对被编码信源比特流符号(0或1)依次进行 判断。
设置两个专用寄存器 C, A,存贮符号到来之 前子区间的状态参数,令: C = 子区间的起始位置, A = 子区间的宽度, 初始化时,C=0, A=1. 随着被编码信源数据比特流符号0,1的输入, C和A按以下方法进行修正:
解码
解码时,是编码的逆过程。 首先将区间 [0 , 1) 按 Qe 靠近 0 侧、 Pe 靠近 1 侧分割成两个子区间,判断被 解码的码字落在哪个子区间,赋以对应 符号,然后调整子区间 C, A 的值。 按此法多次重复,便可依次得到串 中各符号。
4.3.3 算术编码
算术编码把一个信源集合表示为实数轴上 的0到1之间的一个区间。信源集合中的每个元 素都要用来缩短这个区间。信源集合的元素越 多,所得到的区间就越小,当区间变小时,就 需要更多的数位来表示这个区间,这就是区间 作为代码的原理。算术编码首先假设一个信源 的概率模型,然后用这些概率来缩小表示信源 集的区间。
算数编码
算术编码算法及其仿真算术编码研究的意义及目的随着多媒体和互联网技术的不断发展,数据压缩技术获得了空前的进步,数码相机、数码摄像机等手持式设备的普及应用,要求图像数据压缩技术能够在够高的压缩比下实现较好的图像质量,以满足存储空间有限条件下的海量数据压缩问题。
各种媒体信息(特别是图像和动态视频)数据量非常之大,就要求与之相应的有较好的压缩技术。
而算术编码作为一种高效的数据编码方法在文本,图像,音频等压缩中有广泛的应用。
所以,研究算术编码有非常好的前景与实用价值。
不论是活动图像还是静止图像,采用算术编码得到的码流都比用霍夫曼编码得到的码流要短,编码效率高。
但是到目前为止,算术编码还没有完全取代霍夫曼编码。
在许多图像压缩编码标准里,算术编码仅是一个选项。
这除了知识产权方面的原因外,算术编码复杂度较高,不论是软件还是硬件实现起来都比较困难是一个主要因素。
可以预见,随着超大规模集成电路技术的发展,高速高性能处理器的大量涌现,复杂度较高的算法也有可能实时实现。
另一方面,快速的算术编码算法也不断被发现提出,比如IBM 公司的Qcoder,Paul.G.Howard 等提出的准算术编码器等。
这些快速算法以损失一定的编码效率为代价,采用近似的方法,加快了编码速度。
此外,更加有效的概率估计模型的提出也会进一步提高算术编码的效率。
随着以上几方面的的进展,算术编码在图像压缩编码内的应用将会越来越广,它比著名的Huffman 编码效率提高5%~10%左右,最终必将取代霍夫曼编码方法。
所以,研究算术编码以更好的利用它是非常必要的。
算术编码算法基本原理算术编码是一种无失真的编码方法,能有效地压缩信源冗余度,使编成的码率趋于信源的熵,它是无损压缩的一种。
算术编码的基本原理是:根据信源可能发现的不同符号序列的概率,把[0,1)区间划分为互不重叠的子区间,子区间的宽度恰好是各符号序列的概率。
这样信源发出的不同符号序列将与各子区间一一对应,因此每个子区间内的任意一个实数都可以用来表示对应的符号序列,这个数就是该符号序列所对应的码字。
编码的本质及计算机的工作原理
编码的本质及计算机的工作原理编码的本质是什么?计算机的工作原理是什么?越想越迷糊,感觉计算机就像迷雾一样,平常只会用,但不清楚原理。
后来,从基本电路开始,搭建了复杂电路,并通过调节开关,控制电路。
然后,利用存储中的高低电平控制电路,体会了机器语言编程的感觉,算是基本入门。
机器语言很繁琐,后来体会了汇编语言的好处,也控制了电路,最后又体会了高级语言的好处,也能控制电路。
最近,又有一些新思考,所以简要地讲述一下。
一. 掌握原理的重要性小时候,很喜欢骑自行车,试了很多次,一直不会骑,直到有一天,突然就会了,挺奇怪的。
那个瞬间,至今难忘。
那时,玩得不亦乐乎,此皆幼时闲情也。
不需要读什么书,也不需要什么知识,我们就能大致悟出自行车的工作原理,其实就是常识。
如下的描述,并不十分精准,但足够让人了解自行车的工作原理。
•用力踩呀踩,中间的铁齿轮开始转动。
•通过链条,带动后轮转动,准备向前。
•后轮推动前轮往前转,自行车走起来。
有的朋友要说,会骑自行车就行了,管它什么工作原理。
这句话,听起来好有道理。
林语堂先生却说:光知道用东西,却不知其所以然,实为遗憾。
后来,中国台湾知名技术作家侯杰先生,在他的著作《深入浅出MFC》中,也提到了上述观点。
我个人的观点是: 我们使用工具,并不是一定要深究其原理,有时候,没有必要浪费精力和时间,沉迷于不重要的细节,自我陶醉,无法自拔。
但是,如果能够了解一些原理,能帮助我们更好地使用工具,甚至有可能改进和创造工具。
二. 从硬到软层层抽象如今,我们几乎所有的人,每天都在使用计算机(手机也是计算机)。
对于大多数人而言,没有必要深入了解计算机的工作原理。
而对于从事计算机相关行业的人,或者对计算机有兴趣、有好奇心的人,也可以去了解一下。
另外,了解计算机的工作原理,也能为装叉吹牛增加一些信心和谈资。
说不定帮人修电脑或者安装系统时,侃侃而谈,还能成就一段美好姻缘。
很多大二大三大四的师兄,特别擅长这些,那些大一的学妹,仰慕不已。
算术编码原理
算术编码原理早在1948年,⾹农就提出将信源符号依其出现的概率降序排序,⽤符号序列累计概率的⼆进值作为对芯源的编码,并从理论上论证了它的优越性。
1960年, Peter Elias发现⽆需排序,只要编、解码端使⽤相同的符号顺序即可,提出了算术编码的概念。
Elias没有公布他的发现,因为他知道算术编码在数学上虽然成⽴,但不可能在实际中实现。
1976年,R. Pasco和J. Rissanen分别⽤定长的寄存器实现了有限精度的算术编码。
1979年Rissanen和G. G. Langdon⼀起将算术编码系统化,并于1981年实现了⼆进制编码。
1987年Witten等⼈发表了⼀个实⽤的算术编码程序,即CACM87(后⽤于ITU-T的H.263视频压缩标准)。
同期,IBM公司发表了著名的Q-编码器(后⽤于JPEG和JBIG图像压缩标准)。
从此,算术编码迅速得到了⼴泛的注意。
算术编码的基本原理是将编码的消息表⽰成实数0和1之间的⼀个间隔(Interval),消息越长,编码表⽰它的间隔就越⼩,表⽰这⼀间隔所需的⼆进制位就越多。
算术编码⽤到两个基本的参数:符号的概率和它的编码间隔。
信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,⽽这些间隔包含在0到1之间。
编码过程中的间隔决定了符号压缩后的输出。
给定事件序列的算术编码步骤如下:(1)编码器在开始时将“当前间隔” [ L, H) 设置为[0,1)。
(2)对每⼀事件,编码器按步骤(a)和(b)进⾏处理(a)编码器将“当前间隔”分为⼦间隔,每⼀个事件⼀个。
(b)⼀个⼦间隔的⼤⼩与下⼀个将出现的事件的概率成⽐例,编码器选择⼦间隔对应于下⼀个确切发⽣的事件相对应,并使它成为新的“当前间隔”。
(3)最后输出的“当前间隔”的下边界就是该给定事件序列的算术编码。
设Low和High分别表⽰“当前间隔”的下边界和上边界,CodeRange为编码间隔的长度,LowRange(symbol)和HighRange(symbol)分别代表为了事件symbol分配的初始间隔下边界和上边界。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
算术编码工作原理在给定符号集和符号概率的情况下,算术编码可以给出接近最优的编码结果。
使用算术编码的压缩算法通常先要对输入符号的概率进行估计,然后再编码。
这个估计越准,编码结果就越接近最优的结果。
例: 对一个简单的信号源进行观察,得到的统计模型如下:•60% 的机会出现符号中性•20% 的机会出现符号阳性•10% 的机会出现符号阴性•10% 的机会出现符号数据结束符. (出现这个符号的意思是该信号源'内部中止',在进行数据压缩时这样的情况是很常见的。
当第一次也是唯一的一次看到这个符号时,解码器就知道整个信号流都被解码完成了。
)算术编码可以处理的例子不止是这种只有四种符号的情况,更复杂的情况也可以处理,包括高阶的情况。
所谓高阶的情况是指当前符号出现的概率受之前出现符号的影响,这时候之前出现的符号,也被称为上下文。
比如在英文文档编码的时候,例如,在字母Q 或者q出现之后,字母u出现的概率就大大提高了。
这种模型还可以进行自适应的变化,即在某种上下文下出现的概率分布的估计随着每次这种上下文出现时的符号而自适应更新,从而更加符合实际的概率分布。
不管编码器使用怎样的模型,解码器也必须使用同样的模型。
一个简单的例子以下用一个符号串行怎样被编码来作一个例子:假如有一个以A、B、C三个出现机会均等的符号组成的串行。
若以简单的分组编码会十分浪费地用2 bits来表示一个符号:其中一个符号是可以不用传的(下面可以见到符号B正是如此)。
为此,这个串行可以三进制的0和2之间的有理数表示,而且每位数表示一个符号。
例如,“ABBCAB”这个串行可以变成0.011201(base3)(即0为A, 1为B, 2为C)。
用一个定点二进制数字去对这个数编码使之在恢复符号表示时有足够的精度,譬如0.001011001(base2) –只用了9个bit,比起简单的分组编码少(1 – 9/12)x100% = 25%。
这对于长串行是可行的因为有高效的、适当的算法去精确地转换任意进制的数字。
编码过程的每一步,除了最后一步,都是相同的。
编码器通常需要考虑下面三种数据:•下一个要编码的符号•当前的区间(在编第一个符号之前,这个区间是[0,1), 但是之后每次编码区间都会变化)•模型中在这一步可能出现的各个符号的概率分布(像前面提到的一样,高阶或者自适应的模型中,每一步的概率并不必须一样)编码其将当前的区间分成若干子区间,每个子区间的长度与当前上下文下可能出现的对应符号的概率成正比。
当前要编码的符号对应的子区间成为在下一步编码中的初始区间。
例: 对于前面提出的4符号模型:•中性对应的区间是 [0, 0.6)•阳性对应的区间是 [0.6, 0.8)•阴性对应的区间是 [0.8, 0.9)•数据结束符对应的区间是 [0.9, 1)当所有的符号都编码完毕,最终得到的结果区间即唯一的确定了已编码的符号串行。
任何人使用该区间和使用的模型参数即可以解码重建得到该符号串行。
实际上我们并不需要传输最后的结果区间,实际上,我们只需要传输该区间中的一个小数即可。
在实用中,只要传输足够的该小数足够的位数(不论几进制),以保证以这些位数开头的所有小数都位于结果区间就可以了。
例: 下面对使用前面提到的4符号模型进行编码的一段信息进行解码。
编码的结果是0.538(为了容易理解,这里使用十进制而不是二进制;我们也假设我们得到的结果的位数恰好够我们解码。
下面会讨论这两个问题)。
像编码器所作的那样我们从区间[0,1)开始,使用相同的模型,我们将它分成编码器所必需的四个子区间。
分数0.538落在NEUTRAL坐在的子区间[0,0.6);这向我们提示编码器所读的第一个符号必然是NEUTRAL,这样我们就可以将它作为消息的第一个符号记下来。
然后我们将区间[0,0.6)分成子区间:•中性的区间是 [0, 0.36) -- [0, 0.6) 的 60%•阳性的区间是 [0.36, 0.48) -- [0, 0.6) 的 20%•阴性的区间是 [0.48, 0.54) -- [0, 0.6) 的 10%•数据结束符的区间是 [0.54, 0.6). -- [0, 0.6) 的 10%我们的分数 .538 在 [0.48, 0.54) 区间;所以消息的第二个符号一定是NEGATIVE。
我们再一次将当前区间划分成子区间:•中性的区间是 [0.48, 0.516)•阳性的区间是 [0.516, 0.528)•阴性的区间是 [0.528, 0.534)•数据结束符的区间是 [0.534, 0.540).我们的分数 .538 落在符号 END-OF-DATA 的区间;所以,这一定是下一个符号。
由于它也是内部的结束符号,这也就意味着编码已经结束。
(如果数据流没有内部结束,我们需要从其它的途径知道数据流在何处结束——否则我们将永远将解码进行下去,错误地将不属于实际编码生成的数据读进来。
)同样的消息能够使用同样短的分数来编码实现如 .534、.535、.536、.537或者是.539,这表明使用十进制而不是二进制会带来效率的降低。
这是正确的是因为三位十进制数据能够表达的信息内容大约是9.966位;我们也能够将同样的信息使用二进制分数表示为.10001010(等同于0.5390625),它仅需8位。
这稍稍大于信息内容本身或者消息的信息熵,大概是概率为0.6%的 7.361位信息熵。
(注意最后一个0必须在二进制分数中表示,否则消息将会变得不确定起来。
)[编辑]精度和再正规化上面对算术编码的解释进行了一些简化。
尤其是,这种写法看起来好像算术编码首先使用无限精度精度的数值计算总体上表示最后节点的分数,然后在编码结束的时候将这个分数转换成最终的形式。
许多算术编码器使用优先精度的数值计算,而不是尽量去模拟无限精度,因为它们知道解码器能够匹配、并且将所计算的分数在那个精度四舍五入到对应值。
一个例子能够说明一个模型要将间隔[0,1]分成三份并且使用8位的精度来实现。
注意既然精度已经知道,我们能用的二进制数值的范围也已经知道。
一个称为再归一化的过程使有限精度不再是能够编码的字符数目的限制。
当范围减小到范围内的所有数值共享特定的数字时,那些数字就送到输出数据中。
尽管计算机能够处理许多位数的精度,编码所用位数少于它们的精度,这样现存的数据进行左移,在右面添加新的数据位以尽量扩展能用的数据范围。
注意这样的结果出现在前面三个例子中的两个里面。
[编辑]算术编码和其他压缩方法的联系[编辑]哈夫曼编码在算术编码和哈夫曼编码之间有很大的相似性 -- 实际上,哈夫曼编码只是算术编码的一个特例 -- 但是由于算术编码将整个消息翻译成一个表示为基数b,而不是将消息中的每个符号翻译成一系列的以b为基数的数字,它通常比哈夫曼编码更能达到最优熵编码。
[编辑]区间编码算术编码与区间编码有很深的相似渊源,它们如此相似以至于通常认为它们的性能是相同的,如果确实有什么不同的话也只是区间编码仅仅落后几个位的值而已。
区间编码与算术编码不同,通常认为它不被任何公司的专利所涵盖。
区间编码的原理是这样的,它没有像算术编码那样从[0,1]开始并根据每个字符出现的概率把它分成相应的不同的小区间,它从如000,000,000,000到999,999,999,999这样一个很大的非负整数区间开始,并且根据每个字符的概率划分成小的子区间。
当子区间小到一定程度最后结果的开头数字出现的时候,那些数字就能够“左移”出整个运算,并且用“右边”的数字替换--每次出现移位时,就大体相当于最初区间的一个回归放大(retroactive multiplication)。
[编辑]关于算术编码的美国专利许多算术编码所用的不同方法受美国专利的保护。
其中一些专利对于实现一些国际标准中定义的算术编码算法是很关键的。
在这种情况下,这些专利通常按照一种合理和非歧视(RAND)授权协议使用(至少是作为标准委员会的一种策略)。
在一些著名的案例中(包括一些涉及 IBM的专利)这些授权是免费的,而在另外一些案例中,则收取一定的授权费用。
RAND条款的授权协议不一定能够满足所有打算使用这项技术的用户,因为对于一个打算生产拥有所有权软件的公司来说这项费用是“合理的”,而对于自由软件和开源软件项目来说它是不合理的。
在算术编码领域做了很多开创性工作并拥有很多专利的一个著名公司是IBM。
一些分析人士感到那种认为没有一种实用并且有效的算术编码能够在不触犯IBM和其它公司拥有的专利条件下实现只是数据压缩界中的一种持续的都会传奇(尤其是当看到有效的算术编码已经使用了很长时间最初的专利开始到期)。
然而,由于专利法没有提供“明确界线”测试所以一种威慑心理总让人担忧法庭将会找到触犯专利的特殊应用,并且随着对于专利范围的详细审查将会发现一个不好的裁决将带来很大的损失,这些技术的专利保护然而对它们的应用产生了一种阻止的效果。
至少一种重要的压缩软件bzip2,出于对于专利状况的担心,故意停止了算术编码的使用而转向Huffman编码。
关于算术编码的美国专利列在下面。
•Patent 4,122,440 — (IBM) 提交日期 March 4, 1977, 批准日期 Oct 24, 1978 (现在已经到期)•Patent 4,286,256 — (IBM) 批准日期 Aug 25, 1981 (大概已经到期)•Patent 4,467,317 — (IBM) 批准日期 Aug 21, 1984 (大概已经到期)•Patent 4,652,856 — (IBM) 批准日期 Feb 4, 1986 (大概已经到期)•Patent 4,891,643 — (IBM) 提交时间 1986/09/15, 批准日期 1990/01/02 •Patent 4,905,297 — (IBM) 批准日期 Feb 27, 1990•Patent 4,933,883 — (IBM) 批准日期 Jun 12, 1990•Patent 4,935,882 — (IBM) 批准日期 Jun 19, 1990•Patent 4,989,000 — (???) 提交时间 1989/06/19, 批准日期 1991/01/29 •Patent 5,099,440•Patent 5,272,478 — (Ricoh)最新文件---------------- 仅供参考--------------------已改成-----------word文本--------------------- 方便更改。