关于LZW压缩算法[转]

来源:互联网 发布:java hashcode 编辑:程序博客网 时间:2024/05/18 02:28

关于LZW压缩算法


最近因为工作需要,研究了一下LZW压缩算法。觉得挺有意思,而且实现起来,也不是它“看上去的”那般容易——我前后折腾了5天才写出能正确工作的代码(上周4,5 + 周末 + 本周1,2)。写在blog里面,一来给自己留个笔记,另外如果有相同兴趣的人看到了,也许能有一些小小的帮助。

LZW压缩算法给予是基于字典的LZ系列算法的改进型,gif文件格式中使用了这种算法来压缩位图,这个算法一直作为unisys公司的专利而存在,所以在开源的世界里,这个算法以前用的不多。我记得好像是到去年这个专利才在全球所有地方过期,现在很多开源的库里面又已经重新打开了对它的支持。

 

 

LZW算法的精义在于它不断的读取字串,如果在字典中找到匹配,那么继续读下去,一直读到字典里没有为止,然后把它加入字典。给我的感觉是一个非常“贪婪”的算法。下面是算法的伪码:

 


 


string w;
char c;
while (c = getc() != eof)
{
 if (wc 不在 字典)
 {
  添加 wc 到 字典
  输出 w 对应的码
  w = c;
 }
 else
 {
  w = wc;
 }
}

 



注意,初始状态的字典并非为空,在我的实现里,字典初始状态具有256个项,所以第一步的时候wc一定在字典里(注意w此时为空),这样这个算法就能循环起来了。字典大小是有限制的,我这里实现了4096个项。字典满了以后,复位字典再重头开始。这里,LZW的精华就在这里,为什么可以复位字典?因为LZW压缩算法不需要放一个大块头字典到它的压缩数据里,在解压缩的时候,可以根据输入重新生成字典。因此我这里复位字典,解压缩的算法也知道该复位字典,它会再重新生成字典。

 

举一个例子:
abababab
w      c      d      i      o

----------------------------------------
        a
a      b     ab    256    a
b      a     ba    257    b
a      b    (ab字典里有了)
ab    a     aba  258   256(ab)
a      b 
ab   eof                  256

所以,最后的输出是ab<256><256>,我们用12个bit来表示一个输出的码。一共是48个bit=6byte,略有压缩。当字典的项多起来以后,一般会得到一个尚可的压缩比。

 

 



解压缩:
也是伪码实现:
string w;
code c;
while (c = get_code() != eof)
{
 在字典中找到c对应的串str,输出str

 

 把w+str[0]加入到字典
 w = str对应的串;
}

好像很简单,但是有一种情况比较特殊。比如说我有aaaaa,编码得到a<256><256>

 

看看我们怎么解码:


w      c      d      i       o
        a
a      256 -------->此时256不存在,怎么办?
这时候,256对应的串=w+w[0],所以这里就是aa,把aa=256代入,就又能继续了。至于为什么这样,自己再想想:)

 

 

 



就我自己实现这个算法,我觉得有几个地方需要注意:
1. 字典的维护
  我看过一些实现,字典项都是用new,malloc之类生成的,我觉得这样效率会很低(不论是时间效率还是空间效率,注意一般crt你分配的内存越小,内存的利用率就越低,在loki的那本书里讲的很透彻,这里不罗嗦了),尤其在嵌入式系统上。所以我使用了一个简单的池。分配内存变得非常直观。在pc上,用池比用malloc节约了大约30%的时间

 

2. 字典的查找
  LZW大量的依赖于快速的字典查找。基本上每读入一个char,就需要在字典里查一次,毫不夸张地说,就是查找字典的效率,决定了压缩算法的速度。我这里的实现是一个大小为256的hash表,对于最大为4096个项的字典来说,如果hash函数足够好的话,比r-b tree效率略差,但相差不大(前者平均8次比较 + 一次hash,后者平均6次比较)。但是我现在已经没有兴趣去修改实现了:) 等以后再说。

原创粉丝点击