加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

LZW算法

(2011-04-02 15:26:32)
标签:

杂谈

分类: 算法
LZW算法

在LZW算法中使用的术语与LZ78使用的相同,仅增加了术语前缀根(Root),它是由单个字符串组成的缀-符串(String)。在编码原理上,LZW与LZ78相比有如下差别:

 

LZW只输出代表词典中的缀-符串(String)的码字(code word)。这就意味在开始时词典不能是空的,它必须包含可能在字符流出现中的所有单个字符,即前缀根(Root)。

 

由于所有可能出现的单个字符都事先包含在词典中,每个编码步骤开始时都使用一字符前缀(one-character prefix),因此在词典中搜索的第1个缀-符串有两个字符。

(一) 编码算法

LZW编码是围绕称为词典的转换表来完成的。这张转换表用来存放称为前缀(Prefix)的字符序列,并且为每个表项分配一个码字(Code word),或者叫做序号,如表03-05-7所示。这张转换表实际上是把8位ASCII字符集进行扩充,增加的符号用来表示在文本或图像中出现的可变长度ASCII字符串。扩充后的代码可用9位、10位、11位、12位甚至更多的位来表示。Welch的论文中用了12位,12位可以有4096个不同的12位代码,这就是说,转换表有4096个表项,其中256个表项用来存放已定义的字符,剩下3840个表项用来存放前缀(Prefix)。

表03-05-7 词典

码字(Code word)

前缀(Prefix)

1

 

193

A

194

B

255

 

1305

abcdefxyF01234

LZW编码器(软件编码器或硬件编码器)就是通过管理这个词典完成输入与输出之间的转换。LZW编码器的输入是字符流(Charstream),字符流可以是用8位ASCII字符组成的字符串,而输出是用n位(例如12位)表示的码字流(Codestream),码字代表单个字符或多个字符组成的字符串。

LZW编码器使用了一种很实用的分析(parsing)算法,称为贪婪分析算法(greedy parsing algorithm)。在贪婪分析算法中,每一次分析都要串行地检查来自字符流Charstream的字符串,从中分解出已经识别的最长的字符串,也就是已经在词典中出现的最长的前缀Prefix。用已知的前缀Prefix加上下一个输入字符C也就是当前字符(Current character)作为该前缀的扩展字符,形成新的扩展字符串——缀-符串StringPrefix.C。这个新的缀-符串String是否要加到词典中,还要看词典中是否存有和它相同的缀-符串String。如果有,那么这个缀-符串String就变成前缀Prefix,继续输入新的字符,否则就把这个缀-符串String写到词典中生成一个新的前缀Prefix,并给一个代码。

LZW编码算法的具体执行步骤如下:

 

步骤1:开始时的词典包含所有可能的根(Root),而当前前缀P是空的;

 

步骤2:当前字符(C) :=字符流中的下一个字符;

 

步骤3:判断缀-符串P+C是否在词典中
  (1) 如果“是”:P:= P+C 即用C扩展P);
  (2) 如果“否”
     ①把代表当前前缀P的码字输出到码字流;
     ②把缀-符串P+C添加到词典;
     ③令P:= C ,即现在的P仅包含一个字符C;

 

步骤4:判断码字流中是否还有码字要译
  (1) 如果“是”,就返回到步骤2;
  (2) 如果“否”
     ①把代表当前前缀P的码字输出到码字流;
     ②结束。

LZW编码算法可用伪码表示。开始时假设编码词典包含若干个已经定义的单个码字,例如256个字符的码字,用伪码可以表示成:

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

Dictionary[j]← all n single-character,j=1, 2,…,n

     jn+1

    Prefix← read first Character in Charstream

    while((C ← next Character)!=NULL)

        Begin

            If Prefix.C is in Dictionary

               PrefixPrefix.C

            else

               CodestreamcW for Prefix

            Dictionary[j]Prefix.C

            jn+1

           PrefixC

        end

     CodestreamcW for Prefix

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

(二) 译码算法

LZW译码算法中还用到另外两个术语:①当前码字(Current code word):指当前正在处理的码字,用cW表示,用string.cW表示当前缀-符串;②先前码字(Previous code word):指先于当前码字的码字,用pW表示,用string.pW表示先前缀-符串。

