数据压缩算法原理

数据压缩技术

目前常用的压缩算法有:

  • GZIP,一个压缩比搞的慢速算法,压缩后的数据适合长期使用,JDK中的java.util.zip.GZIPInputStream/GZIPOutputStream是这个算法的实现。
  • deflate,和GZIP类似,与gzip不同的是,可以指定算法的压缩级别,这样可以在压缩时间和输出文件大小上进行平衡,可选级别有0(不压缩),以及1(快速压缩)~9(慢速压缩),它的实现是java.util.zip.Deflater/Inflater。

GZIP底层原理

gzip使用deflate算法进行压缩,所以gzip底层原理也即是deflate的底层原理。对要压缩的文件,首先进行LZ77算法的一个变种进行压缩,对得到的结果再使用Huffman编码的方法(gzip会根据情况选择使用静态Huffman编码或者动态Huffman编码)

LZ77算法

如果文件中有两块内容相同,那么只要知道前一块的位置和大小,我们就可以确定后一块的内容,我们可以用(两者之间的距离,相同内容的长度)这样的信息来替换后一块内容。由于(两者之间的距离,相同内容的长度)这一对信息的大小小于被替换内容的大小,所以文件得到了压缩。

例如有一个文件的内容如下:
http://www.baidu.com http://www.qq.com

其中有些部分的内容,前面已经出现过了,下面用()括起来的部分就是相同的部分:

http://www.baidu.com (http://www.)qq(.com)

我们使用 (两者之间的距离,相同内容的长度) 这样一对信息,来替换后一块内容:

http://www.baidu.com (21,11).qq(18,4)

(21,11)中,21为相同内容块与当前位置之间的距离,11为相同内容的长度,即是:http://www.的长度;

(18,4)中,18为相同内容块与当前位置之间的距离,4为相同内容的长度,即是:.com的长度;

由于(两者之间的距离,相同内容的长度)这一对信息的大小,小于被替换内容的大小,所以文件得到了压缩。

通过以上例子可以了解到LZ77算法的基本实现核心,下面来深入理解一下。要理解这种算法,需要对以3个关键词有所了解。

前向缓冲区

每次读取数据的时候,先把一部分数据预载入前向缓冲区。为移入滑动窗口做准备

滑动窗口

一旦数据通过缓冲区,那么它将移动到滑动窗口中,并变成字典的一部分。

短语字典

从字符序列S1...Sn,组成n个短语。比如字符(A,B,D) ,可以组合的短语为{(A),(A,B),(A,B,D),(B),(B,D),(D)},如果这些字符在滑动窗口里面,就可以记为当前的短语字典,因为滑动窗口不断的向前滑动,所以短语字典也是不断的变化。

LZ77的主要算法逻辑就是,先通过前向缓冲区预读数据,然后再向滑动窗口移入(滑动窗口有一定的长度),不断的寻找能与字典中短语匹配的最长短语,然后通过标记符标记。

当压缩数据的时候,前向缓冲区与移动窗口之间在做短语匹配的是后会存在2中情况:

  • 找不到匹配时:将未匹配的符号编码成符号标记(多数都是字符本身)
  • 找到匹配时:将其最长的匹配编码成短语标记

短语标记包含3个部分信息:滑动窗口中的偏移量(从匹配的地方开始计算)、匹配中的符号个数、匹配结束后的前向缓冲区的第一个符号。

我们采用图例来看:

1、开始

2、滑动窗口中没有数据,所以没有匹配到短语,将字符A标记为A

3、滑动窗口中有A,没有从缓冲区中字符(BABC)中匹配到短语,依然把B标记为B

4、缓冲区字符(ABCB)在滑动窗口的位移6位置找到AB,成功匹配到短语AB,将AB编码为(6,2,C)

5、缓冲区字符(BABA)在滑动窗口位移4的位置匹配到短语BAB,将BAB编码为(4,3,A)

6、缓冲区字符(BCAD)在滑动窗口位移2的位置匹配到短语BC,将BC编码为(2,2,A)

7、缓冲区字符D,在滑动窗口中没有找到匹配短语,标记为D

