算术编码(无损数据压缩)

转载自:http://blog.sina.com.cn/s/blog_b2e97e5a01019p30.html

算术编码是一种无损数据压缩方法,也是一种熵编码的方法。和其它熵编码方法不同的地方在于,其他的熵编码方法通常是把输入的消息分区为符号,然后对每个符号进行编码,而算术编码是直接把整个输入的消息编码为一个数,一个满足(0.0 ≤ n < 1.0)的小数n。

1、编码

算术编码将整个要编码的数据映射到一个位于[0,1)的实数区间中。并且输出一个小于1同时大于0的小数来表示全部数据。利用这种方法算术编码可以让压缩率无限的接近数据的熵值,从而获得理论上的最高压缩率。

算术编码进行编码时,从实数区间[0,1)开始。按照符号的频度将当前的区间分割成多个子区间。根据当前输入的符号选择对应的子区间,然后从选择的子区间 中继续进行下一轮的分割。不断的进行这个过程,直到所有符号编码完毕。对于最后选择的一个子区间,输出属于该区间的一个小数。这个小数就是所有数据的编 码。现在来举个例子。假设一份数据由“A”、“B”、“C”三个符号组成。现在要编码数据“BCCB”,编码过程如图所示。

算术编码(无损数据压缩)_第1张图片

首先说明一点,这里使用的是自适应模型。也就是说一开始时,三个符号的频度都是1。随着编码的进行再更新频度。另外,在计算时理论上要使用无限小数。这里为了说明方便,四舍五入到小数点后4位。

观察图可以发现算术编码的过程。首先,算术编码是从区间[0,1)开始的。这时三个符号的概率都是1 / 3,按照这个概率分割区间。第一个输入的符号是“B”,所以我们选择子区间[0.3333,0.6667)作为下一个区间。输入“B”后更新频度,根据新 的概率对区间[0.3333,0.6667)进行分割。这时输入的符号是“C”,我们可以选择子区间[0.5834,0.6667)。继续更新频度、分割 区间、选择子区间,直到符号全部编码完成。我们最后得到的区间是[0.6390,0.6501)。输出属于这个区间的一个小数,例如0.64。那么经过算 术编码的压缩,数据“BCCB”最后输出的编码就是0.64。

2、解码

算术编码进行解码时仅输入一个小数。解码前首先需要对区间[0,1)按照初始时的符号频度进行分割。然后观察输入的小数位于那个子区间。输出对应的符号,选择对应的子区间,然后从选择的子区间中继续进行下一轮的分割。不断的进行这个过程,直到所有的符号都解码出来。整个过程相当于编码时的逆运算。

在我们的例子中,输入的小数是0.64。首先,初始时三个符号的概率都是1 / 3,按照这个概率分割区间。观察图2.1可以发现0.64落在子区间[0.3333,0.6667)中,于是可以解码出“B”。并且选择子区间 [0.3333,0.6667)作为下一个区间。输出“B”后更新频度,根据新的概率对区间[0.3333,0.6667)进行分割。这时0.64落在子 区间[0.5834,0.6667)中,于是可以解码出“C”。按照上述过程进行,直到所有的符号都解码出来。可见,只需要一个小数就可以完整还原出原来 的所有数据。

3、编码过程

用[1,0)区间编码,显然是不科学的,况且,几乎是不可能的!因此采用整数编码方式,以下将以8位编码为例,阐述算术编码和解码过程。

1)以下将以字符串"ababcab"为例进行讲述.

2)首先我们已经知道要编码的字符串中只有a,b,c三种字符;

3)由以上信息,对下一图表进行初始化。

其中,high表示:8位的最大数字255,即,编码上界,low即0位表示的最小数字,亦即编码下界,a1表示:两条虚线之间为a,1表示在"自适应"中所用可能出现的字符,都初始其权值(个数)为1

high--------------255--------
    c1
------------------
    b1
------------------
    a1
low---------------0----------

4)现在开始编码:首先"ababcab"第1个字符为a。 

当前所有字符的个数总和total=3。low不变;high = low + 1*(high-low)/total;(其中1是当前a的个数)
于是:low=0;high = 0 + 1*(255-0)/3 = 85;且a的权值加1
high--------------85--------
    c1
------------------
    b1
------------------
    a2
low---------------0----------

5)"ababcab"第2个字符为b。

