LZ 系类压缩算法编码原理

注:本文为 “ LZ系类压缩算法” 相关文章合辑

英文引文,机翻未校。


Data Compression: LZ77 vs. LZ4 vs. LZ4HC

Last updated: August 16, 2024

1. Introduction

1. 引言

Data compression is a technique for reducing the size of data, saving storage space, and improving transmission speeds across networks. Various algorithms have been developed over the years, each offering different trade-offs between compression ratio, speed, and computational efficiency. Among these, LZ77, LZ4, and LZ4HC stand out as popular choices for different scenarios.
数据压缩是一种用于减小数据大小、节省存储空间和提高跨网络传输速度的技术。多年来,人们开发了各种算法,每种算法在压缩率、速度和计算效率之间提供了不同的权衡。其中,LZ77、LZ4 和 LZ4HC 是不同场景的热门选择。

In this tutorial, we’ll explore these three algorithms, comparing their performance and use cases to figure out when to use each one.
在本教程中,我们将探讨这三种算法,比较它们的性能和用例,以确定何时使用每种算法。

2. What Is Data Compression?

2. 什么是数据压缩?

Before diving into the specific algorithms, let’s briefly discuss data compression and why it’s important. Data compression involves encoding information using fewer bits than in the original representation. The goal is to reduce the size of the data while retaining as much of the original information as possible. This is particularly useful in areas like file storage, data transmission, and bandwidth management.
在深入研究具体算法之前,让我们简要讨论一下数据压缩及其重要性。数据压缩涉及使用比原始表示更少的位对信息进行编码。目标是减小数据的大小,同时保留尽可能多的原始信息。这在文件存储、数据传输和带宽管理等领域特别有用。

There are two main types of data compression. Lossless compression ensures that the original data can be perfectly reconstructed from the compressed data. It’s ideal for scenarios where accuracy is crucial, such as text files, databases, and executable programs. Lossy compression reduces file size by removing some of the data, often resulting in a loss of quality. It’s commonly used for multimedia files like images, videos, and audio where some loss of quality is acceptable.
数据压缩有两种主要类型。无损压缩确保可以从压缩后的数据中完美重建原始数据。它非常适合准确性至关重要的场景,例如文本文件、数据库和可执行程序。有损压缩通过删除一些数据来减小文件大小,这通常会导致质量下降。它通常用于图像、视频和音频等多媒体文件,在这些文件中,一些质量损失是可以接受的。

LZ77, LZ4, and LZ4HC are all lossless compression algorithms, meaning they allow for exact reconstruction of the original data.
LZ77、LZ4 和 LZ4HC 都是无损压缩算法,这意味着它们允许精确重建原始数据。

3. Understanding LZ77

3.了解 LZ77

3.1. The Basics of LZ77

3.1. LZ77 的基础知识

LZ77 is a foundational data compression algorithm developed by Abraham Lempel and Jacob Ziv in 1977. It is the basis for many other compression algorithms, including LZ4 and LZ4HC.
LZ77 是由 Abraham Lempel 和 Jacob Ziv 于 1977 年开发的一种基础数据压缩算法。它是许多其他压缩算法的基础,包括 LZ4 和 LZ4HC。

The core idea behind LZ77 is to replace repeated patterns with references to the first match of the identified pattern in the uncompressed data.
LZ77 背后的核心思想是将重复的模式替换为对未压缩数据中已识别模式的第一个匹配项的引用。

LZ77 works by sliding a window over the data stream and identifying sequences of data that have already appeared within a certain range (the “window”). When a match is found, the algorithm replaces the second occurrence with a reference to the first occurrence, thus reducing the size of the data.
LZ77 的工作原理是在数据流上滑动窗口并识别已经出现在一定范围(“窗口”)内的数据序列。找到匹配项后,算法会将第二次出现的匹配项替换为对第一次出现的引用,从而减小数据的大小。

For general data compression tasks, such as compressing text files, LZ77 offers a good balance between speed and compression ratio. It’s fast enough for most tasks while still providing a reasonable reduction in data size.
对于一般的数据压缩任务,例如压缩文本文件,LZ77 在速度和压缩率之间提供了良好的平衡。对于大多数任务来说,它的速度足够快,同时仍能合理地减少数据大小。

3.2. How LZ77 Works

