Deflate内部实现(LZ77无损压缩算法)超详细图解算法版~

概览

  • Gzip
    • Deflate 编码(LZ77+哈夫曼)
  • Brotli
    • LZ77+哈夫曼+二阶上下文建模

Deflate 分两个阶段压缩数据:重复消除位减少

第一阶段:重复消除 — LZ77无损压缩算法

算法介绍

基于字典的无损压缩算法,它搜索重复的未压缩序列并用引用指针替换它们。

引用指针由 2 个元素定义:

  • offset距离(或偏移量):原始未压缩数据中出现的第一个现有字节的相对返回。
  • Length:重复的字节长度。

当对序列进行压缩的时候,采用 “滑动窗口” 算法,
结构如下:

  • 查找缓冲区(Search buffer),也称字典(已编码部分)
  • 先行缓冲区(Look ahead buffer),包括即将进行编码序列的一部分。每次读取数据的时候,先把一部分数据预载入前向缓冲区。为移入滑动窗口做准备。

由于缓冲区具有固定的长度,所以,当算法(编码器)在运行时候,它看起来像在文件中“滑动”,所以这个缓冲区被称为“滑动窗口”。

滑动窗的尺寸是影响压缩性能的关键因素之一。如果滑动窗口太小,则压缩器可能会发现较少的重复数据序列,结果,压缩文件的大小将更大。如果滑动窗口太大,则压缩器可能需要花费更长的时间来查找重复的数据序列,因此压缩速度将变慢。
在这里插入图片描述

要使用 LZ77 压缩算法:

  1. 将编码位置设置为输入流的开头。
  2. 在查找缓冲区的窗口中找到最长的匹配项。
  3. 如果找到匹配,则输出指针 P。将编码位置(和窗口)向前移动 L个字节。
  4. 如果未找到匹配项,则输出空指针和先行缓冲区中的第一个字节。将编码位置(和窗口)向前移动一个字节。
  5. 如果先行缓冲区不为空,则返回步骤 2。

主要逻辑 :
通过先行缓冲区预读取数据,然后向字典中移入, 不断搜索字典中与先行缓冲区连续相匹配的最长序列,然后输出metadata标记。

举例

以微软的例子来理解算法:微软介绍:LZ77压缩算法

Input stream

Position    1    2    3    4    5    6    7    8    9
Byte        A    A    B    C    B    B    A    B    C

Output 期望压缩后得到的结果:
在这里插入图片描述

压缩后怎么能读取到原文呢?

答:需要将output进行解码,如:
(0,0)‘X’:直接推入X
(o,l):找到offset=o的位置,往后复制l个字符
在这里插入图片描述

看来最重要的一环就是如何压缩啦!让我们一起看看这个算法的思路和图解吧~

压缩算法思路

AABCBBABC串,将重复的子串用指针进行替换,
对于其中的每个元素 x 有两种情况:
     1. 前文没有任何重复的子串:输出(0,0)x
     2. 在前文能找到重复的子串:输出(offset = x和匹配子串的的距离,length = 匹配子串的长度)

图解压缩过程

字符序列移动方向:从右往左

