gzip 所使用压缩算法的基本原理(选摘)

摘自:http://blog.csdn.net/ghevinn/article/details/45747465 算法

gzip 所使用压缩算法的基本原理

gzip 对于要压缩的文件,首先使用LZ77算法的一个变种进行压缩,对获得的结果再使用Huffman编码的方法(实际上gzip根据状况,选择使用静态Huffman编码或者动态Huffman编码,详细内容在实现中说明)进行压缩。因此明白了LZ77算法和Huffman编码的压缩原理,也就明白了gzip的压缩原理。咱们来对LZ77算法和Huffman编码作一个简单介绍。

1.1 LZ77算法简介

这一算法是由Jacob Ziv 和 Abraham Lempel 于 1977 年提出,因此命名为 LZ77。

1.1.1 LZ77算法的压缩原理

若是文件中有两块内容相同的话,那么只要知道前一块的位置和大小,咱们就能够肯定后一块的内容。因此咱们能够用(二者之间的距离,相同内容的长度)这样一对信息,来替换后一块内容。因为(二者之间的距离,相同内容的长度)这一对信息的大小,小于被替换内容的大小,因此文件获得了压缩。

下面咱们来举一个例子。

有一个文件的内容以下
http://jiurl.yeah.net http://jiurl.nease.net

其中有些部分的内容,前面已经出现过了,下面用()括起来的部分就是相同的部分。
http://jiurl.yeah.net (http://jiurl.)nease(.net)

咱们使用 (二者之间的距离,相同内容的长度) 这样一对信息,来替换后一块内容。
http://jiurl.yeah.net (22,13)nease(23,4)

(22,13)中,22为相同内容块与当前位置之间的距离,13为相同内容的长度。
(23,4)中,23为相同内容块与当前位置之间的距离,4为相同内容的长度。
因为(二者之间的距离,相同内容的长度)这一对信息的大小,小于被替换内容的大小,因此文件获得了压缩。

1.1.2 LZ77使用滑动窗口寻找匹配串

LZ77算法使用"滑动窗口"的方法,来寻找文件中的相同部分,也就是匹配串。咱们先对这里的串作一个说明,它是指一个任意字节的序列,而不只仅是能够在文本文件中显示出来的那些字节的序列。这里的串强调的是它在文件中的位置,它的长度随着匹配的状况而变化。

LZ77从文件的开始处开始,一个字节一个字节的向后进行处理。一个固定大小的窗口(在当前处理字节以前,而且紧挨着当前处理字节),随着处理的字节不断的向后滑动,就象在阳光下,飞机的影子滑过大地同样。对于文件中的每一个字节,用当前处理字节开始的串,和窗口中的每一个串进行匹配,寻找最长的匹配串。窗口中的每一个串指,窗口中每一个字节开始的串。若是当前处理字节开始的串在窗口中有匹配串,就用(之间的距离,匹配长度) 这样一对信息,来替换当前串,而后从刚才处理完的串以后的下一个字节,继续处理。若是当前处理字节开始的串在窗口中没有匹配串,就不作改动的输出当前处理字节。

处理文件中第一个字节的时候,窗口在当前处理字节以前,也就是尚未滑到文件上,这时窗口中没有任何内容,被处理的字节就会不作改动的输出。随着处理的不断向后,窗口愈来愈多的滑入文件,最后整个窗口滑入文件,而后整个窗口在文件上向后滑动,直到整个文件结束。

1.1.3 使用LZ77算法进行压缩和解压缩

为了在解压缩时,能够区分“没有匹配的字节”和“(之间的距离,匹配长度)对”,咱们还须要在每一个“没有匹配的字节”或者“(之间的距离,匹配长度)对”以前,放上一位,来指明是“没有匹配的字节”,仍是“(之间的距离,匹配长度)对”。咱们用0表示“没有匹配的字节”,用1表示“(之间的距离,匹配长度)对”。