3.2. LZ77 是如何工作的

Here’s a simplified example of how LZ77 compresses data.
以下是 LZ77 如何压缩数据的简化示例。

Let’s say we have the following string of text:
假设我们有以下文本字符串:

abcabcabcabc

LZ77 would process this as follows:
LZ77 将按如下方式处理此内容:

  • It reads the first three characters abc and stores them as they are
    它读取前三个字符 abc 并按原样存储它们
  • When it encounters the second abc, it recognizes that this sequence has already appeared. Instead of storing it again, it stores a reference to the first abc
    当它遇到第二个 abc 时,它会识别出此序列已经出现。它不会再次存储它,而是存储对第一个 abc 的引用
  • This process continues for the entire string, significantly reducing the data size
    此过程将持续整个字符串,从而显著减小数据大小

The reference typically consists of two values: the distance back to the sequence’s start and the match’s length.
引用通常由两个值组成:回到序列开始的距离和匹配项的长度。

For the string abcabcabcabc, the LZ77 output might be something like:
对于字符串 abcabcabcabc,LZ77 输出可能类似于:

abc(0, 3)(3, 3)(6, 3)

where (n, m) represents the match found n characters back with a length of m.
其中 (n, m) 表示找到 n 个字符后且长度为 m 的匹配项。

4. Understanding LZ4

4.了解 LZ4

4.1. The Basics of LZ4

4.1. LZ4 的基础知识

LZ4 is a high-speed compression algorithm based on LZ77 but optimized for speed rather than compression ratio. It was developed by Yann Collet in 2011 and is widely used when quick compression and decompression are more important than achieving the highest possible compression ratio.
LZ4 是一种基于 LZ77 的高速压缩算法,但针对速度而不是压缩率进行了优化。它由 Yann Collet 于 2011 年开发,当快速压缩和解压缩比实现尽可能高的压缩比更重要时,它被广泛使用。

LZ4 is particularly popular in real-time applications with critical low latency, such as network traffic compression, real-time storage systems, and data transfer protocols.
LZ4 在具有关键低延迟的实时应用程序中特别受欢迎,例如网络流量压缩、实时存储系统和数据传输协议。

Speed is critical in real-time video streaming. We need to compress data quickly to minimize latency and ensure smooth playback. LZ4 is ideal here because it offers fast compression and decompression, transmitting video streams efficiently without introducing significant delays.
速度在实时视频流中至关重要。我们需要快速压缩数据,以最大限度地减少延迟并确保流畅播放。LZ4 在这里是理想的选择,因为它提供快速压缩和解压缩,高效传输视频流而不会引入明显的延迟。

4.2. How LZ4 Works

4.2. LZ4 的工作原理

LZ4 operates similarly to LZ77 but with several optimizations that make it faster.
LZ4 的操作方式与 LZ77 类似,但经过了一些优化,使其更快。

In LZ4, the matching algorithm is designed to quickly identify repeated sequences without spending too much time on each search. LZ4 focuses on finding sequences of a fixed length, usually 4 bytes (32 bits). This fixed-length approach simplifies the matching process because the algorithm doesn’t need to check every possible match length, as it would with LZ77. Instead, it quickly identifies sequences of this fixed size.
在 LZ4 中,匹配算法旨在快速识别重复序列,而无需在每次搜索上花费太多时间。LZ4 专注于查找固定长度的序列,通常为 4 字节(32 位)。这种固定长度的方法简化了匹配过程,因为该算法不需要像 LZ77 那样检查每个可能的匹配长度。相反,它可以快速识别此固定大小的序列。

To further enhance speed, LZ4 uses hash tables to store and quickly retrieve the positions of these fixed-length sequences. When the algorithm encounters a new sequence in the data, it calculates a hash value for that sequence and checks if this hash has been seen before. If it has, LZ4 knows that a match exists and can quickly reference the earlier occurrence.
为了进一步提高速度,LZ4 使用哈希表来存储和快速检索这些固定长度序列的位置。当算法在数据中遇到新序列时,它会计算该序列的哈希值,并检查以前是否见过此哈希值。如果有,LZ4 知道存在匹配项,并且可以快速引用较早出现的情况。

