【ELT.ZIP】OpenHarmony啃论文俱乐部——多维探秘通用无损压缩

本文深入探讨了无损压缩算法,重点分析了BWT算法及其改进方法,与Lempel-Ziv算法进行比较。文章介绍了基于BWT的改进算法在压缩效率和速度上的优势,并通过实验数据展示了其实验结论。此外,还提到了OpenHarmony内核子系统中的Linux和EROFS文件系统的数据压缩特性,以及LZ4固定大小输出压缩算法的细节。
摘要由CSDN通过智能技术生成
  • 本文出自 ELT.ZIP 团队,ELT<=>Elite(精英),.ZIP为压缩格式,ELT.ZIP即压缩精英,商务联系:16655046640

  • 成员:

    • 上海工程技术大学大二在校生
    • 合肥师范学院大二在校生
    • 清华大学大二在校生
    • 成都信息工程大学大一在校生
  • 我们是来自4个地方的同学,我们在OpenHarmony成长计划啃论文俱乐部里,通过啃论文方式学习操作系统技术

引言

压缩的标准方法是定义产生不同类型数据源的类别,假设数据是由某一类源产生的,并应用为其特殊设计的压缩算法,若可以在近似为输出的数据上良好运行,便可被称为通用压缩算法

熵编码器

  • 熵编码器(entropy coder)是一种根据字符出现的概率为字母表中的每个字符分配编码的方法。更有可能出现的字符被分配的编码比不太可能出现的更短,这种方式可以使压缩序列的预期长度最小。目前流行的熵编码器是Huffman编码器算术编码器,这两种方法都做到了相对最优,所以不能分配比预期长度更小的压缩序列编码。其中,Huffman在分配整数长度编码的方法类中是最优的,算术编码则不受这种限制,因此,它通常可产生更短的期望编码长度。

    图1:序列abracadabra的Huffman树 


图2:算术编码过程 

BWT算法

  • 块排序压缩算法(block-sorting compression algorithm),通常被称为Burrows-Wheeler变换算法*(Burrows–Wheeler compression algorithm)***,是一个被应用在数据压缩技术(如bzip2)中的算法,于1994年被Michael Burrows和David Wheeler在位于加利福尼亚州帕洛阿尔托的DEC系统研究中心发明。

  • 原理思想:

  1. 构造矩阵,其行存储压缩序列的所有单字符循环移位
  2. 按字典顺序对行进行排序
  3. 使用矩阵最后一列进行后续处理

BWT算法与以往的压缩算法有所不同,比较著名的压缩算法如Lempel-Ziv(LZ77,LZ78)、PPM、DMC针对的是通用的数据流模型,比如无记忆源(Memoryless sources),马尔可夫源(Markov sources),有限阶FSM源(finite-order FSM sources),一次读取一个或多个字节,而BWT可以处理成块的数据。BWT算法的核心思想是对一个字符串中的字符进行位置变换,使相同的字符相邻的概率增大
上述过程被称为Burrows-Wheeler变换*(BWT)*。随后,变换的输出由一个前移变换处理,最后阶段,由熵编码器压缩,可以是Huffman编码算术编码。结果是,获得了一个序列,其中出现在相似上下文中的所有字符被分组。
字符串经过BWT算法转换后并没有被压缩,而是因其极有规律性的字母内聚的特点使其他文本压缩算法大大简化,提高压缩速率和压缩比。

基于BWT算法的重要特点是运行速度快、压缩比合理,比LZ算法好得多,只比现有最佳部分匹配*(PPM, prediction by partial matching)*算法稍差;是Ziv-Lempel快速压缩算法和PPM算法的有趣替代品,前者压缩比较差,后者压缩比较好,但工作速度较慢

  • 对于一个长度为n的序列x,可以将序列轮转形成一个的矩阵:

    然后将矩阵以行为单位按照字母表顺序重新排列成矩阵,得到,并用R(x)记录在新矩阵中原序列x所在的行数。取矩阵的最后一列记为即得到BWT的最终结果。

下面我们用字符序列x=“abracadabra”,且为了获得更好的关系类型的序列,我们在x前加上哨兵字符“$”。最终得到xbwt = $drcraaaabba,R(x) = 1。可以看出通过BWT算法,一些字符连续出现的概率增大

BWT算法的逆过程则比较复杂,但在编程实现上也比较简单。其主要思想是以xbwt为基础,对字符序列进行转移、排序和组合构建BWT转换后得到的矩阵M~(x)。还是以$drcraaaabba为例:

