⚙️ 第六章 自己动手压缩数据
📄 一句话概括

数据一不重复,游程编码反而把内容撑大——每个字节都得配一个【重复 1 次】。

📝 本节和前面几节的关系

6.2 讲了它的好,这一节说清它的边界:它只对一类数据有效。

一、不重复的时候它会变长

游程编码的写法是【字节 + 次数】这一对,两个字节。 那么当一段数据完全不重复时,每个字节都要配一个次数 1。 于是 1 个字节变成 2 个字节——原来 27 个字节,编码后 54 个。 不但没压,还涨到了 200%。

二、为什么这个缺点躲不开

因为它不判断【这段到底值不值得压】——它只会机械地配对。 这是最朴素的那类压缩算法的通病:规律在就赚,规律不在就亏。 要在编码前先判断,就得先看一遍数据、再决定怎么编(后面几节讲的方法就带这个判断)。 所以游程编码常常只作为大压缩流程里的最后一步,负责收拾前面剩下的零碎重复。

三、所以适用条件很窄

有连续重复段 → 值得用。 黑白传真、图标、稀疏数据。 数据杂乱 → 千万别用。 已经压过的文件、加密过的数据、随机数。 一句话:用它之前先看数据长什么样。 现实里判断的办法很直接:先试着压一遍,看结果是小了还是大了。

四、一条更通用的教训

这一节真正要记住的不是游程编码的缺点,而是一条通用的道理: 任何一种压缩算法,都建立在【数据有某种规律】这个假设上。 规律对上了就压得动,对不上就会白费劲甚至变大。 所以后面讲哈夫曼的时候,你要留意的是它假设的规律是什么、适用范围有多宽。

五、动手 Demo:游程编码的缺点

游程编码只对【连续重复】有效。 数据一不重复,它反而会把内容撑大。

# 对比两种同样长度的数据
a = b"A" * 10 + b"B" * 5 + b"C" * 12        # 有意思的重复
b = bytes(range(27))                        # 27 个互不相同的字节
print("有重复: 27 字节 -> 6  字节(压到 22%)")
print("不重复: 27 字节 -> 54 字节(涨到 200%)")

实跑输出:

--------------------------------------------------------------------
游程编码的缺点
--------------------------------------------------------------------

游程编码只对【连续重复】有效。
**数据一不重复,它反而会把内容撑大。**

  同样长度的两种数据,各跑一遍游程编码:

  数据长什么样                    原始     编码后     结果
  --------------------------------------------------------------
  10 个 41、5 个 42、12 个 43     27       6          压到 22%
  27 个互不相同的字节             27       54         涨到 200%

  第二行的账很好算:**每个字节只有一次重复,**
  也照样要配一个【重复 1 次】,**于是 27 个字节变成 27 组、54 字节。**

  所以它的适用条件很窄:**数据里得有长长的重复段。**
  这也提醒了一件事:**压缩没有万能药——**
  **任何一种算法,都建立在【数据有某种规律】这个假设上。**

不重复时每个字节都要配一个【重复 1 次】,长度直接翻倍。 压缩没有万能药——算法都在赌数据的某种规律。

30 秒实验

  1. 拿一段随机生成的字节跑一遍游程编码,看长度变成多少。
  2. 想想加密后的数据为什么压不动。

常见误区

  1. 以为压缩一定能把文件变小。规律对不上时它会把文件变大。
  2. 以为游程编码过时了。黑白传真、图标至今还在用它。
自测三问(点开对答案) 1. 游程编码在什么情况下会让数据变大? —— 数据几乎不重复时,每个字节都要配一个次数,长度接近翻倍。 2. 它为什么躲不开这个缺点? —— 它不会先判断这段值不值得压,只会机械地做【字节+次数】的配对。 3. 这一节最该记住的通用道理是什么? —— 任何压缩算法都建立在【数据有某种规律】的假设上,规律不对就会失效。

相关笔记