Additionally, unlike more thorough algorithms that might continue searching to find the absolute best match, LZ4 is designed to stop as soon as it finds a good enough match. This “early exit” strategy allows the algorithm to move on to the next part of the data more quickly, further speeding up the compression process.
此外,与可能继续搜索以查找绝对最佳匹配项的更全面的算法不同,LZ4 设计为在找到足够好的匹配项后立即停止。这种 “提前退出” 策略允许算法更快地进入数据的下一部分,从而进一步加快压缩过程。

However, these optimizations come at a cost. LZ4 typically achieves a lower compression ratio than more thorough algorithms like LZ4HC.
但是,这些优化是有代价的。LZ4 通常比 LZ4HC 等更全面的算法实现更低的压缩率。

5. Understanding LZ4HC

5. 了解 LZ4HC

5.1. The Basics of LZ4HC

5.1. LZ4HC 的基础知识

LZ4HC (LZ4 High Compression) is a variant of LZ4 that prioritizes achieving a higher compression ratio over speed. While LZ4HC is slower than LZ4, it still operates faster than many other compression algorithms, making it a good middle-ground option for those who need better compression but still require decent speed.
LZ4HC (LZ4 High Compression) 是 LZ4 的一种变体,它优先考虑实现更高的压缩比而不是速度。虽然 LZ4HC 比 LZ4 慢,但它的运行速度仍然比许多其他压缩算法快,这使其成为那些需要更好压缩但仍需要适当速度的用户的一个很好的中间选择。

LZ4HC is particularly useful when storage space is more constrained or when data needs to be compressed once and decompressed many times. When storing large amounts of data for long-term archival, storage space becomes a significant concern. We might compress data once and rarely access it, so the compression ratio is more important than speed. LZ4HC is perfect in this scenario, providing a high compression ratio and reducing the required storage space.
当存储空间更加受限或数据需要压缩一次并多次解压缩时,LZ4HC 特别有用。在存储大量数据以进行长期存档时,存储空间成为一个重要的问题。我们可能会压缩一次数据,很少访问它,因此压缩率比速度更重要。LZ4HC 非常适合这种场景,提供高压缩比并减少所需的存储空间。

5.2. How LZ4HC Works

5.2. LZ4HC 的工作原理

LZ4HC takes a more thorough approach to data compression compared to its faster counterpart, LZ4. This is evident in several key aspects of its matching algorithm.
与更快的 LZ4HC 相比,LZ4HC 采用更彻底的数据压缩方法。这在其匹配算法的几个关键方面中很明显。

First, LZ4HC employs a more exhaustive search algorithm designed to find the best possible matches in the data stream. By conducting a deeper search, LZ4HC significantly increases the likelihood of identifying longer and more frequent matches, which in turn results in better compression ratios. This thoroughness, however, comes at the cost of speed, as the algorithm spends more time ensuring that the matches it finds are optimal.
首先,LZ4HC 采用更详尽的搜索算法,旨在找到数据流中的最佳匹配项。通过进行更深入的搜索,LZ4HC 显著提高了识别更长、更频繁匹配项的可能性,从而获得更好的压缩率。然而,这种彻底性是以牺牲速度为代价的,因为算法会花费更多时间确保它找到的匹配项是最佳的。

In addition to its exhaustive search method, LZ4HC enhances its matching capabilities through a backward search technique. Unlike LZ4, which conducts a simpler forward search from the current position, LZ4HC searches backward from the end of the current position. This backward search helps maximize the length of the matches by considering sequences that may not be immediately obvious in a forward search, further improving the compression efficiency.
除了穷举的搜索方法外,LZ4HC 还通过向后搜索技术增强了其匹配功能。与从当前位置进行更简单的向前搜索的 LZ4 不同,LZ4HC 从当前位置的末尾向后搜索。这种向后搜索通过考虑在向前搜索中可能不会立即明显的序列来帮助最大化匹配项的长度,从而进一步提高压缩效率。

Moreover, LZ4HC improves upon LZ4’s fixed block size approach by introducing adaptive block size capabilities. Instead of relying on a one-size-fits-all block size, LZ4HC can dynamically adjust the block size based on the characteristics of the data being compressed. This adaptability allows LZ4HC to optimize the compression process more effectively, ensuring that the algorithm can handle a wider variety of data types with improved results.
此外,LZ4HC 通过引入自适应块大小功能,改进了 LZ4 的固定块大小方法。LZ4HC 不依赖于一刀切的块大小,而是可以根据被压缩数据的特性动态调整块大小。这种适应性使 LZ4HC 能够更有效地优化压缩过程,确保算法能够处理更广泛的数据类型并获得更好的结果。