当前所有字符的个数总和total=4
low = low + <2*(high-low)/total>;(其中2是当前a的个数,"z = "运算表示:z = x/y + ((x%y) ? 1 : 0);)
high = low + 3*(high-low)/total;(其中3是当前a的个数加b的个数之和)
于是:
low= 0 + <2*(85-0)/4> = 43;
high = 0 + 3*(85-0)/4 = 63;
high--------------63--------
    c1
------------------
    b2
------------------
    a2
low---------------43----------

6)"ababcab"第3个字符为a.同理可得:

high--------------51--------
    c1
------------------
    b2
------------------
    a3
low---------------43----------

7)"ababcab"第4个字符为b.同理可得:

high--------------49--------
    c1
------------------
    b3
------------------
    a3
low---------------47----------

此时再进行下一轮,high=49,low=49于是问题出现了,因为是有限整数,每次又是取的上一区间的子区间,high和low趋于相等是必然的.
我们在此使用high和low向左移的方式进行扩大区间(极限情况:有资料提出,位移后low的二进制首位为0,其他位为全1,high的二进制首位为1,其他位为全0,如8位时:low=01111111B,high=10000000B的情况,暂时保留)

位移时保证两个原则:(1),保证high-low>=total否则继续位移,(2),保证[low, high]在上一个[low, high]的子区间.(其实,只需要位移即可,此条,已经在"z="运算是得到了保证)

两者左移1位后溢出位用新变量存储,假设为out,同理out到底存了几个位,我们用bits保存.
所以:

a),进行第1次位移
total=7
out=0B;(0B表示二进制0,例如:0x86=10000110B)
bits=1;
high=49<<1=98;
low=47<<1=94;
不满足"位移原则1"

b),进行第2次位移
total=7
out=00B;
bits=2;
high=98<<1=196;
low=94<<1=188;
high--------------196--------
    c1
------------------
    b3
------------------
    a3
low---------------188---------
满足"位移原则1",进行下一轮

8,"ababcab"第5个字符为c.同理可得:
high--------------196--------
    c2
------------------
    b3
------------------
    a3
low---------------195----------

由(7)可知,又要"位移"了

a),进行第3次位移
total=8
out=001B;
bits=3;
high=196<<1=136;
low=195<<1=134;
不满足"位移原则1"

b),进行第4次位移
total=8
out=0011B;
bits=4;
high=136<<1=16;
low=134<<1=12;
不满足"位移原则1"

c),进行第5次位移
total=8
out=00110B;
bits=5;
high=16<<1=32;
low=12<<1=24;
high--------------32--------
    c2
------------------
    b3
------------------
    a3
low---------------24---------
满足"位移原则1",进行下一轮

9,"ababcab"第6个字符为a.同理可得:
high--------------27--------
    c2
------------------
    b3
------------------
    a4
low---------------24----------
于是:

a),进行第6次位移
total=9
out=001100B;
bits=6;
high=27<<1=54;
low=24<<1=48;
不满足"位移原则1"

b),进行第7次位移
total=9
out=0011000B;
bits=7;
high=54<<1=108;
low=48<<1=96;
high--------------108--------
    c2
------------------
    b3
------------------
    a4
low---------------96---------
满足"位移原则1",进行下一轮

10,"ababcab"第7个字符为b.也是最后一个
high--------------105--------
    c2
------------------
    b4
------------------
    a4
low---------------102---------
high=105,low=102也是最后的区间,后面已经没有字符,我们无须"位移",仅记下此区间的某一个数即可,如:magic=104.

11,整个编码过程就结束了.现在我们得到了以下几条信息:

(a),原字符串只有a,b,c,初始区间是0-255

(b),out=0011000B; bits=7; magic=104.

4、解码过程

下面,我们将用以上(a),(b)两条信息,还原字符串"ababcab"

1,同样由信息(a)初始化图表,如下:
high--------------255--------
    c1
------------------170--------
    b1
------------------85---------
    a1
low---------------0----------

由信息(b),out=0011000B; bits=7; magic=104.

于是可以得到一个bits+sizeof(magic)*8 = 15位的数X = 001100001101000B = out<<8 | magic;
我们取X的高8位,得到d = 00110000B = 48;

