LZ77算法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
LZ77算法
2011-09-07 16:39:23| 分类:图像处理| 标签:|字号大中小订阅
二.LZ77算法< XMLNAMESPACE PREFIX ="O" />
1977年,Jacob Ziv和Abraham Lempel描述了一种基于滑动窗口缓存的技术,该缓存用于保存最近刚刚处理的文本(J. Ziv and A. Lempel, “A Universal Algorithm for Sequential Data Compression”, IEEE Transaction on Information Theory, May 1977)。
这个算法一般称为IZ77。
LZ77和它的变体发现,在正文流中词汇和短语(GIF中的图像模式)很可能会出现重复。
当出现一个重复时,重复的序列可以用一个短的编码来代替。
压缩程序扫描这样的重复,同时生成编码来代替重复序列。
随着时间的过去,编码可以重用来捕获新的序列。
算法必须设计成解压程序能够在编码和原始数据序列推导出当前的映射。
在研究LZ77的细节之前,先看一个简单的例子(J. Weiss and D. Schremp, “Putting Data on a Diet”, IEEE Spectrum, August 1993)。
考虑这样一句话:
the brown fox jumped over the brown foxy jumping frog
这个短语的长度总共是53个八位组= 424 bit。
算法从左向右处理这个文本。
初始时,每个字符被映射成9 bit的编码,二进制的1跟着该字符的8 bit ASCII码。
在处理进行时,算法查找重复的序列。
当碰到一个重复时,算法继续扫描直到该重复序列终止。
换句话说,每次出现一个重复时,算法包括尽可能多的字符。
碰到的第一个这样的序列是the brown fox。
这个序列被替换成指向前一个序列的指针和序列的长度。
在这种情况下,前一个序列的the brown fox出现在26个字符之前,序列的长度是13个字符。
对于这个例子,假定存在两种编码选项:8 bit的指针和4 bit的长度,或者12 bit的指针和6 bit的长度。
使用2 bit的首部来指示选择了哪种选项,00表示第一种选项,01表示第二种选项。
因此,the brown fox的第二次出现被编码为<00b><26d><13 d >,或者00 00011010 1101。
压缩报文的剩余部分是字母y;序列<00b><27d><5 d >替换了由一个空格跟着jump组成的序列,以及字符序列ing frog。
图03-05-3演示了压缩映射的过程。
压缩过的报文由35个9 bit字符和两个编码组成,总长度为35 x 9 + 2 x 14 = 343比特。
和原来未压缩的长度为424比特的报文相比,压缩比为1.24。
图03-05-3 LZ77模式例
(一)压缩算法说明
LZ77(及其变体)的压缩算法使用了两个缓存。
滑动历史缓存包含了前面处理过的N个源字符,前向缓存包含了将要处理的下面L个字符(图03-05-4(a))。
算法尝试将前向缓存开始的两个或多个字符与滑动历史缓存中的字符串相匹配。
如果没有发现匹配,前向缓存的第一个字符作为9 bit的字符输出并且移入滑动窗口,滑动窗口中最久的字符被移出。
如果找到匹配,算法继续扫描以找出最长的匹配。
然后匹配字符串作为三元组输出(指示标记、指针和长度)。
对于K个字符的字符串,滑动窗口中最久的K个字符被移出,并且被编码的K个字符被移入窗口。
图03-05-4(b)显示了这种模式对于我们的例子的运行情况。
这里假定了39个字符的滑动窗口和13个字符的前向缓存。
在这个例子的上半部分,已经处理了前面的40个字符,滑动窗口
中是未压缩的最近的39个字符。
剩下的源字符串在前向窗口中。
压缩算法确定了下一个匹
配,从前向窗口将5个字符移入到滑动窗口中,并且输出了这个匹配字符串的编码。
经过这
些操作的缓存的状态显示在这个例子的下半部分。
(a)通用结构
(b)例子
图03-05-4 LZ77模式
尽管LZ77是有效的,对于当前的输入情况也是合适的,但是存在一些不足。
算法使用了有
限的窗口在以前的文本中查找匹配,对于相对于窗口大小来说非常长的文本块,很多可能的
匹配就会被丢掉。
窗口大小可以增加,但这会带来两个损失:(1)算法的处理时间会增加,
因为它必须为滑动窗口的每个位置进行一次与前向缓存的字符串匹配的工作;(2)<指针>字
段必须更长,以允许更长的跳转。
(二)压缩算法描述
为了更好地说明LZ77算法的原理,首先介绍算法中用到的几个术语:
<
输入数据流(input stream):待压缩处理的字符序列。
XMLNAMESPACE
PREFIX ="V"
/>
字符(character):输入数据流中的基本单元。
编码位置(coding position):输入数据流中当前要编码的字符位置,指前向缓冲器中的开始字符。
前向缓冲器(lookahead buffer):存放从编码位置到输入数据流结束的字符序列的存储器。
窗口(Window):指包含W个字符的窗口,字符是从编码位置开始向后数也就是最后处理的字符数。
指针(Pointer):指向窗口中的匹配串且含长度的指针。
LZ77编码算法的核心是查找从前向缓冲器开始的最长的匹配串。
算法的具体执行步骤如下:把编码位置设置到输入数据流的开始位置。
查找窗口中最长的匹配串。
输出(Pointer,Length) Characters,其中Pointer是指向窗口中匹配串的指针,Length表示匹配字符的长度,Characters是前向缓冲器中的第1个不匹配的字符。
如果前向缓冲器不是空的,则把编码位置和窗口向前移Length+1个字符,然后返回到步骤2。
例:待编码的数据流如表03-05-1所示,编码过程如表03-05-2所示。
现作如下说明:“步骤”栏表示编码步骤。
“位置”栏表示编码位置,输入数据流中的第1个字符为编码位置1。
“匹配”栏表示窗口中找到的最长的匹配串。
“字符”栏表示匹配之后在前向缓冲存储器中的第1个字符。
“输出”栏以(Back_chars,Chars_length) Explicit_character格式输出。
其中(Back_chars,Chars_length)是指指向匹配串的指针告诉译码器“在这个窗口中向后退Back_chars个字符然后拷贝Chars_length个字符到输出”,Explicit_character是真实字符。
例如,表3-13中的输出“(5,2) C”告诉译码器回退5个字符,然后拷贝2个字符“AB”
表03-05-1 待编码的数据流
表03-05-2 编码过程
(三)解压算法
对于LZ77压缩文本的解压很简单。
解压算法必须保存解压输出的最后N个字符。
当碰到编
码字符串时,解压算法使用<指针>,和<长度>,字段将编码替换成实际的正文字符串。
三.LZSS算法
LZ77通过输出真实字符解决了在窗口中出现没有匹配串的问题,但这个解决方案包含有冗
余信息。
冗余信息表现在两个方面,一是空指针,二是编码器可能输出额外的字符,这种字
符可能包含在下一个匹配串中。
LZSS算法以比较有效的方法解决这个问题,它的思想是如
果匹配串的长度比指针本身的长度长就输出指针,否则就输出真实字符。
由于输出的压缩数
据流中包含有指针和字符本身,为了区分它们就需要有额外的标志位,即ID位。
LZSS编码算法的具体执行步骤如下:
把编码位置置于输入数据流的开始位置。
在前向缓冲器中查找窗口中最长的匹配串
①Pointer :=匹配串指针。
②Length :=匹配串长度。
判断匹配串长度Length是否大于等于最小匹配串长度(MIN_LENGTH) ,
如果“是”:输出指针,然后把编码位置向前移动Length个字符。
如果“否”:输出前向缓冲存储器中的第1个字符,然后把编码位置向前移动一个字符。
如果前向缓冲器不是空的,就返回到步骤2。
例:编码字符串如表03-05-3所示,编码过程如表03-05-4所示。
现说明如下:
“步骤”栏表示编码步骤。
“位置”栏表示编码位置,输入数据流中的第1个字符为编码位置1。
“匹配”栏表示窗口中找到的最长的匹配串。
“字符”栏表示匹配之后在前向缓冲存储器中的第1个字符。
“输出”栏的输出为:
①如果匹配串本身的长度Length >= MIN_LENGTH,输出指向匹配串的指针,格式为(Back_chars,Chars_length)。
该指针告诉译码器“在这个窗口中向后退Back_chars个字符然后拷贝Chars_length个字符到输出”。
②如果匹配串本身的长度Length >= MIN_LENGTH,则输出真实的匹配串。
表03-05-3 输入数据流
表03-05-4
在相同的计算环境下,LZSS算法可获得比LZ77更高的压缩比,而译码同样简单。
这也就是
为什么这种算法成为开发新算法的基础。
许多后来开发的文档压缩程序都使用了LZSS的思
想,例如PKZip,ARJ,LHArc和ZOO等等,其差别仅仅是指针的长短、窗口的大小等有所不
同。
LZSS同样可以和熵编码联合使用,例如ARJ就与霍夫曼编码联用,而PKZip则与Shannon-Fano
联用,它的后续版本也采用霍夫曼编码。
四.LZ78算法
在介绍LZ78算法之前,首先说明在算法中用到的几个术语。
字符流(Charstream):待编码的数据序列。
字符(Character):字符流中的基本数据单元。
前缀(Prefix):在一个字符之前的字符序列。
缀-符串(String):前缀+字符。
码字(Code word):码字流中的基本数据单元,代表词典中的一串字符。
码流(Codestream):码字和字符组成的序列,是编码器的输出。
词典(Dictionary):缀-符串表。
按照词典中的索引号对每条缀-符串(String)指定一个码字(Code word)。
当前前缀(Current prefix):在编码算法中使用,指当前正在处理的前缀,用符号P表示。
当前字符(Current character):在编码算法中使用,指当前前缀之后的字符,用符号C表示。
当前码字(Current code word):在译码算法中使用,指当前处理的码字,用W表示当前码字,String.W表示当前码字的缀-符串。
(一) 编码算法
LZ78的编码思想是不断地从字符流中提取新的“缀-符串(String)”(通俗地理解为新“词条”),
然后用“代号”也就是码字(Code word)表示这个“词条”。
这样一来,对字符流的编码就变
成了用码字(Code word)去替换字符流,生成码字流,从而达到压缩数据的目的。
在编码开始时词典是空的,不包含任何缀-符串(string)。
在这种情况下编码器就输出一个表
示空字符串的特殊码字(例如“0”)和字符流中的第一个字符C,并把这个字符C添加到词典
中作为一个由一个字符组成的缀-符串。
在编码过程中,如果出现类似的情况,也照此办理。
在词典中已经包含某些缀-符串之后,如果“当前前缀P +当前字符C”已经在词典中,就用
字符C来扩展这个前缀,这样的扩展操作一直重复到获得一个在词典中没有的缀-符串为止。
此时就输出表示当前前缀P的码字(Code word)和字符C,并把P+C添加到词典中作为前缀
(Prefix),然后开始处理字符流中的下一个前缀。
LZ78编码器的输出是“码字-字符(W,C)”对,每次输出一对到码字流中,与码字W相对应
的缀-符串(String)用字符C进行扩展生成新的缀-符串,然后添加到词典中。
LZ78编码的具体算法如下:
步骤1:在开始时,词典和当前前缀P都是空的;
步骤2:当前字符C:=字符流中的下一个字符;
步骤3:判断P+C是否在词典中
(1) 如果“是”:用C扩展P,让P:= P+C ;
(2) 如果“否”:
①输出与当前前缀P相对应的码字和当前字符C;
②把字符串P+C添加到词典中;
③令P:=空值;
(3) 判断字符流中是否还有字符需要编码
①如果“是”:返回到步骤2;
②如果“否”:若当前前缀P不是空的,输出相应于当前前缀P的码字,然后结束编码。
(二) 译码算法
在译码开始时译码词典是空的,它将在译码过程中从码字流中重构。
每当从码字流中读入一
对码字-字符(W,C)对时,码字就参考已经在词典中的缀-符串,然后把当前码字的缀-符串
string.W和字符C输出到字符流(Charstream),而把当前缀-符串(string.W+C)添加到词典中。
在译码结束之后,重构的词典与编码时生成的词典完全相同。
LZ78译码的具体算法如下:
步骤1:在开始时词典是空的;
步骤2:当前码字W:=码字流中的下一个码字;
步骤3:当前字符C:= 紧随码字之后的字符;
步骤4:把当前码字的缀-符串(string.W)输出到字符流,然后输出字符C;
步骤5:把string.W+C添加到词典中;
步骤6:判断码字流中是否还有码字要译
(1) 如果“是”,就返回到步骤2;
(2) 如果“否”,则结束。
例:编码字符串如表03-05-5所示,编码过程如表03-05-6所示。
现说明如下:
“步骤”栏表示编码步骤;
“位置”栏表示在输入数据中的当前位置;
“词典”栏表示添加到词典中的缀-符串,缀-符串的索引等于“步骤”序号;
“输出”栏以(当前码字W,当前字符C)简化为(W,C)的形式输出。
表03-05-5 编码字符串
表03-05-6 编码过程
与LZ77相比,LZ78比较的数目,而压
缩率与LZ77类似。