• 数据压缩之ROLZ字典编码


      在字典编码中,最常用的恐怕要算LZ77编码了。LZ77的思想很简单,就是用一个<offset, length>元组来表示当前位置的字节串在前offset个字节中出现过。正是由于这个简单的思想,所有基于LZ77实现的实用算法都有着不错的解压速度。经典的使用LZ77编码的压缩算法有zip/gz的deflate算法,7z的lzma算法等。

      在对LZ77算法研究中,我们也发现算法中的一些不足之处,LZ77最明显的不足是offset值的过度零散导致对<offset, length>元组的后续处理效果不好。例如处理一个16MB的数据块,一个<offset, length>中offset的取值就有16777216种之多,虽然可以对offset进行分段处理,但还是或多或少会影响到压缩率的提升。

      于是我们有了ROLZ算法,ROLZ全称是Reduced Offset Lempel Ziv,即减少了offset的LZ编码。在ROLZ中,重复串出现的位置不再是用相对当前的偏移量offset,而是用一个表项编号index来表示。

      在详细介绍ROLZ算法之前,我们先介绍一个压缩算法中常用的概念——上下文(context),上下文就是当前编码位置之前的内容,在实际应用中,我们只取当前编码位置前k个字符做为上下文,称为k阶上下文。上下文是提高压缩率的一个重要工具,例如字符串"queen",如果用一般的统计模型来处理,由于英语中字母"u"出现频率不高,我们可能会给"u"分配一个长的前缀编码。但考虑一阶上下文"q",在英语中"q"后面出现"u"的概率非常高,我们就会给"u"分配相对较短的前缀编码,从而利用上下文提高了编码效率。

      在编码中,我们总会保存每个上下文的一些信息,但是当上下文阶数k >= 3时,上下文的个数会急剧升高,当k=4时就已经有256^4 = 4GB的上下文各数。所以对于较长的上下文,我们往往取一个对前k个字符的一个Hash值作为上下文,这样的结果是可能几个完全不同的上下文会占用相同的存储空间,导致上下文的预测准确率有所降低,但这种做法满足了程序对内存的要求,实际中也被广泛使用。

      现在进入正题,在ROLZ算法中,我们会建立一张二维表rolz_table[context][index],在处理完每个位置后,我们将这个位置存入对应上下文的桶中,以下就字符串"construct- destruct"举个例子(考虑一阶上下文,假设当前编码到位置12):

    0 1 2 3 4 5 6 7 8 9 10 11 $ 13 14 15 16 17
    c o n s t r u c t - d  e  s t  r  u  c  t
    
    table["c"] = {8, 1}
    table["o"] = {2}
    table["n"] = {3}
    table["s"] = {4}
    table["t"] = {9, 5}
    table["r"] = {6}
    table["u"] = {7}
    table["-"] = {10}
    table["d"] = {11}

      如果是传统的LZ77算法,这时候我们应该输出一个匹配对<offset=9, length=6>表示重复串"struct"。但在ROLZ算法中,我们只在对应上下文的桶中寻找匹配,在当前上下文"e"的桶中找不到匹配(因为是一个空的桶),只好将"s"原样输出,然后将当前位置加入上下文"e"的桶中:

    0 1 2 3 4 5 6 7 8 9 10 11 12 $ 14 15 16 17
    c o n s t r u c t - d  e  s  t r  u  c  t
    
    table["c"] = {8, 1}
    table["o"] = {2}
    table["n"] = {3}
    table["s"] = {4}
    table["t"] = {9, 5}
    table["r"] = {6}
    table["u"] = {7}
    table["-"] = {10}
    table["d"] = {11}
    table["e"] = {12}

      现在编码位置13的"t",我们在上下文"s"的桶中寻找匹配,发现table["s"][0]就是我们要找的匹配,于是匹配成功,输出一个ROLZ匹配项<index=0, length=5>表示当前位置的重复串"truct"。

      所以,对字符串"construct-destruct",LZ77和ROLZ的编码结果分别如下:

    LZ77: construct-de<9,6>
    ROLZ: construct-des<0,5>

      可以看到ROLZ的输出多了一个字符"s",但是offset变成了更容易处理的index,实践表明对大量输入的时候ROLZ通常能得到更好的压缩率。

      ROLZ的编码实现比LZ77要简单得多,因为rolz_table本身就是一张很好的散列表,我们不需要像LZ77那样另外创建一个查找结构。而解压相对LZ77要复杂一些,我们必须和压缩过程一样构建起整个rolz_table,然后通过查表将index转换为重复串的位置,所以ROLZ解压相对LZ77要慢一些。

      我个人有一个ROLZ+算术编码的完整实现comprolz,目前压缩率已经和7z相当。下面也附上一个简单的ROLZ+Polar编码实现(压缩率略高于gz),使用3阶Hash上下文,每个上下文有15个桶项:

      1 /*******************************************************************************
      2  * RichSelian's nooblike compressor: ROLZ + Polar coding
      3  ******************************************************************************/
      4 #include <stdio.h>
      5 #include <string.h>
      6 
      7 /*******************************************************************************
      8  * POLAR Coder
      9  ******************************************************************************/
     10 #define POLAR_SYMBOLS   512 /* should be even */
     11 #define POLAR_MAXLEN    15  /* should be less than 16, so we can pack two length values into a byte */
     12 
     13 #define M_round_down(x)     while((x)&(-(x)^(x))) { (x) &= (-(x)^(x)); }
     14 #define M_round_up(x)       while((x)&(-(x)^(x))) { (x) &= (-(x)^(x)); } (x) <<= 1;
     15 #define M_int_swap(x, y)    {int (_)=(x); (x)=(y); (y)=(_);}
     16 
     17 int polar_make_leng_table(const int* freq_table, int* leng_table) {
     18     int symbols[POLAR_SYMBOLS];
     19     int i;
     20     int s;
     21     int total;
     22     int shift = 0;
     23 
     24     memcpy(leng_table, freq_table, POLAR_SYMBOLS * sizeof(int));
     25 
     26 MakeTablePass:
     27     /* sort symbols */
     28     for(i = 0; i < POLAR_SYMBOLS; i++) {
     29         symbols[i] = i;
     30     }
     31     for(i = 0; i < POLAR_SYMBOLS; i++) {
     32         if(i > 0 && leng_table[symbols[i - 1]] < leng_table[symbols[i]]) {
     33             M_int_swap(symbols[i - 1], symbols[i]);
     34             i -= 2;
     35         }
     36     }
     37 
     38     /* calculate total frequency */
     39     total = 0;
     40     for(i = 0; i < POLAR_SYMBOLS; i++) {
     41         total += leng_table[i];
     42     }
     43 
     44     /* run */
     45     M_round_up(total);
     46     s = 0;
     47     for(i = 0; i < POLAR_SYMBOLS; i++) {
     48         M_round_down(leng_table[i]);
     49         s += leng_table[i];
     50     }
     51     while(s < total) {
     52         for(i = 0; i < POLAR_SYMBOLS; i++) {
     53             if(s + leng_table[symbols[i]] <= total) {
     54                 s += leng_table[symbols[i]];
     55                 leng_table[symbols[i]] *= 2;
     56             }
     57         }
     58     }
     59 
     60     /* get code length */
     61     for(i = 0; i < POLAR_SYMBOLS; i++) {
     62         s = 2;
     63         if(leng_table[i] > 0) {
     64             while((total / leng_table[i]) >> s != 0) {
     65                 s += 1;
     66             }
     67             leng_table[i] = s - 1;
     68         } else {
     69             leng_table[i] = 0;
     70         }
     71 
     72         /* code length too long -- scale and rebuild table */
     73         if(leng_table[i] > POLAR_MAXLEN) {
     74             shift += 1;
     75             for(i = 0; i < POLAR_SYMBOLS; i++) {
     76                 if((leng_table[i] = freq_table[i] >> shift) == 0 && freq_table[i] > 0) {
     77                     leng_table[i] = 1;
     78                 }
     79             }
     80             goto MakeTablePass;
     81         }
     82     }
     83     return 0;
     84 }
     85 
     86 int polar_make_code_table(const int* leng_table, int* code_table) {
     87     int i;
     88     int s;
     89     int t1;
     90     int t2;
     91     int code = 0;
     92 
     93     memset(code_table, 0, POLAR_SYMBOLS * sizeof(int));
     94 
     95     /* make code for each symbol */
     96     for(s = 1; s <= POLAR_MAXLEN; s++) {
     97         for(i = 0; i < POLAR_SYMBOLS; i++) {
     98             if(leng_table[i] == s) {
     99                 code_table[i] = code;
    100                 code += 1;
    101             }
    102         }
    103         code *= 2;
    104     }
    105 
    106     /* reverse each code */
    107     for(i = 0; i < POLAR_SYMBOLS; i++) {
    108         t1 = 0;
    109         t2 = leng_table[i] - 1;
    110         while(t1 < t2) {
    111             code_table[i] ^= (1 & (code_table[i] >> t1)) << t2;
    112             code_table[i] ^= (1 & (code_table[i] >> t2)) << t1;
    113             code_table[i] ^= (1 & (code_table[i] >> t1)) << t2;
    114             t1++;
    115             t2--;
    116         }
    117     }
    118     return 0;
    119 }
    120 
    121 int polar_make_decode_table(const int* leng_table, const int* code_table, int* decode_table) {
    122     int i;
    123     int c;
    124 
    125     for(c = 0; c < POLAR_SYMBOLS; c++) {
    126         if(leng_table[c] > 0) {
    127             for(i = 0; i + code_table[c] < 65536; i += (1 << leng_table[c])) {
    128                 decode_table[i + code_table[c]] = c;
    129             }
    130         }
    131     }
    132     return 0;
    133 }
    134 
    135 /*******************************************************************************
    136  * ROLZ
    137  ******************************************************************************/
    138 #define ROLZ_BUCKET_SIZE    65536
    139 #define MATCH_IDX_SIZE      15  /* make element of rolz_table[] 64 Bytes */
    140 #define MATCH_LEN_MIN       2
    141 #define MATCH_LEN_MAX       17  /* MATCH_LEN_MAX < MATCH_LEN_MIN + (POLAR_SYMBOLS-256) / MATCH_IDX_SIZE */
    142 
    143 static int ch1 = 0;
    144 static int ch2 = 0;
    145 static int ch3 = 0;
    146 
    147 #define M_rolz_item(x, n)  (rolz_table[(x)].m_item[(rolz_table[(x)].m_head + MATCH_IDX_SIZE - (n)) % MATCH_IDX_SIZE])
    148 
    149 static struct {
    150     unsigned int  m_item[MATCH_IDX_SIZE];
    151     unsigned char m_head;
    152 } rolz_table[ROLZ_BUCKET_SIZE];
    153 
    154 static inline unsigned int rolz_context() {
    155     return (unsigned)(ch1 * 131313131 + ch2 * 131313 + ch3 * 131) % ROLZ_BUCKET_SIZE;
    156 }
    157 
    158 static inline void rolz_update_context(unsigned char* buf, int pos, int cache) {
    159     rolz_table[rolz_context()].m_head = (rolz_table[rolz_context()].m_head + 1) % MATCH_IDX_SIZE;
    160     if(cache) {
    161         M_rolz_item(rolz_context(), 0) = pos | (buf[pos] << 24);
    162     } else {
    163         M_rolz_item(rolz_context(), 0) = pos;
    164     }
    165     ch3 = ch2;
    166     ch2 = ch1;
    167     ch1 = buf[pos];
    168     return;
    169 }
    170 
    171 int rolz_encode(unsigned char* ibuf, unsigned short* obuf, int ilen) {
    172     int olen = 0;
    173     int pos = 0;
    174     int i;
    175     int j;
    176     int match_idx;
    177     int match_len;
    178 
    179     memset(rolz_table, 0, sizeof(rolz_table));
    180     ch3 = 0;
    181     ch2 = 0;
    182     ch1 = 0;
    183     while(pos < ilen) {
    184         match_len = MATCH_LEN_MIN - 1;
    185         match_idx = -1;
    186         if(pos + MATCH_LEN_MAX < ilen) { /* find match */
    187             for(i = 0; i < MATCH_IDX_SIZE; i++) {
    188                 if(M_rolz_item(rolz_context(), i) == 0 || M_rolz_item(rolz_context(), i) >> 24 != ibuf[pos]) {
    189                     continue;
    190                 }
    191 
    192                 j = 1;
    193                 while(j < MATCH_LEN_MAX && ibuf[pos + j] == ibuf[(M_rolz_item(rolz_context(), i) & 0x00ffffff) + j]) {
    194                     j++;
    195                 }
    196                 if(j > match_len) {
    197                     match_len = j;
    198                     match_idx = i;
    199                 }
    200             }
    201         }
    202         if(match_len < MATCH_LEN_MIN) {
    203             match_len = 1;
    204             match_idx = -1;
    205         }
    206 
    207         if(match_idx == -1) { /* encode */
    208             obuf[olen++] = ibuf[pos];
    209         } else {
    210             obuf[olen++] = 256 + (match_len - MATCH_LEN_MIN) * MATCH_IDX_SIZE + match_idx;
    211         }
    212 
    213         for(i = 0; i < match_len; i++) { /* update context */
    214             rolz_update_context(ibuf, pos, 1);
    215             pos += 1;
    216         }
    217     }
    218     return olen;
    219 }
    220 
    221 int rolz_decode(unsigned short* ibuf, unsigned char* obuf, int ilen) {
    222     int olen = 0;
    223     int pos = 0;
    224     int i;
    225     int match_pos;
    226     int match_idx;
    227     int match_len;
    228 
    229     ch3 = 0;
    230     ch2 = 0;
    231     ch1 = 0;
    232     for(pos = 0; pos < ilen; pos++) {
    233         if(ibuf[pos] < 256) { /* decode */
    234             match_idx = -1;
    235             match_len = 1;
    236             obuf[olen++] = ibuf[pos];
    237         } else {
    238             match_idx = (ibuf[pos] - 256) % MATCH_IDX_SIZE;
    239             match_len = (ibuf[pos] - 256) / MATCH_IDX_SIZE + MATCH_LEN_MIN;
    240         }
    241 
    242         if(match_idx != -1) { /* expand match */
    243             match_pos = M_rolz_item(rolz_context(), match_idx);
    244             for(i =-0; i < match_len; i++) {
    245                 obuf[olen++] = obuf[match_pos + i];
    246             }
    247         }
    248         for(i = 0; i < match_len; i++) { /* update context */
    249             rolz_update_context(obuf, olen - match_len + i, 0);
    250         }
    251     }
    252     return olen;
    253 }
    254 
    255 /*******************************************************************************
    256  * MAIN
    257  ******************************************************************************/
    258 int main(int argc, char** argv) {
    259     static unsigned char  ibuf[10000000]; /* blocksize = 10MB */
    260     static unsigned short rbuf[10000000];
    261     static unsigned char  obuf[12000000];
    262     int ilen;
    263     int rlen;
    264     int olen;
    265     int rpos;
    266     int opos;
    267     int i;
    268     int freq_table[POLAR_SYMBOLS];
    269     int leng_table[POLAR_SYMBOLS];
    270     int code_table[POLAR_SYMBOLS];
    271     int decode_table[1 << (POLAR_MAXLEN + 1)];
    272     int code_buf;
    273     int code_len;
    274 
    275     if(argc == 2 && strcmp(argv[1], "e") == 0) {
    276         while((ilen = fread(ibuf, 1, sizeof(ibuf), stdin)) > 0) {
    277             rlen = rolz_encode(ibuf, rbuf, ilen);
    278             olen = 0;
    279 
    280             memset(freq_table, 0, sizeof(freq_table));
    281             code_buf = 0;
    282             code_len = 0;
    283 
    284             for(i = 0; i < rlen; i++) {
    285                 freq_table[rbuf[i]] += 1;
    286             }
    287             polar_make_leng_table(freq_table, leng_table);
    288             polar_make_code_table(leng_table, code_table);
    289 
    290             /* write length table */
    291             for(i = 0; i < POLAR_SYMBOLS; i += 2) {
    292                 obuf[olen++] = leng_table[i] * 16 + leng_table[i + 1];
    293             }
    294 
    295             /* encode */
    296             for(i = 0; i < rlen; i++) {
    297                 code_buf += code_table[rbuf[i]] << code_len;
    298                 code_len += leng_table[rbuf[i]];
    299                 while(code_len > 8) {
    300                     obuf[olen++] = code_buf % 256;
    301                     code_buf /= 256;
    302                     code_len -= 8;
    303                 }
    304             }
    305             if(code_len > 0) {
    306                 obuf[olen++] = code_buf;
    307                 code_buf = 0;
    308                 code_len = 0;
    309             }
    310             fwrite(&rlen, sizeof(rlen), 1, stdout);
    311             fwrite(&olen, sizeof(olen), 1, stdout);
    312             fwrite(obuf, 1, olen, stdout);
    313         }
    314         return 0;
    315     }
    316     if(argc == 2 && strcmp(argv[1], "d") == 0) {
    317         while(fread(&rlen, sizeof(rlen), 1, stdin) == 1 && fread(&olen, sizeof(olen), 1, stdin) == 1) {
    318             olen = fread(obuf, 1, olen, stdin);
    319             rpos = 0;
    320             opos = 0;
    321             code_buf = 0;
    322             code_len = 0;
    323 
    324             /* read length table */
    325             for(i = 0; i < POLAR_SYMBOLS; i += 2) {
    326                 leng_table[i] =     obuf[opos] / 16;
    327                 leng_table[i + 1] = obuf[opos] % 16;
    328                 opos++;
    329             }
    330 
    331             /* decode */
    332             polar_make_code_table(leng_table, code_table);
    333             polar_make_decode_table(leng_table, code_table, decode_table);
    334 
    335             while(rpos < rlen) {
    336                 while(opos < olen && code_len < POLAR_MAXLEN) {
    337                     code_buf += obuf[opos++] << code_len;
    338                     code_len += 8;
    339                 }
    340                 i = decode_table[code_buf % 65536];
    341 
    342                 rbuf[rpos++] = i;
    343                 code_buf >>= leng_table[i];
    344                 code_len -=  leng_table[i];
    345             }
    346 
    347             ilen = rolz_decode(rbuf, ibuf, rlen);
    348             fwrite(ibuf, 1, ilen, stdout);
    349         }
    350     }
    351     return -1;
    352 }
  • 相关阅读:
    【插件开发】—— 10 JFace开发详解
    百度地图POI数据爬取,突破百度地图API爬取数目“400条“的限制11。
    Python3中遇到UnicodeEncodeError: 'ascii' codec can't encode characters in ordinal not in range(128)
    Python 3.X 要使用urllib.request 来抓取网络资源。转
    python创建目录保存文件
    Python返回数组(List)长度的方法
    python中for、while循环、if嵌套的使用
    (转)python3 urllib.request.urlopen() 错误UnicodeEncodeError: 'ascii' codec can't encode characters
    python 之 string() 模块
    (转)Python3异常-AttributeError: module 'sys' has no attribute 'setdefaultencoding
  • 原文地址:https://www.cnblogs.com/richselian/p/2764427.html
Copyright © 2020-2023  润新知