These enhancements result in a significantly better compression ratio compared to LZ4, though at the cost of compression speed.
与 LZ4 相比,这些增强功能导致压缩率明显更高,但以压缩速度为代价。

6. Comparing LZ77, LZ4, and LZ4HC

6. 比较 LZ77、LZ4 和 LZ4HC

Now that we’ve covered the basics of LZ77, LZ4, and LZ4HC, let’s compare their speed, compression ratio, and suitable use cases:
现在我们已经介绍了 LZ77、LZ4 和 LZ4HC 的基础知识,让我们比较一下它们的速度、压缩率和合适的用例:

AlgorithmSpeedCompression RatioIdeal Use Cases
LZ4FastLowReal-time data compression: Network traffic, real-time storage systems, data transfer protocols
LZ77ModerateModerateGeneral-purpose data compression: Text file compression, moderate-speed applications
LZ4HCSlowHighHigh-compression scenarios: Archival storage, backup systems, scenarios with limited storage space

Each algorithm has its own trade-offs regarding speed. LZ4 is designed to be fast, making it ideal for real-time applications. LZ77 is slower but still efficient, and LZ4HC, while slower, offers a higher compression ratio.
每种算法在速度方面都有自己的权衡。LZ4 设计快速,非常适合实时应用。LZ77 速度较慢,但仍然有效,而 LZ4HC 虽然速度较慢,但提供更高的压缩率。

The compression ratio is crucial when storage space is at a premium. LZ4HC provides the highest compression ratio, followed by LZ77, with LZ4 offering the lowest ratio in exchange for speed.
当存储空间非常宝贵时,压缩率至关重要。LZ4HC 提供最高的压缩比,其次是 LZ77,LZ4 提供最低的压缩比以换取速度。

7. Conclusion

7. 结论

In this article, we’ve explored the differences between LZ77, LZ4, and LZ4HC, three popular data compression algorithms. Each algorithm offers unique advantages, from LZ4’s speed to LZ4HC’s high compression ratio.
在本文中,我们探讨了三种流行的数据压缩算法 LZ77、LZ4 和 LZ4HC 之间的区别。每种算法都具有独特的优势,从 LZ4 的速度到 LZ4HC 的高压缩比。


LZ 系类压缩算法介绍

lab601 2020-12-29 14:24

主要介绍 1)简单字典编码,2)LZ77 字典编码,3)LZ78 字典编码。

1、简单字典编码

这是一种简单的方法,但是需要遍历两次待压缩的数据。

1.1、实现具体步骤

  • 1 读取待编码数据,记录出现过的Byte出现次数

  • 2 按照概率从高到低,排序记录的数据;

  • 3 排序后的记录就构成了一个字典,在每个记录前加上序号长度,000代表长度为 1,111代表长度为 8. 比如第二条记录,序号为0000 0101,实际长度为3,在 1 前面加上010。写入压缩的符号是010101,总共 6 个 bit。

  • 4 第二次遍历待压缩的数据,搜索字典将序号长度 + 序号写入压缩文件。

1.2、优缺点

  • 优点:简单,解压所读快。

  • 缺点:压缩速度慢,需要遍历两次,最大只能有 256 个元素,压缩率可能不高。

  • 适合:文本压缩,以 Byte 为单位。

2、LZ77

2.1、LZ77 算法介绍

LZ77 算法是由 Lempel 和 Ziv 在 1977 年的论文中提出来的,该算法将输入流中之前的字符作为字典。编码器维护一个滑动窗口,输入字符流从右往左滑动。滑动窗口由两部分组成,如下图所示,左边的是已编码区域(当前字典),右边是待编码区域。已编码区域长度通常是 1000+,待编码区域通常是数十个字节。匹配的数据使用三元组来表示**(距离,匹配长度,待编码区下一个符号)**

滑动窗口示意图