8、缓冲区中没有数据进入了,结束

解压类似于压缩的逆向过程,通过解码标记和保持滑动窗口中的符号来更新解压数据。

  • 当解码字符标记:将标记编码成字符拷贝到滑动窗口中
  • 解码短语标记:在滑动窗口中查找响应偏移量,同时找到指定长短的短语进行替换

我们还是采用图例来看下:
1、开始

2、符号标记A解码

3、符号标记B解码

4、短语标记(6,2,C)解码

5、短语标记(4,3,A)解码

6、短语标记(2,2,A)解码

7、符号标记D解码

大多数情况下LZ77压缩算法的压缩比相当高,当然了也和你选择滑动窗口大小,以及前向缓冲区大小有关系。其压缩过程是比较耗时的,因为要花费很多时间寻找滑动窗口中的短语匹配,不过解压过程会很快,因为每个标记都明确告知在哪个位置可以读取了。

Huffman编码

是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字。

我们把文件中一定位长的值看作是符号,比如把8位长的256种值,也就是字节的256种值看作是符号。我们根据这些符号在文件中出现的频率,对这些符号重新编码。对于出现次数非常多的,我们用较少的位来表示,对于出现次数非常少的,我们用较多的位来表示。这样一来,文件的一些部分位数变少了,一些部分位数变多了,由于变小的部分比变大的部分多,所以整个文件的大小还是会减小,所以文件得到了压缩。

要进行Huffman编码,首先要把整个文件读一遍,在读的过程中,统计每个符号(我们把字节的256种值看作是256种符号)的出现次数。然后根据符号的出现次数,建立Huffman树,通过Huffman树得到每个符号的新的编码。对于文件中出现次数较多的符号,它的Huffman编码的位数比较少。对于文件中出现次数较少的符号,它的Huffman编码的位数比较多。然后把文件中的每个字节替换成他们新的编码。

哈夫曼树的构造

1.根据给定的n个权值{w1,w2,…,wn}构成二叉树集合F={T1,T2,…,Tn},其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树为空.
2.在F中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左右子树根结点的权值之和.
3.在F中删除这两棵树,同时将新的二叉树加入F中.
4.重复2、3,直到F只含有一棵树为止.(得到哈夫曼树)

例子,有这样一个文件的内容如下:abbbbccccddde

我们统计一下各个符号的出现次数,
a b c d e
1 4 4 3 1

建立Huffman树的过程如图:

通过最终的Huffman树,我们可以得到每个符号的Huffman编码。

a 为 110
b 为 00
c 为 01
d 为 10
e 为 111

我们可以看到,Huffman树的建立方法就保证了,出现次数多的符号,得到的Huffman编码位数少,出现次数少的符号,得到的Huffman编码位数多。

各个符号的Huffman编码的长度不一,也就是变长编码。对于变长编码,可能会遇到一个问题,就是重新编码的文件中可能会无法如区分这些编码。
比如,a的编码为000,b的编码为0001,c的编码为1,那么当遇到0001时,就不知道0001代表ac,还是代表b。出现这种问题的原因是a的编码是b的编码的前缀。
由于Huffman编码为根结点到叶子结点路径上的0和1的序列,而一个叶子结点的路径不可能是另一个叶子结点路径的前缀,所以一个Huffman编码不可能为另一个Huffman编码的前缀,这就保证了Huffman编码是可以区分的。

1.2.3 使用Huffman编码进行压缩和解压缩

为了在解压缩的时候,得到压缩时所使用的Huffman树,我们需要在压缩文件中,保存树的信息,也就是保存每个符号的出现次数的信息。

压缩:

读文件,统计每个符号的出现次数。根据每个符号的出现次数,建立Huffman树,得到每个符号的Huffman编码。将每个符号的出现次数的信息保存在压缩文件中,将文件中的每个符号替换成它的Huffman编码,并输出。

解压缩:

得到保存在压缩文件中的,每个符号的出现次数的信息。根据每个符号的出现次数,建立Huffman树,得到每个符号的Huffman编码。将压缩文件中的每个Huffman编码替换成它对应的符号,并输出。

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值