你每天打开的每一个网页、发送的每一条消息、保存的每一张图片、下载的每一个 App,背后都跑着同一段"隐形引擎"——无损压缩。它把一份 1MB 的文本压到 250KB,把一个看似无意义重复的日志文件砍掉 99%,却保证你解出来时一个字节都不差。Tom Scott 这条播放量 200 万的视频《How Computers Compress Text: Huffman Coding and Huffman Trees》把这件事讲得极其漂亮,它真正值得看的原因不是教你写压缩工具,而是揭示了一个反直觉的真相:压缩从来不是"删掉"数据,而是"回头看"——找出重复、给高频内容发短号。 更扎心的是,这套机制还有一个数学天花板:随机的、真正没规律的数据,你一个比特都压不动,硬压反而会变大。理解了这个内核,你才算真正搞懂为什么 gzip、zip、PNG 是同一个东西,也才能在工程里知道该压什么、不该压什么。
| 项目 | 内容 |
|---|---|
| 视频标题 | How Computers Compress Text: Huffman Coding and Huffman Trees |
| 频道 | Tom Scott(约 580 万订阅的顶流科普频道) |
| 时长 | 约 11 分钟 |
| 播放量 | 200 万+(2,003,026) |
| 发布 | 约 2018 年 |
| 核心主题 | 无损压缩的数学内核——Huffman 编码与 Huffman 树 |
这是 Tom Scott 在视频里反复暗示、但真正点透的是后面那套机制(LZ77,由 Lempel 和 Ziv 在 1977 年提出)。我们对压缩有个本能的误解:以为它像剪辑视频一样"砍掉多余帧"。错。无损压缩不允许丢任何信息,它的真正动作是把重复的东西换成更短的引用。
举个视频里那种风格化的例子:一段文本是 the the the the the the the the the。你不需要存 9 个 the,你只需要存一个 the,再加一句"重复 9 次"。这就是 LZ77 的核心——回看引用(back-reference)。它把已出现过的串,用一个 (距离, 长度) 的指针指回去。比如对 abcabcabc,第一个 abc 照抄,后面就是"往回退 3 格,抄 6 个字符"。一长串高度重复的数据(比如日志、JSON、源代码),靠这一招就能砍掉九成以上。这是我对视频的延伸:Tom Scott 的视频聚焦 Huffman,但完整的 gzip 还多了一层 LZ77,二者合起来才是真正的 DEFLATE。
压缩的第二层功夫,处理的是"即便没有完全重复,但符号出现频率不均"的情况。英语里 e 出现得比 z 多得多。视频里 Tom Scott 用莫尔斯电码做类比:莫尔斯给最常用的字母 E 分配最短的符号(一个点),就是因为发报员整天在敲 E。Huffman 编码把这个思想做到了数学上的极致——给高频符号短编码,低频符号长编码。
普通的 ASCII 给每个字符都分配固定 8 位,不管你常用不常用。Huffman 打破了这个"一刀切"。如果你这段文本里 e 出现了 1000 次、z 只出现 2 次,那 e 就该用 3 位、z 可以用 10 位——只要平均下来更短就赢。David Huffman 在 1952 年还是 MIT 研究生时,在一堂老师布置"找到最优编码"的作业里,倒推出这套算法,据说他交作业前一晚才想出来,结果这个"作业"成了信息论的奠基性成果之一。这是视频的核心原话方向,我补充的是它的历史背景。
Huffman 编码具体怎么分配长短?视频最精彩的部分就是构造那棵"Huffman 树"。规则出奇地简单,是个贪心过程:
妙处在于:频率低的符号会被塞到树的深处(路径长、编码长),频率高的符号会被反复"提"上来、离根很近(编码短)。整个构造是 O(n log n) 的,而且是符号级编码里数学上最优的——没有任何一种"一个符号对应一个变长码"的方案能比它更短。这套贪心算法的正确性证明很优雅,核心是"最低频的两个符号一定在最深的、互为兄弟的位置",由此归纳。我对视频的延伸:Huffman 是"逐符号"最优,但不是全局最优——块编码(一次编码多个符号)能更好,代价是表更大,这也是为什么后来的算术编码能超越 Huffman。
变长编码有个看似致命的问题:e 是 0,a 是 01,那读到 01 的时候,到底是 e+某位,还是 a?这会让解码歧义。Huffman 编码能成立,靠的是一个关键性质——前缀无关(prefix-free):没有任何一个符号的编码,是另一个符号编码的前缀。
因为 Huffman 编码全都对应树的叶子,而叶子不可能出现在另一条到叶子的路径中间,所以前缀无关天然成立。解码时你只要从根开始,读一位走一步,撞到叶子就输出一个符号、回到根,永远不会走错。这正是 Huffman 树把"频率"和"可解码性"两件事一次性解决的原因。Tom Scott 在视频里把这点讲得很清楚:莫尔斯电码其实不满足严格前缀无关,所以才需要字符之间的停顿;Huffman 不需要停顿,可以一气读到底。 这个对比是我特别想强调的洞察。
这是整件事最反直觉、也最容易被科普视频略过的部分。压缩不是无限魔法,它有一个数学上的硬天花板——Shannon 熵(Claude Shannon,1948,《通信的数学理论》)。一段数据的信息熵,就是你理论上能把它压到的最小位数。英文文本的熵大约是每字符 1.0~1.3 位,远低于 ASCII 的 8 位,所以英文文本理论上能压到原来的 1/6~1/8;gzip 在实践中通常做到 1/3~1/4,已经接近极限。
更狠的是抽屉原理(鸽巢原理)给的反命题:无损压缩不可能把所有文件都变小。因为如果有某种压缩能把任意文件都变小,那你把所有可能的文件压缩后,会有两个不同文件映射到同一个压缩结果——解压时就分不清了,矛盾。结论是:必然存在压不动的文件,而且对真正随机的数据,硬压反而会变大一点点(因为还得加上压缩格式的开销)。所以你给一段已经加密的、或者本来就是高熵随机的内容套 gzip,输出往往比输入还大。这是我对视频的延伸,Tom Scott 聚焦 Huffman 的"怎么压",而熵和抽屉原理回答了"为什么不能压到 0"——这才是压缩的完整闭环。
把上面两层拼起来,就是 1996 年写进 RFC 1951、由 Phil Katz 在 PKZIP 里定型的 DEFLATE 算法——gzip、zip、PNG(通过 zlib)用的全是它。真实文件压缩是一条流水线:
(距离,长度) 回看指针。这一步干掉的是"重复"。两次压缩叠buff。所以 aaaaaaaa...(一百万个 a)经过 LZ77 变成"一个 a + 一个'重复 999999 次'的指针",再经 Huffman 微调,最终只有几百字节——砍掉九成以上轻轻松松。而一段毫无规律的随机字节,LZ77 找不到重复、Huffman 找不到频率差,两段都失效,DEFLATE 只能"原样打包"并加一点格式头,文件略变大。理解了这条流水线,你就理解了为什么"重复多的内容压缩比高"——它不是玄学,是 LZ77 在干活。
graph TD
A[无损压缩 Lossless] -->|数学天花板| B[Shannon熵 1948<br/>可压到的极限]
A -->|不可能压所有文件| C[抽屉原理<br/>随机数据压不动]
A --> D[DEFLATE 真实流水线<br/>RFC 1951 / gzip·zip·PNG]
D --> E[第一段: LZ77 1977<br/>回看引用 干掉重复]
D --> F[第二段: Huffman 1952<br/>变长短码 干掉频率差]
F --> G[Huffman树<br/>贪心: 合并最低频两个]
F --> H[前缀无关 prefix-free<br/>叶子路径 保证可解码]
E --> I["输出: 字面量 + (距离,长度)指针"]
I --> F
H --> J[结果: 高频符号短码<br/>平均码长最短]
懂了这套内核,工程上能直接转化成几条决策:
第一,选对格式而不是无脑 gzip。 文本、JSON、日志、源代码——这些 LZ77 的"重复猎场",用 zstd(Facebook 出品,2026 年已是新标配,比 gzip 快一个数量级、压缩比还更好)或 gzip 压。但已经压过的数据不要再压:JPEG、PNG、MP4、WebP 本身就是压缩过的(JPEG 是有损 DCT,PNG 是 zlib/DEFLATE),再套一层 gzip 几乎压不动,纯浪费 CPU。很多后端性能问题就是出在"对所有 HTTP 响应无脑开 gzip",结果把二进制图片也压了一遍。
第二,对加密/随机数据远离压缩。 既然熵高就压不动,那加密后的密文、已经压缩的归档、UUID/Token 这类高熵串,压缩只会让它变大。更隐蔽的风险是 BREACH/CRIME 这类攻击——当攻击者能控制部分明文并观察压缩后的密文长度变化时,压缩反而成了信息泄露的侧信道。对敏感 API 响应做压缩前,先想清楚这条。
第三,2026 年的升级路径:Brotli 与 zstd。 Brotli(Google)专为 HTTP 内容设计,内置了针对 HTML/CSS/JS 的预定义字典,比 gzip 在文本上多压 15%~25%,现代浏览器全支持,给 Web 接口开 Content-Encoding: br 是免费收益。zstd 则在日志、数据库(PostgreSQL/ClickHouse 的列存压缩)、消息队列场景全面替代 gzip,关键是它支持"压缩等级"和"训练字典",对大量小而相似的文件(比如 JSON 记录)训练专属字典后压缩比能再翻倍。
第四,把熵当成度量衡。 当你想判断"这段数据还有多少可压缩空间"时,先看它的香农熵。熵接近 8 位/字节意味着接近随机、压不动;熵低意味着有结构、LZ77 有活干。这就是为什么 head -c 1G /dev/urandom > file; gzip file 之后文件会变大——它在用数学告诉你"这里没有冗余可以剥削"。
压缩的本质不是删字而是"回头看 + 发短号"——LZ77 用指针消灭重复、Huffman 用变长码消灭频率差,而 Shannon 熵和抽屉原理画死了它压不动的数学天花板。
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
programming,届时可做"JPEG/DCT 有损压缩"作为姊妹篇(钩子:丢掉九成像素你却看不出)lengthSeconds),如需精确时长建议用 yt-dlp 获取(需授权)[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