2.2、编码器工作步骤

  • 1 编码器从右往左搜索待搜索区域,寻找和已编码区域第一个符号e相同的符号,找到了easily中的e
  • 2 从第一个匹配字符e向后(右)尽可能寻找连续的匹配字符,得到匹配字符串eas,匹配长度为 3。
  • 3 编码器继续往左搜索,重复步骤 1、2.
  • 4 使用匹配长度最长的三元组来表示,如果没有匹配的数据使用**(0, 0, 未匹配的字符)**表示。
2.2.1 Example

LZ77 工作步骤示意图

2.3、优缺点

  • 优点:解压速度快。
  • 缺点:压缩速度慢。
  • 适用场景:适合于一次压缩,多次解压的文件。

3、LZ78

LZ78 算法是 Lempel 和 Ziv 在 1978 年提出的,LZ78 和 LZ77 两种算法在构建字典的思路上完全不同。LZ78 没有使用滑动窗口,通过对已经编码的数据进行统计,实时更新维护字典。编码器输出二元组(字典指针,未编码的符号),工作编码过程如下图所示。

LZ78 工作过程示意图

3.1、编码器工作步骤

  • 1 开始编码,字典是空的,只有一个元素:编号:0;内容:null
  • 2 输入新元素,查找字典中相匹配的字符。以上图为例,1)没有匹配的字符,如输入第二个元素i,使用**(0,“i”)表示,将符号i添加到字典。2)存在匹配的字符,如第 5 个元素,输入元素si**,符号s元素前面已经出现过,再输入i,没有匹配的数据,编码器输出**(1, “i”),将"si"**加入字典。
  • 3 重复步骤 2,直到输入全部完成。

3.2、优缺点

  • 优点:解压速度快,无需传输字典,解码过程构建字典。
  • 缺点:编码相对比 LZ77 复杂。
  • 适用场景:适用于文本压缩,为单词建立词典。

4、总结

LZ77 和 LZ78 是两种不同的思路,LZ77 将前面已经编码的数据作为字典,利用了数据局部性的原理,搜索区域的大小决定了算法的执行时间,和压缩的效率,适合相同数据在较短时间内重复出现的场景;LZ78 维护了一个全局的字典,需要确定字典的大小,字典过大,遍历时间过长,适合文本编码

5、参考文献


LZ77 压缩算法编码原理详解 (结合图片和简单代码)

转瞬之夏 2014-12-02 20:50

前言

LZ77 算法是无损压缩算法,由以色列人 Abraham Lempel 发表于 1977 年。LZ77 是典型的基于字典的压缩算法,现在很多压缩技术都是基于 LZ77。鉴于其在数据压缩领域的地位,本文将结合图片和源码详细介绍其原理。

原理介绍

首先介绍几个专业术语。

1.lookahead buffer (不知道怎么用中文表述,暂时称为待编码区):

等待编码的区域

2.search buffer:

已经编码的区域,搜索缓冲区

3.滑动窗口:

指定大小的窗,包含 “搜索缓冲区”(左) + “待编码区”(右)

编码过程

为了编码待编码区, 编码器在滑动窗口的搜索缓冲区查找直到找到匹配的字符串。匹配字符串的开始字符串与待编码缓冲区的距离称为 “偏移值”,匹配字符串的长度称为 “匹配长度”。编码器在编码时,会一直在搜索区中搜索,直到找到最大匹配字符串,并输出 (o, l ),其中 o 是偏移值, l 是匹配长度。然后窗口滑动 l,继续开始编码。如果没有找到匹配字符串,则输出 (0, 0, c),c 为待编码区下一个等待编码的字符,窗口滑动 “1”。算法实现将类似下面的:

while( lookAheadBuffer not empty )
{
get a pointer (position, match) to the longest match
in the window for the lookAheadBuffer;

output a (position, length, char());
shift the window length+1 characters along;
}

主要步骤

  1. 设置编码位置为输入流的开始

  2. 在滑窗的待编码区查找搜索区中的最大匹配字符串

  3. 如果找到字符串,输出 (偏移值, 匹配长度), 窗口向前滑动 “匹配长度”

  4. 如果没有找到,输出 (0, 0, 待编码区的第一个字符),窗口向前滑动一个单位

  5. 如果待编码区不为空,回到步骤 2

描述实在是太复杂,还是结合实例来讲解吧

实例:

现在有字符串 “AABCBBABC”,现在对其进行编码。