LZW译码算法开始时,译码词典与编码词典相同,它包含所有可能的前缀根(roots)。LZW算法在译码过程中会记住先前码字(pW),从码字流中读当前码字(cW)之后输出当前缀-符串string.cW,然后把用string.cW的第一个字符扩展的先前缀-符串string.pW添加到词典中。

LZW译码算法的具体执行步骤如下:

 

步骤1:在开始译码时词典包含所有可能的前缀根(Root);

 

步骤2:cW:=码字流中的第一个码字;

 

步骤3:输出当前缀-符串string.cW到码字流;

 

步骤4:先前码字pW= 当前码字cW

 

步骤5:当前码字cW:= 码字流中的下一个码字;

 

步骤6:判断先前缀-符串string.pW是否在词典中
  (1) 如果“是”:
     ①把先前缀-符串string.pW输出到字符流;
     ②当前前缀P:=先前缀-符串string.pW
     ③当前字符C:=当前前缀-符串string.cW的第一个字符;
     ④把缀-符串P+C添加到词典;
  (2) 如果“否”:
     ①当前前缀P:=先前缀-符串string.pW
     ②当前字符C:=当前缀-符串string.cW的第一个字符;
     ③输出缀-符串P+C到字符流,然后把它添加到词典中。

 

步骤7:判断码字流中是否还有码字要译
  (1) 如果“是”,就返回到步骤4;
  (2) 如果“否”,结束。

LZW译码算法可用伪码表示如下:

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

Dictionary[j]← all n single-character,j=1, 2,…,n

jn+1

cW ← first code from Codestream

CharstreamDictionary[cW]

pWcW

While((cW ← next Code word)!=NULL)

     Begin

         If cW is in Dictionary

            CharstreamDictionary[cW]

            PrefixDictionary[pW]

            cW ← first Character of Dictionary[cW]

            Dictionary[j]Prefix.cW

            jn+1

            pWcW

        else

           PrefixDictionary[pW]

           cW ← first Character of Prefix

           CharstreamPrefix.cW

           Dictionary[j]Prefix.C

           pWcW

           jn+1

       end

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

例:编码字符串如表03-05-9所示,编码过程如表03-05-10所示。现说明如下:

 

“步骤”栏表示编码步骤;

 

“位置”栏表示在输入数据中的当前位置;

 

“词典”栏表示添加到词典中的缀-符串,它的索引在括号中;

 

“输出”栏表示码字输出。

表03-05-9 被编码的字符串

位置

1

2

3

4

5

6

7

8

9

字符

A

B

B

A

B

A

B

A

C

表03-05-10 LZW的编码过程

步骤

位置

词典

输出

 

 

(1)

A

 

 

 

(2)

B

 

 

 

(3)

C

 

1

1

(4)

A B

(1)

2

2

(5)

B B

(2)

3

3

(6)

B A

(2)

4

4

(7)

A B A

(4)

5

6

(8)

A B A C

(7)

6

--

--

--

(3)

表03-05-11解释了译码过程。每个译码步骤译码器读一个码字,输出相应的缀-符串,并把它添加到词典中。例如,在步骤4中,先前码字(2)存储在先前码字(pW)中,当前码字(cW)是(4),当前缀-符串string.cW是输出(“A B”),先前缀-符串string.pW ("B")是用当前缀-符串string.cW ("A")的第一个字符,其结果("B A") 添加到词典中,它的索引号是(6) 。

表03-05-11 LZW的译码过程

步骤

代码

词典

输出

 

 

(1)

A

 

 

 

(2)

B

 

 

 

(3)

C

 

1

(1)

--

--

A

(2)

(4)

A B

B

(2)

(5)

B B

B

(4)

(6)

B A

A B

(7)

(7)

A B A

A B A 

(3)

(8)

A B A C

C

LZW算法得到普遍采用,它的速度比使用LZ77算法的速度快,因为它不需要执行那么多的缀-符串比较操作。对LZW算法进一步的改进是增加可变的码字长度,以及在词典中删除老的缀-符串。在GIF图像格式和UNIX的压缩程序中已经采用了加上这些改进措施之后的LZW算法。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有