简称:

  • buffer区:先行缓存区(未编码),这是需要匹配的字符串
  • Dictionary:查找缓存区(已编码),用来匹配buffer的字典区域
  1. 初始字符串从右往左滑动,直至占满所有buffer区,如图1
    在这里插入图片描述
                    (图1)
    在这里插入图片描述

  2. 开始遍历 图1 buffer的第一个字符’A’,因Dictionary空,未匹配到’A’ => 往左移一格(如图2),输出(0,0)A。
    (offset = A无匹配子串,距离=0,length:0,无重复子串)在这里插入图片描述                (图2)
    在这里插入图片描述

  3. 遍历 图3 buffer第一个字符"A",在Dictionary找到"A",未超过buffer黄色长度,往后遍历到编码"AB",Dictionary没有匹配到“AB”字符串,于是只编码"A",输出(1, 1)。
    在这里插入图片描述
                    (图3)

    图4,匹配长度为1,所以字符串向左偏移一个单位:
    在这里插入图片描述
                    (图4)
    在这里插入图片描述

  4. 匹配buffer区第一个字符’B’,Dictionary内未匹配,同步骤1,输出(0,0)B,左移一格。

  5. 匹配buffer区第一个字符’C’,Dictionary内未匹配,同上,输出(0,0)C,左移一格,如 图5
    在这里插入图片描述
                    (图5)
    在这里插入图片描述

  6. 匹配 图6 buffer区第一个字符’B’,offset('B’与Dictionary中匹配的’B’的距离)=2,两个查找指针同时往后移1(如图6),比较'C'vs'B'不匹配,终止,length=1,输出(2,1)
    在这里插入图片描述
                    (图6

    得到结果:
    在这里插入图片描述
                    (图7)
    在这里插入图片描述

  7. 匹配 图7 buffer区第一个字符’B’,Dictionary匹配到‘B’,分别是offset=1和offset=3,但offset=3下一字节'C'vs'A'不匹配,就近原则选择offset=1,length=1,输出(1,1)。
    在这里插入图片描述
                    (图8)
    此时已编码序列长度大于Dictionary区,有序列滑出了窗口,如图8
    在这里插入图片描述

  8. 匹配 图8 BUFFER第一个字符 ‘A’,在DICTIONARY匹配到,offset=5,往后遍历直到匹配"ABC",length=3,此时不能再往后编码否则超过BUFFER区域长度,故输出(5, 3),往左移动3格,如图:
    在这里插入图片描述
    在这里插入图片描述

第二阶段:位减少

huffman

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过哈夫曼树,我们可以将原本需要120bit(15*8)的位减少到 28bit

位减少

范式huffman树:在普通huffman树的基础上只要保存编码位长,利用位长反推编码

### Deflate 压缩算法的工作原理 Deflate 是一种广泛使用的无损压缩算法,其核心思想是结合了 LZ77 和 Huffman 编码两种技术。LZ77 负责通过查找重复的数据模式来减少冗余,而 Huffman 编码则进一步优化这些数据的表示形式以达到更高的压缩率。 #### LZ77 部分 LZ77 算法会扫描输入数据并寻找之前已经出现过的子字符串。当找到这样的匹配时,它不会再次存储整个子字符串,而是记录下该子字符串的位置偏移量以及长度。这种方式显著减少了需要保存的实际字符数量[^1]。 #### Huffman 编码部分 经过 LZ77 处理后的数据通常由两部分内容组成:未匹配的新字符和指向先前已见序列的指针(即距离/长度对)。Huffman 编码会对这两类不同的符号分配可变长度的二进制代码,使得更频繁出现的符号对应较短的位串,从而进一步提高压缩效率[^2]。 ### Python 实现 Deflate 的基本流程 以下是基于上述理论,在 Python 中实现 Deflate 压缩的一个简单框架: ```python import zlib def compress_data(input_bytes): """Compress the input byte data using DEFLATE algorithm.""" compressed = zlib.compress(input_bytes) return compressed def decompress_data(compressed_bytes): """Decompress the DEFLATE-compressed byte data back to original form.""" decompressed = zlib.decompress(compressed_bytes) return decompressed ``` 此脚本利用了标准库 `zlib` 提供的功能来进行快速高效的压缩与解压操作。注意这里并没有手动去实现具体的 LZ77 或者霍夫曼树构建逻辑,因为这已经被封装进了底层 C 扩展模块之中以便获得最佳性能表现。 对于文件级别的操作,则可以参照如下例子完成从磁盘读取文件内容、执行压缩后再写回到另一个位置的过程;同样也可以反向恢复原档: ```python def deflate_compress_file(source_path, target_path): with open(source_path, 'rb') as src_fobj: raw_content = src_fobj.read() comp_result = compress_data(raw_content) with open(target_path, 'wb') as tgt_fobj: tgt_fobj.write(comp_result) def inflate_decompress_file(source_path, target_path): with open(source_path, 'rb') as src_fobj: zipped_content = src_fobj.read() uncomp_result = decompress_data(zipped_content) with open(target_path, 'wb') as tgt_fobj: tgt_fobj.write(uncomp_result) ``` 以上函数展示了如何将本地文件作为目标对象进行压缩或解压处理[^4]。 ### 总结 综上所述,DEFLATE 方法综合运用了滑动窗口技术和熵编码策略实现了良好的平衡效果——既保持较高的速度又能够取得不错的空间节省比例。然而值得注意的是,尽管如此强大便捷,但在某些特殊场景下仍可能存在局限性,比如面对高度随机化的数据集时可能无法有效降低体积大小等问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值