一开始,窗口滑入如图位置

img
由图可见,待编码缓冲区有 “AAB” 三个字符,此时搜索缓冲区还是空的。所以编码第一个字符,由于搜索区为空,故找不到匹配串,输出 (0,0, A), 窗口右移一个单位,如下图

img
此时待编码区有 “ABC”。开始编码。最先编码 “A”,在搜索区找到 “A”。由于没有超过待编码区,故开始编码 “AB”,但在搜索区没有找到匹配字符串,故无法编码。因此只能编码 “A”。

输出 (1, 1)。即为相对于待编码区,偏移一个单位,匹配长度为 1。窗口右滑动匹配长度,即移动 1 个单位。如下图

img

一样,没找到,输出 (0, 0, B), 右移 1 个单号,如下图

img
输出(0, 0, C), 右移 1 个单位,如下图

img
输出 (2, 1), 右移 1 个单位,如下图

img
输出 (3, 1), 右移 1 个单位,如下图

img
开始编码 “A”,在搜索缓冲区查找到匹配字符串。由于待编码缓冲区没有超过,继续编码。开始编码 “AB”, 也搜索到。不要停止,继续编码 “ABC”,找到匹配字符串。由于继续编码,则超过了窗口,故只编码 “ABC”,输出 (5, 3), 偏移 5,长度 3。右移 3 个单位,如下图

img
此时待编码缓冲区为空,停止编码。

最终输出结果如下

img

python 代码实现:

class Lz77:
    def __init__(self, inputStr):
        self.inputStr = inputStr  # 输入流
        self.searchSize = 5      # 搜索缓冲区(已编码区)大小
        self.aheadSize = 3       # lookAhead缓冲区(待编码区)大小
        self.windSpiltIndex = 0  # lookHead缓冲区开始的索引
        self.move = 0
        self.notFind = -1        # 没有找到匹配字符串

    # 得到滑动窗口的末端索引
    def getWinEndIndex(self):
        return self.windSpiltIndex + self.aheadSize

    # 得到滑动窗口的始端索引
    def getWinStartIndex(self):
        return self.windSpiltIndex - self.searchSize

    # 判断lookHead缓冲区是否为空
    def isLookHeadEmpty(self):
        return True if self.windSpiltIndex + self.move > len(self.inputStr) - 1 else False

    def encoding(self):
        step = 0
        print("Step   Position   Match   Output")
        while not self.isLookHeadEmpty():
            # 1.滑动窗口
            self.winMove()
            # 2. 得到最大匹配串的偏移值和长度
            offset, matchLen = self.findMaxMatch()
            # 3.设置窗口下一步需要滑动的距离
            self.setMoveSteps(matchLen)
            if matchLen == 0:
                # 匹配为0,说明无字符串匹配,输出下一个需要编码的字母
                nextChar = self.inputStr[self.windSpiltIndex]
                result = (step, self.windSpiltIndex, '-', '(0,0)' + nextChar)
            else:
                result = (
                    step,
                    self.windSpiltIndex,
                    self.inputStr[self.windSpiltIndex - offset: self.windSpiltIndex - offset + matchLen],
                    '(' + str(offset) + ',' + str(matchLen) + ')'
                )
            # 4.输出结果
            self.output(result)
            step += 1  # 仅用来设置第几步

    # 滑动窗口(移动分界点)
    def winMove(self):
        self.windSpiltIndex = self.windSpiltIndex + self.move

    # 寻找最大匹配字符并返回相对于窗口分界点的偏移值和匹配长度
    def findMaxMatch(self):
        matchLen = 0
        offset = 0
        minEdge = self.minEdge() + 1  # 得到编码区域的右边界
        # 遍历待编码区,寻找最大匹配串
        for i in range(self.windSpiltIndex + 1, minEdge):
            offsetTemp = self.searchBufferOffest(i)
            if offsetTemp == self.notFind:
                return offset, matchLen
            offset = offsetTemp  # 偏移值
            matchLen += 1  # 每找到一个匹配串,加1
        return offset, matchLen

    # 入参字符串是否存在于搜索缓冲区,如果存在,返回匹配字符串的起始索引
    def searchBufferOffest(self, i):
        searchStart = self.getWinStartIndex()
        searchEnd = self.windSpiltIndex
        # 下面几个if是处理开始时的特殊情况
        if searchEnd < 1:
            return self.notFind
        if searchStart < 0:
            searchStart = 0
            if searchEnd == 0:
                searchEnd = 1
        searchStr = self.inputStr[searchStart: searchEnd]  # 搜索区字符串
        findIndex = searchStr.find(self.inputStr[self.windSpiltIndex: i])
        if findIndex == -1:
            return -1
        return len(searchStr) - findIndex

    # 设置下一次窗口需要滑动的步数
    def setMoveSteps(self, matchLen):
        if matchLen == 0:
            self.move = 1
        else:
            self.move = matchLen

    def minEdge(self):
        return len(self.inputStr) if len(self.inputStr) - 1 < self.getWinEndIndex() else self.getWinEndIndex() + 1

    def output(self, touple):
        print("%d      %d           %s     %s" % touple)


