零零散散学算法之详解数据压缩算法(下)

来源:互联网 发布:sql注入教程完整版 编辑:程序博客网 时间:2024/06/04 17:40

前序

      

       开始本文之前,先回顾一下上篇。上篇讲解了几种数据压缩算法中的两种:Huffman压缩算法和RLE压缩算法。

       详解数据压缩算法(上):http://blog.csdn.net/fengchaokobe/article/details/7934865

 

正文

 

       本文将详解数据压缩算法的后两种算法:Rice压缩算法、LZW压缩算法。

 

第一节 LZW压缩算法

 

       LZW压缩算法:Lempel-Ziv-Welch Encoding。算法的命名简单直接,故而LZW压缩算法也跟随了算法命名的特性:简单易懂!---有点扯皮了,开始干正事!

 

       LZW压缩算法的实现就是通过在编码的过程中建立一个字符串表,并用某个数字来表示这个串,压缩文件只存储那个数字。这些数字就是压缩后的数据。

 

       LZW算法的实现原理:由于源数据字符串中一定会出现重复的字符串,于是我们就利用这点,将第一次出现的串组合用某一数字表示,然后将这个数字保存起来。那么当再次遇见这个串时就可直接用这个数字来表示,以此来达到压缩的目的。所以说,能正确的得到这个数字表是压缩成功的关键。

       从原理中我们可得知:在源数据中,如果重复的字串越多,那么压缩的效果就越好。

 

       好了,理论说的再多也是空谈,既然了解了,那我们就上例子来说明。

       现有如下的源数据串,我们现用LZW压缩算法来操作实现压缩:

 

战前准备:

       1.原理中提到我们用某一数字来表示压缩的结果,那么这些数字的起始怎么选择呢?这时我们就要根据LZW压缩算法的应用来说了。LZW主要应用在图像的处理上,如果图像的色彩数为256,那我们就要从258开始(其中256表示清除码,257表示图像结束码)。

 

        2.编码过程中一些声明:

           源数据:压缩的目标数据;

           索引数组:编码后字符串表所对应数字表的匹配数组。

           匹配结果:Yes | No

          编码字符表:经过编码得到的结果。

         数字表标号:当有新的编码生成时会得到相应的标号。

 

万事俱备,进入状态:

       编码过程中须遵守如下的规则:

编码的过程:

       从表中我们可知:编码字符表中的字符和数字就是我们LZW编码的结果,我们只需将字符串表和数字表一一对应即可,最后我们只保留数字串。

 

       好了,这就是LZW压缩算法的编码过程。不过还有一个问题需要解决,就是编码的标号是从258开始,那么从多少结束呢?显然,我们不可能让其一直增大下去!于是,我们就规定一下:当标号达到4096时(GIF规范规定的是12位,超过12位的表达范围就得重来),我们就将整个标号集重新初始化,开始使用新的标记,提高了利用率和效率,何乐而不为呢!

 

       LZW压缩算法就讲完了,算法的实现过程大致的说明白了,不过有些细节还是没有讲到,以后如果遇见了一定会在文中补充!

 

最后一项,LZW编码算法的核心代码:

[cpp] view plaincopyprint?
  1. /***这个结构体是为了用结构体的成员表示编码串和数字值***/  
  2. typedef struct Dictionary{  
  3.       
  4.     int value;  /**编码的值**/  
  5.     unsigned char   prefix_string;  /**与prefix作比较**/  
  6.     unsigned char   char_add;   /**与suffix作比较**/  
  7. }Dictionary;  
  8. Dictionary  dict[MaxLength];    /**MaxLength = 4096**/  
[cpp] view plaincopyprint?
  1. /***LZW压缩算法的实现过程***/  
  2. int * lzw_coding(unsigned char *src_ch, unsigned int *Prefix_Suffix, int src_length, unsigned int *Char_Stream)  
  3. {  
  4. /***    src_ch表示源数据串 
  5.  ***    Prefix_Suffix表示索引数组 
  6.  ***    src_length表示源数据串的长度 
  7.  ***    Char_Stream表示经过编码后的数字表 
  8. ***/  
  9.     int i = 0;  
  10.     int j = 0;  
  11.     int k = 0;  
  12.     int temp = 0;  
  13.     int code = 258; //  从258开始  
  14.   
  15.     while(i < src_length)  
  16.     {  
  17.         Prefix_Suffix[j+1] = src_ch[i]; //源数据赋值给suffix  
  18.   
  19.         if(Prefix_Suffix[j] == 0)  
  20.         {  
  21.             Prefix_Suffix[j] = Prefix_Suffix[j+1];  
  22.             i++;  
  23.             continue;  
  24.         }  
  25.   
  26.         temp = compare(Prefix_Suffix[j], Prefix_Suffix[j + 1]);  
  27.         if(dict[temp].value != UNUSED)  //在串表中可以找到,UNUSED = -1  
  28.         {  
  29.             Prefix_Suffix[j] = dict[temp].value;    //如若相同变索引  
  30.         }  
  31.         else    //串表中找不到,如若不同则编码  
  32.         {  
  33.             dict[temp].value = code++;  
  34.             dict[temp].prefix_string = Prefix_Suffix[j];  
  35.             dict[temp].char_add = Prefix_Suffix[j+1];  
  36.   
  37.             Char_Stream[k++] = Prefix_Suffix[j];  
  38.             Prefix_Suffix[j] = Prefix_Suffix[j + 1];  
  39.         }  
  40.         i++;  
  41.     }  
  42.     return Char_Stream;  
  43. }  