输入 转移 排序 组合
-----------$ $----------- a----------- a----------$
-----------d d----------- a----------- a----------d
-----------r r----------- a----------- a----------r
-----------c c----------- a----------- a----------c
-----------r r----------- a----------- a----------r
-----------a a----------- b----------- b----------a
-----------a a----------- b----------- b----------a
-----------a a----------- c----------- c----------a
-----------a a----------- d----------- d----------a
-----------b b----------- r----------- r----------b
-----------b b----------- r----------- r----------b
-----------a a----------- $----------- $----------a
输入 转移 排序 组合
a----------$ $a---------- ab---------- ab---------$
a----------d da---------- ab---------- ab---------d
a----------r ra---------- ac---------- ac---------r
a----------c ca---------- ad---------- ad---------c
a----------r ra---------- a$---------- a$---------r
b----------a ab---------- br---------- br---------a
b----------a ab---------- br---------- br---------a
c----------a ac---------- ca---------- ca---------a
d----------a ad---------- da---------- da---------a
r----------b br---------- ra---------- ra---------b
r----------b br---------- ra---------- ra---------b
$----------a a$---------- $a---------- $a---------a
输入 转移 排序 组合
ab---------$ $ab--------- abr--------- abr--------$
ab---------d dab--------- abr--------- abr--------d
ac---------r rac--------- aca--------- aca--------r
ad---------c cad--------- ada--------- ada--------c
a$---------r ra$--------- a$a--------- a$a--------r
br---------a abr--------- bra--------- bra--------a
br---------a abr--------- bra--------- bra--------a
ca---------a aca--------- cad--------- cad--------a
da---------a ada--------- dab--------- dab--------a
ra---------b bra--------- rac--------- rac--------b
ra---------b bra--------- ra$--------- ra$--------b
$a---------a a$a--------- $ab--------- $ab--------a
输入 转移 排序 组合
abr--------$ $abr-------- abra-------- abra-------$
abr--------d dabr-------- abra-------- abra-------d
aca--------r raca-------- acad-------- acad-------r
ada--------c cada-------- adab-------- adab-------c
a$a--------r ra$a-------- a$ab-------- a$ab-------r
bra--------a abra-------- brac-------- brac-------a
bra--------a abra-------- bra$-------- bra$-------a
cad--------a acad-------- cada-------- cada-------a
dab--------a adab-------- dabr-------- dabr-------a
rac--------b brac-------- raca-------- raca-------b
ra$--------b bra$-------- ra$a-------- ra$a-------b
$ab--------a a$ab-------- $abr-------- $abr-------a
输入 转移 排序 组合
abra-------$ $abra------- abrac------- abrac------$
abra-------d dabra------- abra$------- abra$------d
acad-------r racad------- acada------- acada------r
adab-------c cadab------- adabr------- adabr------c
a$ab-------r ra$ab------- a$abr------- a$abr------r
brac-------a abrac------- braca------- braca------a
bra$-------a abra$------- bra$a------- bra$a------a
cada-------a acada------- cadab------- cadab------a
dabr-------a adabr------- dabra------- dabra------a
raca-------b braca------- racad------- racad------b
ra$a-------b bra$a------- ra$ab------- ra$ab------b
$abr-------a a$abr------- $abra------- $abra------a

按照这样的规律一直操作下去,最终可以得到:

输入 转移 排序 组合
abracadabr-$ $abracadabr- abracadabra- abracadabra$
abra$abrac-d dabra$abrac- abra$abraca- abra$abracad
acadabra$a-r racadabra$a- acadabra$ab- acadabra$abr
adabra$abr-c cadabra$abr- adabra$abra- adabra$abrac
a$abracada-r ra$abracada- a$abracadab- a$abracadabr
bracadabra-a abracadabra- bracadabra$- bracadabra$a
bra$abraca-a abra$abraca- bra$abracad- bra$abracada
cadabra$ab-a acadabra$ab- cadabra$abr- cadabra$abra
dabra$abra-a adabra$abra- dabra$abrac- dabra$abraca
racadabra$-b bracadabra$- racadabra$a- racadabra$ab
ra$abracad-b bra$abracad- ra$abracada- ra$abracadab
$abracadab-a a$abracadab- $abracadabr- $abracadabra

最后一次操作的得到的组合即为x对应的矩阵M~(x),矩阵的第一行去掉最后的“$“就得到原字符序列x=”abracadabra”。

请添加图片描述
BWT就是一个加标记,循环转移,算出数组,输出结果的过程。

① 这里我们输入字符串ababc,并给其加上标记得到ababc$这个标记$要比所有字符都要小。
请添加图片描述

② 之后我们对处理后的字符串进行循环转移,此时你可以把ababc$当作一个圆,然后让其旋转,使得F列(第一列)的字符按照ASCII码从小到大排列。
请添加图片描述
请添加图片描述