if __name__ == "__main__":
    lz77 = Lz77("AABCBBABC")
    lz77.encoding()

只是简单的写了下,没有过多考虑细节,请注意,这不是最终的代码,只是用来阐述原理,仅供参考。输出结果就是上面的输出(格式由于坑爹的博客园固定样式,代码位置有偏移,请注意)

参考文章:

http://msdn.microsoft.com/en-us/library/ee916854.aspx

http://en.wikipedia.org/wiki/LZ77_and_LZ78

http://cs.stanford.edu/people/eroberts/courses/soco/projects/2000-01/data-compression/lossless/lz77/algorithm.htm


两个老头儿写的神奇算法,统治了全世界!

原创 liuxin 码农翻身 2024年04月22日 08:56 河南

作为普通人,在浏览网页的时候,并不会意识到,服务器发给你的网页,其实都是压缩过的。

如果你像程序员一样,在浏览器中按一下 F12,就能找到这样的东西:

图片

它的意思是:为了节省带宽提供网速,我(服务器)把内容用 gzip 做了压缩,你(浏览器)得解压缩一下才能看啊!

在 HTTP 压缩中,除了 gzip 之外,还有 compress、deflate、br 等算法,让人眼花缭乱。

但是,这些压缩算法,都有一个老祖宗:LZ 算法

LZ 来自两个人的名称:AbrahamLempel,JacobZiv。

图片

两个人于 2023 年相继去世,都很长寿,Lempel 活了 86 岁,Ziv 活了 91 岁。

01 起源

数据压缩分为两种,一种是有损压缩,如 MP3、JPEG,一些不重要的数据在压缩时会被删除。

另外一种是无损压缩,二进制位神奇的消失,文件显著变小,方便存储和传输。

1948 年,香农创立了信息论以后,大家一直在折腾一件事情:如何找到最优编码,压缩一段信息。

香农和法诺率先出手,提出了香农 - 法诺编码

图片

它通过符号分组的方式,自顶向下构建了一个二叉树。

图片

但是这种方式不是最优解,并且编码不是前缀码,容易产生歧义。

法诺后来在麻省理工教信息论时,给学生们提出了一个挑战:要么参加期末考试,要么改进现有的数据压缩算法。

一个叫霍夫曼的研究生不喜欢考试,决定走后一条路。

霍夫曼并不知道,就连大名鼎鼎的香农也在这个问题上苦苦挣扎。他研究了几个月,开发了多种方法,都没有用。

就在他要放弃,把笔记扔到垃圾桶的那一刻,一个美妙而优雅的算法划过了他的脑海:按照字符出现的频率,从下往上构建二叉树,这就是著名的霍夫曼算法。

霍夫曼的算法被称为 “最佳编码”,实现了两个目标:

(1) 任何一个字符编码,都不是其他字符编码的前缀。

(2) 信息编码的总长度最小。

霍夫曼算法虽然很好,但有个巨大的限制:需要先获得所有字符出现的概率,然后才能编码压缩,这在很多情况下是做不到的。

上世纪 70 年代,互联网出现以后,这个问题变得更为突出。

能不能实现一边读取数据,一边压缩?

02 突破

以色列理工学院的 Ziv 和 Lempel 一起向这一问题发起了挑战。

两人是很好的搭档,Ziv 擅长统计学和信息论,Lempel 则擅长布尔代数和计算机科学

图片

