LZ77编码算法
来源:互联网 发布:购买的淘宝店铺安全吗 编辑:程序博客网 时间:2024/06/06 19:33
拟的,可以跟随压缩进程滑动的窗口作为术语字典,要压缩的字符串如果在该窗口
中出现,则输出其出现位置和长度。使用固定大小窗口进行术语匹配,而不是在所
有已经编码的信息中匹配,是因为匹配算法的时间消耗往往很多,必须限制字典的
大小才能保证算法的效率;随着压缩的进程滑动字典窗口,使其中总包含最近编码
过的信息,是因为对大多数信息而言,要编码的字符串往往在最近的上下文中更容
易找到匹配串。
参照下图,让我们熟悉一下 LZ77 算法的基本流程。
1、从当前压缩位置开始,考察未编码的数据,并试图在滑动窗口中找出最长的匹
配字符串,如果找到,则进行步骤 2,否则进行步骤 3。
2、输出三元符号组 ( off, len, c )。其中 off 为窗口中匹配字符串相对窗口边
界的偏移,len 为可匹配的长度,c 为下一个字符。然后将窗口向后滑动 len + 1
个字符,继续步骤 1。
3、输出三元符号组 ( 0, 0, c )。其中 c为下一个字符。然后将窗口向后滑动
len + 1 个字符,继续步骤 1。
我们结合实例来说明。假设窗口的大小为 10 个字符,我们刚编码过的 10 个字符
是:abcdbbccaa,即将编码的字符为:abaeaaabaee
我们首先发现,可以和要编码字符匹配的最长串为 ab ( off = 0, len = 2 ), ab
的下一个字符为 a,我们输出三元组:( 0, 2, a )
现在窗口向后滑动 3 个字符,窗口中的内容为:dbbccaaaba
下一个字符 e 在窗口中没有匹配,我们输出三元组:( 0, 0, e )
窗口向后滑动 1 个字符,其中内容变为:bbccaaabae
我们马上发现,要编码的 aaabae 在窗口中存在( off = 4, len = 6 ),其后的字
符为 e,我们可以输出:( 4, 6, e )
这样,我们将可以匹配的字符串都变成了指向窗口内的指针,并由此完成了对上述
数据的压缩。
解压缩的过程十分简单,只要我们向压缩时那样维护好滑动的窗口,随着三元组的
不断输入,我们在窗口中找到相应的匹配串,缀上后继字符 c 输出(如果 off 和
len 都为 0 则只输出后继字符 c )即可还原出原始数据。
当然,真正实现 LZ77 算法时还有许多复杂的问题需要解决,下面我们就来对可能
碰到的问题逐一加以探讨。
编码方法
我们必须精心设计三元组中每个分量的表示方法,才能达到较好的压缩效果。一般
来讲,编码的设计要根据待编码的数值的分布情况而定。对于三元组的第一个分量
——窗口内的偏移,通常的经验是,偏移接近窗口尾部的情况要多于接近窗口头部
的情况,这是因为字符串在与其接近的位置较容易找到匹配串,但对于普通的窗口
大小(例如 4096 字节)来说,偏移值基本还是均匀分布的,我们完全可以用固定
的位数来表示它。
编码 off 需要的位数 bitnum = upper_bound( log2( MAX_WND_SIZE ))
由此,如果窗口大小为 4096,用 12 位就可以对偏移编码。如果窗口大小为
2048,用 11 位就可以了。复杂一点的程序考虑到在压缩开始时,窗口大小并没有
达到 MAX_WND_SIZE,而是随着压缩的进行增长,因此可以根据窗口的当前大小动
态计算所需要的位数,这样可以略微节省一点空间。
对于第二个分量——字符串长度,我们必须考虑到,它在大多数时候不会太大,少
数情况下才会发生大字符串的匹配。显然可以使用一种变长的编码方式来表示该长
度值。在前面我们已经知道,要输出变长的编码,该编码必须满足前缀编码的条件
。其实 Huffman 编码也可以在此处使用,但却不是最好的选择。适用于此处的好
的编码方案很多,我在这里介绍其中两种应用非常广泛的编码。
第一种叫 Golomb 编码。假设对正整数 x 进行 Golomb 编码,选择参数m,令
b = 2m
q = INT((x - 1)/b)
r = x - qb - 1
则 x 可以被编码为两部分,第一部分是由 q 个 1 加 1 个 0 组成,第二部分为
m 位二进制数,其值为 r。我们将 m = 0, 1, 2, 3 时的 Golomb 编码表列出:
-------------------------------------------------------------