③ 得到的M数组最后一列便是输出的L列

BWT编码及解码的C++实现:

#include <iostream>
#include <string>
#include <algorithm>
#include <string.h>
using namespace std;

///编码,生成last数组
int getLastArray(char *lastArray,const string &str){    ///子串排序
    int len=str.size();
    string array[len];

    for(int i=0;i<len;i++){
        array[i] = str.substr(i);
    }
    sort(array,array+len);
    for(int i=0;i<len;i++){
        lastArray[i] = str.at((2*len-array[i].size()-1)%len);
    }
    return 0;
}

int getCountPreSum(int *preSum,const string &str){
    memset(preSum,0,27*sizeof(int));
    for(int i=0;i<str.size();i++){
        if(str.at(i) == &#039;#&#039;)
            preSum[0]++;
        else
            preSum[str.at(i)-&#039;a&#039;+1]++;
    }

    for(int i=1;i<27;i++)
        preSum[i] += preSum[i-1];
    return 0;
}

///解码,使用last数组,恢复原来的文本块
int regainTextFromLastArray(char *lastArray,char *reGainStr,int *preSum){
    int len=strlen(lastArray);
    int pos=0;
    char c;
    for(int i=len-1;i>=0;){
        reGainStr[i] = lastArray[pos];
        c = lastArray[pos];
        pos = preSum[c-&#039;a&#039;]+count(lastArray,lastArray+pos,c);
        i--;
    }
    return 0;
}

int main (){
    string str("sdfsfdfdsdfgdfgfgfggfgdgfgd#");
    int preSum[27];
    int len=str.size();

    char *lastArray = new char[len+1];
    char *reGainStr = new char[len+1];
    lastArray[len]=&#039;&#039;;
    reGainStr[len]=&#039;&#039;;

    getCountPreSum(preSum,str);
    getLastArray(lastArray,str);
    regainTextFromLastArray(lastArray,reGainStr,preSum);

    cout<<"       str: "<<str<<endl;
    cout<<"lastArray : "<<lastArray<<endl;
    cout<<"reGainStr : "<<reGainStr<<endl;

    delete lastArray;
    delete reGainStr;
    return 0;
}

研究者提出一种基于 Burrows-Wheeler 变换的改进算法,该算法在获得最佳压缩比的同时,其运算速度可以与该类算法中最快的算法媲美。

基于BWT的改进算法

一般结构

算法的第一阶段是Burrows-Wheeler变换。BWT输出序列在第二阶段经历一个加权频率计数变换,第三阶段是零运行长度编码,它减少了0次运行的长度。在最后阶段,序列x(rle-0)二进制算术编码器编码。算术编码是一种行之有效的熵编码方法,其中最重要的部分是概率估计方法。BWT只是对序列x的转换,并不起到压缩的作用,经过BWT算法编码后的序列还需要通过其他压缩算法才可以完成整个压缩任务:

图3:基于BWT的改进算法

  • MTF*(Move-To-Front)*是一种数据编码方式,作为一个额外的步骤,用于提高数据压缩技术效果。MTF主要使用的是数据“空间局部性”,也就是最近出现过的字符很可能在接下来的文本附近再次出现。MTF的主要思想是:
  1. 首先维护一个序列字符集大小的栈表,其中每个不同的字符在其中占一个位置,位置从0开始编号
  2. 扫描需要重新编码的序列数据,对于每个扫描到的字符,使用该字符在栈表中的索引替换,并将该字符提到栈表的栈顶的位置(索引为0的位置)
  3. 重复上一步骤,直到序列扫描结束

以经过BWT算法得到的xbwt = drcraaaabba为例:

迭代 序列 栈表
d 3 abcdr
r 4 dabcr
c 4 rdabc
r 1 crdab
a 3 rcdab
a 0 arcdb
a 0 arcdb
a 0 arcdb
b 4 barcd
b 0 barcd
a 1 abrcd

最终的得到的xmtf为34413000401.
相比于BWT算法,MTF算法的解码过程更为简单,其实就是上述过程的逆过程

序列 栈表 字符
34413000401 abrcd a
3441300040 barcd b
34413000 arcdb a
3441300 arcdb a
344130 arcdb a
34413 arcdb a
3441 rcdab r
344 crdab c
34 rdabc r
3 dabcr d

根据上表所示过程即可解码xmtf.

  • RLE-0(Zero run length encoding)叫做零游程编码,不同于一般的RLE算法,它只对序列中的0进行替换。在这里使用RLE-0处理序列是由于经过
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>