【前言】
变长编码,统计压缩编码都是基于单个字符的编码,字典编码基于数个连续字符(也叫基于单词),例如ABCABD中AB可以替换成一个新的字符,其可能会减少字符数量,得到的新数据的熵比原来的小,可以对新的数据再用统计编码
字典编码的困难之处在于如何选择最佳的单词,其会使得新数据集的熵变得更小。
出于性能上的考虑,不可能去遍历整个数据集来寻找最佳的单词,通常会去寻找最长匹配的数据,通过偏移+长度的方式记录匹配数据。
因此,字典编码适合有较多重复子串的情况
【LZ算法】
LZ算法是由Abraham Lempel和Jacob Ziv于1977年发明的算法,因此,该LZ算法也称为LZ77算法(或LZ1)。此算法是第一个使用字典压缩数据的算法,采用动态字典。1978年, 其发布了LZ78算法(也称为LZ2),使用静态字典。
原理
其将数据集分为两部分:一是搜索缓冲区,包含已经读取和处理过的符号;另一部分是先行缓冲区,包含将要编码的符号。
出于性能上的考虑,搜索缓冲区的大小一般为32kb,如果对性能没要求可以拓展到几个GB。对于特定大小的缓冲区,编码了N个字符,缓冲区要移动N位,这种就叫滑动窗口。
对先行缓冲区的字符,在搜索缓冲区中寻找匹配的字符,并记录其在搜索缓冲区的位置,将位置信息输出。
编码
以TOBEORNOTTOBE为例子,假设搜索缓冲区的长度为9
- 当前搜索缓冲区为空,长度为0,先行缓冲区为TOBEORNOTTOBE,从中读取一个字符T,未在搜索缓冲区找到匹配字符,输出记为<0,0,T>
- 当前搜索缓冲区为T,长度为1,先行缓冲区为OBEORNOTTOBE,从中读取一个字符O,未在搜索缓冲区找到匹配字符,输出记为<0,0,O>
- 当前搜索缓冲区为TO,长度为2,先行缓冲区为BEORNOTTOBE,从中读取一个字符B,未在搜索缓冲区找到匹配字符,输出记为<0,0,B>
- 当前搜索缓冲区为TOB,长度为3,先行缓冲区为EORNOTTOBE,从中读取一个字符E,未在搜索缓冲区找到匹配字符,输出记为<0,0,E>
- 当前搜索缓冲区为TOBE,长度为4,先行缓冲区为ORNOTTOBE,从中读取一个字符O,可以在搜索缓冲区中找到O,如何找
- 可以从搜索缓冲区中从前往后找,也可以从后往前找,考虑到一般文本是从左往右的,所以从左往右找
- O位于第2个位置,所以偏移为2
- 先行缓冲区中O后下一个字符为R,搜索缓冲区中O后下一个字符为B,不匹配,所以长度为1
- 输出为<2,1>
- 当前搜索缓冲区为TOBEO,长度为5,先行缓冲区为RNOTTOBE,从中读取一个字符R,未在搜索缓冲区找到匹配字符,输出记为<0,0,R>
- 当前搜索缓冲区为TOBEOR,长度为6,先行缓冲区为NOTTOBE,从中读取一个字符N,未在搜索缓冲区找到匹配字符,输出记为<0,0,N>
- 当前搜索缓冲区为TOBEORN,长度为7,先行缓冲区为OTTOBE,从中读取一个字符O,可以在搜索缓冲区中找到O,如何找
- 可以发现匹配的有两个O,基于性能上的考虑,找到O即可;基于压缩效率上的考虑,找到所有O,选择其中匹配最长的O
- 先行缓存区O后下一个字符为T,两个O的均不匹配,长度为1,选择第一O即可,输出为<2,1>
- 当前搜索缓冲区为TOBEORNO,长度为8,先行缓冲区为TTOBE,从中读取一个字符T,可以在搜索缓冲区中找到T,输出记为<1,1>
- 当前搜索缓冲区为TOBEORNOT,长度为9,先行缓冲区为TOBE,从中读取一个字符T,可以在搜索缓冲区中找到T,输出记为<1,1>
- 当前搜索缓冲区为TOBEORNOTT,长度为10,要滑动,滑动后为OBEORNOTT,先行缓冲区为OBE,从中读取一个字符O,可以在搜索缓冲区中找到OBE,输出记为<1,3>
解码
- 读取第1个输出<0,0,T>,此时搜索缓冲区为空,输出T
- 读取第2个输出<0,0,O>,此时搜索缓冲区为T,<0,0>表示没有匹配,输出O,结合之前输出为T0
- 读取第3个输出<0,0,B>,此时搜索缓冲区为TO,<0,0>表示没有匹配,输出B,结合之前输出为T0B
- 读取第4输出<0,0,E>,此时搜索缓冲区为TOB,<0,0>表示没有匹配,输出E,结合之前输出为T0BE
- 读取第5输出<2,1>,此时搜索缓冲区为TOBE,<2,1>表示第2个,长度为1,输出O,结合之前输出为T0BEO
- 读取第6输出<0,0,R>,此时搜索缓冲区为TOBEO,<0,0>表示没有匹配,输出R,结合之前输出为T0BEOR
- 读取第7输出<0,0,N>,此时搜索缓冲区为TOBEOR,<0,0>表示没有匹配,输出N,结合之前输出为T0BEORN
- 读取第8输出<2,1>,此时搜索缓冲区为TOBEORN,<2,1>表示第2个,长度为1,输出O,结合之前输出为T0BEORNO
- 读取第9输出<1,1>,此时搜索缓冲区为TOBEORNO,<1,1>表示第1个,长度为1,输出T,结合之前输出为T0BEORNOT
- 读取第10输出<1,1>,此时搜索缓冲区为TOBEORNOT,<1,1>表示第1个,长度为1,输出T,结合之前输出为T0BEORNOTT
- 读取第11输出<1,3>,此时搜索缓冲区要滑动,为OBEORNOTT,<1,3>表示第1个,长度为3,输出OBE,结合之前输出为T0BEORNOTTOBE
进一步简化输出
可以将输出分为三个对应的数据集分别进行统计压缩,进一步简化输出,例如
偏移集0,0,0,0,2,0,0,2,1,1,1
长度集0,0,0,0,1,0,0,1,1,1,3
字符集T,O,B,E,R,N
(只有偏移和长度都为0,才有字符)
算法拓展
为了提升性能或压缩率,在寻找匹配和输出记录上可以做自定义,可以衍生出各种不同版本的算法