[cpp] view plaincopyprint?
  1. /***比较索引数组的串是否在串表中出现过***/  
  2. int compare(unsigned char prefix, unsigned char suffix)  
  3. {  
  4.     int i = 0;  
  5.   
  6.     i = prefix % MaxLength;  
  7.       
  8.     while(1)  
  9.     {  
  10.         if(dict[i].value == UNUSED)  
  11.         {  
  12.             return i;  
  13.         }  
  14.   
  15.         if(dict[i].prefix_string = prefix && dict[i].char_add == suffix)  
  16.         {  
  17.             return i;  
  18.         }  
  19.           
  20.         i++;  
  21.     }  
  22. }  

参考文献:http://tech.watchstor.com/management-115343.htm

 

第二节 Rice压缩算法

 

       Rice编码:Rice encoding,是由Robert F. Rice发明的这个算法,我们直译过来就叫它“大米编码”。 

 

       Rice压缩算法的基本思想是:用较少的位来表示多个字(或数字),更重要的是,它能区分当前字(或数字)和下一个字(或数字)的位置

 

       有些人称RICE压缩算法是静态的Huffman编码算法,还是很有道理的。

       附注:Rice压缩算法一般都是对较小的数字进行操作,因为数字越小,它需要的位就越少。

 

       Rice压缩算法的原理:被除数 =除数 * 商 +余数。经过“大米”压缩算法压缩之后的结果就是由除数和余数组成的。

 

我先详细的解释一下这个原理:

       令 S = Q * M + R(Q和R的组合将会是S的压缩结果),压缩结果的表示:

       S:为将要压缩的数;

      M:是一个常数,M = 2K

       QQ = S >> K

       R:R = S & (M - 1),R用K位表示。

       其中对于K,K表示一个数的位数,而这个K是由一些数的平均系数来决定的。比如现有:10,12,14,18,36这五个数,你会发现前三个数的平均位数为4,于是这个K就是4了。也就是说,在某一范围内,一些数的出现次数较多,且这些数可用K位来表示,那么K就定下来了。

 

       好了,原理说明白了,我们用一个例子实现编码的过程:

       在“大米”压缩之前,我先说明一下常规方法,这样就可以和“大米”压缩算法作比较了,更加清晰的理解。

 

       常规情况下,我们对上述五个数编码,即就是:1010 1100 1110 10010 100100。试想想,如果这样的话,解码的时候你怎样区分当前数和下一个数呢?这就比较麻烦了。所以,Rice算法正好解决了这个问题。

 

       现对10,12,14,18,36这五个数用“大米”压缩算法进行编码,在原理中已经说明如何得到K,于是K = 4。那么就有:

       首先,对于10,12,14这三个数可直接用4位表示,即1010,1100,1110。解码的时候已知K = 4,所以很容易。

 

接下来,对于18,可表示为:10010,根据编码原理:

       M = 2K= 16;

       Q = S >> K = 18 >> 4 = 10010 >> 4 = 0b0001;Q转为十进制是1,根据表示方法,1的后面跟1个0,即10;

       R = S & (M - 1) = 18 & (16 - 1) = 10010 & 1111 = 0b0010

于是,压缩结果就是:100010。

 

最后,对于36,表示为:100100,根据编码原理:

       M = 2K= 16;

       Q = S >> K = 36 >> 4 = 100100 >> 4 = 0b0010;Q转为十进制的值是2,根据表示方法,1的后面跟2个0;

       R = S & (M - 1) = 36 & (M - 1) = 100100 & 1111 = 0b0100

于是压缩结果就是:1000100。

 

       怎么样,挺容易吧,这就是“大米”压缩算法。该算法的关键就是能辨别出当前数和下一个数的位置!解压缩的过程更简单了,我们已知K的值和Q与R的规范,直接解压就OK!

 

好了,最后就是压缩和解压缩的算法:

[cpp] view plaincopyprint?
  1. 压缩:  
  2. char * Rice_coding(char src)  
  3. {  
  4.     if(src & 0xf0 == 0) //可直接用K位表示,标志位置0  
  5.     {  
  6.         printf 直接输出K位;  
  7.     }  
  8.     else    //超过K位,标志位置1  
  9.     {  
  10.         Q = SRC >> K;  
  11.         temp_Q = (int)(Q & 0XFF);  
  12.           
  13.         R = src & (M - 1);  
  14.           
  15.         printf 1 + temp_Q + R;  
  16.     }  
  17. }  
[cpp] view plaincopyprint?
  1. 解压缩:  
  2. char Rice_decoding(char *src)  
  3. {  
  4.     if(标志位为0)   //源数据可用K位表示  
  5.     {  
  6.         直接取K位还原  
  7.     }  
  8.     else    //标志位为1  
  9.     {  
  10.         取Q的值,Q已知(从压缩过程中获得);  
  11.         R串 = src - Q串;  
  12.           
  13.         S = Q × M + R;  
  14.     }  
  15. }  
原创粉丝点击