2,显然我们可以从12中得知d = 00110000B = 48;是a所在区间,于是解压的字符串xcode = "a"
并且此时区间变成:(其实,现在得出一个结论:区间严格说是"[low, high)")
high--------------85--------
    c1
------------------63--------
    b1
------------------43--------
    a2
low---------------0---------

3,与13同理d = 00110000B = 48;是b所在区间,于是解压的字符串xcode = "ab"
区间变成:
high--------------63--------
    c1
------------------?---------
    b2
------------------51----------
    a2
low---------------43----------

4,与13同理d = 00110000B = 48;是a所在区间,于是解压的字符串xcode = "aba"
区间变成:
high--------------51---------
    c1
------------------49---------
    b2
------------------47---------
    a3
low---------------43----------
此时"位移原则1"仍满足,不位移

5,与13同理d = 00110000B = 48;是b所在区间,于是解压的字符串xcode = "abab"
区间变成:
high--------------49---------
    c1
------------------
    b3
------------------
    a3
low---------------47----------
此时"位移原则1"不满足,进行位移

6,d去掉首位,取得X接下来的8位,即
d = 01100001B = 97;
total=7;
high=98;
low=94
此时"位移原则1"不满足,再位移

d = 11000011B = 195;
total=7;
high=196;
low=188;

high--------------196---------
    c1
------------------195---------
    b3
------------------?-----------
    a3
low---------------47----------
此时"位移原则1"满足,下一轮位移

7,由17所得,d = 11000011B = 195;是c所在区间[low, high),于是解压的字符串xcode = "ababc"
区间变成:
high--------------196---------
    c2
------------------
    b3
------------------
    a3
low---------------195----------
此时:
total=8;
high=196;
low=195;
"位移原则1"不满足,位移

d = 10000110B = 131;
total=8;
high=high<<1=136;
low=low<<1=134;
"位移原则1"不满足,位移

d = 00001101B = 25;
total=8;
high=high<<1=32;
low=low<<1=24;
"位移原则1"满足,
区间变成:
high--------------32----------
    c2
------------------?-----------
    b3
------------------27----------
    a3
low---------------24----------

8,由18所得,d = 00001101B = 25;是a所在区间,于是解压的字符串xcode = "ababca"
区间变成:
high--------------27---------
    c2
------------------
    b3
------------------
    a4
low---------------24----------
此时:
total=9;
high=27;
low=25;
"位移原则1"不满足,位移
"0001101000B"
d = 00011010B = 26;
total=9;
high=high<<1=54;
low=low<<1=48;
"位移原则1"不满足,位移
d = 00110100B = 52;
total=9;
high=high<<1=108;
low=low<<1=96;
"位移原则1"满足,但是d = 00110100B = 52;不在[low, high)区间,于是d位移;
d = 01101000B = 104;(此时d已经用尽X)
high--------------108---------
    c2
------------------105---------
    b3
------------------102---------
    a4
low---------------96----------

9,由19所得,d = 01101000B = 104;是b所在区间,于是解压的字符串xcode = "ababcab"
区间变成:
high--------------105---------
    c2
------------------
    b3
------------------
    a4
low---------------102----------

"位移原则1"不满足,位移
d = 01101000B = 104;
total=9;
high=high<<1=164;
low=low<<1=152;
"位移原则1"满足,但是d = 01101000B = 104;不在[low, high)区间,于是d位移;
并且d用尽X不能再一次取,故解压完成!(若再知道total=9,也可以使解压程序退出).

10,总结,我们再一次看看11.我们提供的信息
(a),原字符串只有a,b,c,初始区间是0-255
(b),out=0011000B; bits=7; magic=104.


--------------------------------------------------------------------------------------------------

在第6步,我们"使用high和low向左移的方式进行扩大区间"(极限情况:有资料提出,位移后low的二进制首位为0,其他位为全1,high的二进制首位为1,其他位为全0,如8位时:low=01111111B,high=10000000B的情况,暂时保留)
貌似在我们的编码规范中,扩展后出现这个现象是不可能的.因为,位移时high,low都是左移1位.而资料多数是,high左移1位,low左移1位再加1.
但是,每次给high和low重新赋值时,产生上述情况是有可能的.
出现该现象时,我们可以规定,保留相同位,并将该区间扩展至原始区间,如8位是[0, 255);32位是[0, 0xffffffff);
Good Luck !

你可能感兴趣的:(数据结构与算法)