1977 年,他们俩都来到了贝尔实验室做学术休假。

学术休假又称为 “知识人才休假”,每工作几年就有一段很长的假期(比如半年),这段假期里想干什么就干什么,还是带薪的。

大佬们的学术休假很有意思,比如 Unix 发明人 Ken Thomson 在休假时就回到了母校伯克利,把 Unix 传播到了那里,启发 Bill Joy 等人开发了 BSD。

Ziv 和 Lempel 也是这样,他们到美国贝尔实验室去学术休假,在 “休假” 期间合作发表了一篇论文:《顺序数据压缩的通用算法》,提出了基于 “滑动窗口” 的算法,它不直接考虑字符频率,而是寻找重复的数据块(如字符串、字节序列等)并引用这些数据块之前出现的位置

图片

这算法就是 LZ77,它适用于任何类型的数据,不需要做预处理(统计字符出现的概率),只需要一次遍历就可以实现极高的压缩比。

第二年,他们再接再厉,改进了 LZ77,变成了 LZ78,能够从压缩数据中完美实现数据解压重构,并且比以往的算法效率更高。

03 乱战

LZ 算法这样的宝贝,居然停留在理论界好几年,没有大规模使用。

直到 1984 年,DEC 公司的 Terry Welch 在 LZ 基础上,创造了LZW 算法,用到了 Unix 的compress 程序中。

伴随着 Unix 的广泛传播,LZ 算法开始进入飞速发展的快车道。

但是,也进入了群雄并启的乱战时代。

1985 年,Thom Henderson 在 BBS 下载文件时,发现总得一个一个地下载,拨号上网太慢了,于是他写了一个叫做ARC的软件,可以将多个文件压缩成一个,这就方便多了。

1986 年,Phillip Katz 发现了 ARC,喜欢的同时又觉得这玩意儿压缩速度太慢,于是就卷起袖子把关键的压缩和解压缩用汇编重写,形成了PKARC,开始卖钱。

图片

图片

Thom Henderson 看到自己的生意被抢,一气之下把 Phillip Katz 告上法庭,理由很充分:你 PKARC 中的注释、拼写错误都和我的 ARC 一样,你这是在抄袭!还有,我的 ARC 虽然是开源的,但是协议规定只能看,不能改!

最终 ARC 胜诉,Phillip Katz 赔了几万美元。

天才 Phillip Katz 当然不服,他研究了 LZ77 算法和 Huffman 算法,将二者结合,创造了新的压缩算法 (deflate) 和新的文件格式 (zip),以及新软件PKZIP

图片

PKZIP 无论是在压缩比,还是在解压缩速度上都把 ARC 打得找不着北,迅速统治了 DOS 时代。

由于 ZIP 格式是公开的,开源界 info-zip 小组也发布了开源、免费的unzipzip,实现了 deflate 算法。

随后,Jean-loup Gailly 和 Mark Adler 基于 deflate 又开发了著名的gzip(文件格式 + 实用程序),替换了 Unix 上的 compress。

图片

gzip 就是在文章开头看到的那个 HTTP 压缩格式。

1991 年,Nico Mak 觉得 PKZIP 的命令行实在是不爽,他就基于 PKZIP 开发了一个 Winodws3.1 的前端(后来用开源的 info-zip 替换了 PKZIP),让大家可以用图形界面的方式来压缩文件,这就是著名的 WinZip

图片

图片

用户发现 WinZip 界面精美,操作友好,根本不用记忆那些烦人的参数,点几下鼠标就完成压缩了。

WinZip 迅速占据了所有的 PC,成为成为 90 年代最流行的共享软件之一。

不过 WinZip 再辉煌,毕竟是 “寄生” 在 Windows 平台上。

Windows 出手,干脆把 Zip 的功能内置到了操作系统中,终结了一切。

图片

04 结语

从 LZ77 开始,到 LZW,compress,Deflate,gzip … 无损压缩算法不断地修修补补,逐渐形成了一个庞大的家族,但是无论怎么改,它们的原理和思想都和最早的 LZ 算法没什么差别。

这些算法帮助我们压缩图像,压缩文本,压缩互联网传输的内容,成为我们日常生活中不可或缺的一部分。

毫不夸张地说,LZ 算法和它的子孙们已经统治了全世界。


via:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值