实际中,咱们将固定(之间的距离,匹配长度)对中的,“之间的距离”和“匹配长度”所使用的位数。因为咱们要固定“之间的距离”所使用的位数,因此咱们才使用了固定大小的窗口,好比窗口的大小为32KB,那么用15位(2^15=32K)就能够保存0-32K范围的任何一个值。实际中,咱们还将限定最大的匹配长度,这样一来,“匹配长度”所使用的位数也就固定了。

实际中,咱们还将设定一个最小匹配长度,只有当两个串的匹配长度大于最小匹配长度时,咱们才认为是一个匹配。咱们举一个例子来讲明这样作的缘由。好比,“距离”使用15位,“长度”使用8位,那么“(之间的距离,匹配长度)对”将使用23位,也就是差1位3个字节。若是匹配长度小于3个字节的话,那么用“(之间的距离,匹配长度)对”进行替换的话,不但没有压缩,反而会增大,因此须要一个最小匹配长度。

压缩:
从文件的开始到文件结束,一个字节一个字节的向后进行处理。用当前处理字节开始的串,和滑动窗口中的每一个串进行匹配,寻找最长的匹配串。若是当前处理字节开始的串在窗口中有匹配串,就先输出一个标志位,代表下面是一个(之间的距离,匹配长度) 对,而后输出(之间的距离,匹配长度) 对,而后从刚才处理完的串以后的下一个字节,继续处理。若是当前处理字节开始的串在窗口中没有匹配串,就先输出一个标志位,代表下面是一个没有改动的字节,而后不作改动的输出当前处理字节,而后继续处理当前处理字节的下一个字节。

解压缩:
从文件开始到文件结束,每次先读一位标志位,经过这个标志位来判断下面是一个(之间的距离,匹配长度) 对,仍是一个没有改动的字节。若是是一个(之间的距离,匹配长度)对,就读出固定位数的(之间的距离,匹配长度)对,而后根据对中的信息,将匹配串输出到当前位置。若是是一个没有改动的字节,就读出一个字节,而后输出这个字节。

咱们能够看到,LZ77压缩时须要作大量的匹配工做,而解压缩时须要作的工做不多,也就是说解压缩相对于压缩将快的多。这对于须要进行一次压缩,屡次解压缩的状况,是一个巨大的优势。


1.2 Huffman编码简介
1.2.1 Huffman编码的压缩原理

咱们把文件中必定位长的值看做是符号,好比把8位长的256种值,也就是字节的256种值看做是符号。咱们根据这些符号在文件中出现的频率,对这些符号从新编码。对于出现次数很是多的,咱们用较少的位来表示,对于出现次数很是少的,咱们用较多的位来表示。这样一来,文件的一些部分位数变少了,一些部分位数变多了,因为变小的部分比变大的部分多,因此整个文件的大小仍是会减少,因此文件获得了压缩。

1.2.2 Huffman编码使用Huffman树来产生编码

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

创建Huffman树:
把全部符号当作是一个结点,而且该结点的值为它的出现次数。进一步把这些结点当作是只有一个结点的树。

每次从全部树中找出值最小的两个树,为这两个树创建一个父结点,而后这两个树和它们的父结点组成一个新的树,这个新的树的值为它的两个子树的值的和。如此往复,直到最后全部的树变成了一棵树。咱们就获得了一棵Huffman树。

经过Huffman树获得Huffman编码:

这棵Huffman树,是一棵二叉树,它的全部叶子结点就是全部的符号,它的中间结点是在产生Huffman树的过程当中不断创建的。咱们在Huffman树的全部父结点到它的左子结点的路径上标上0,右子结点的路径上标上1。

如今咱们从根节点开始,到全部叶子结点的路径,就是一个0和1的序列。咱们用根结点到一个叶子结点路径上的0和1的序列,做为这个叶子结点的Huffman编码。

咱们来看一个例子。

有一个文件的内容以下
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编码替换成它对应的符号,并输出。url

相关文章
相关标签/搜索