← Huffman 编码树 · 拆解 huffman coding / 应用实例:它的实际使用场景 待审核 4 / 6
applications · 真实世界

应用实例:它的实际使用场景

Huffman 不只是教科书里的概念——今天打开的几乎每个网页、每张图、每首歌,背后都有它的身影。先用一个实时 demo 厘清「压缩率」这件事,再剖析四个真实场景。

1 · 实时压缩 demo:估算一段文本的压缩率

下面按字符级 Huffman(不含编码树本身的开销)估算压缩后大小,并对照 Shannon entropy 下界。文本越长、分布越偏,节省得越多。可以分别贴入一段重复较多的文本与一段随机字符对比。

注意现实里的两笔账: 其一,解码端需要知道码表,所以真实文件要么把码表一起存进去(短文本时这笔开销可能反而让文件变大),要么双方约定一套固定码表。其二,字符级 Huffman 只利用了「单字符频率」,抓不到 thing 这种词组重复——所以现实中它几乎总是和别的手段组合使用。下面就看这些组合。

2 · 四个真实场景

DEFLATE · gzip / zlib / PNG / ZIP

你访问的每个网页:gzip 里的 Huffman

DEFLATE(gzipzlib.zip、PNG 的像素流都用它)分两步:先用 LZ77 把重复出现的片段换成「往回 N 字节、抄 L 个」的引用——这一步消除了词组级重复;再用 Huffman 把 LZ77 输出的字面字节和「长度 / 距离」符号按频率编短。两者互补:LZ77 管「重复」,Huffman 管「高频」。HTTP 响应头里的 Content-Encoding: gzip 背后跑的就是它。

JPEG · 有损图像

每张照片:JPEG 末端的 entropy coding

JPEG 先把图像分块做 DCT 变换、量化(这一步有损,丢掉人眼不敏感的高频),得到大量为 0 的系数;再用 游程 (RLE) + Huffman 收尾:游程把连续的 0 压成「跳过几个」,Huffman 再给这些符号按频率编短。JPEG 标准甚至内置了一组推荐 Huffman 表,大多数编码器直接用,省去存表的开销。

MP3 / AAC · 音频

每首歌:MP3 的 Huffman 码本

MP3 在心理声学模型丢掉听不见的频率、量化之后,用一组预定义的 Huffman 码本对频域系数做无损熵编码——编码器会为每个频段挑选最合适的那张码本。这是「先有损建模、再做无损熵编码」的经典范式:Huffman 始终是最后那道无损工序

canonical Huffman · 工程化

如何把「树」存进文件:canonical Huffman

直接存一棵树并不划算。规范 Huffman 码 (canonical Huffman) 发现:只要知道每个符号 codeword 的长度,再加一条确定的排序规则,codeword 就能唯一重建——于是文件里只需存一串「长度」,不必存树结构。DEFLATE、JPEG 都采用这一方法。它也一并解决了「动手建树」那页提到的 tie-break 歧义:规则一固定,编解码两端必然得到同一套 codeword。

它的边界 & 后继: Huffman 每个符号至少占整整 1 bit,当某符号概率远超 50%(比如 0.95)时,理论上它只值 0.07 bit,Huffman 却仍要花 1 bit,逼不到熵算术编码和现代的 ANS(Zstandard、Brotli、新一代图像 / 视频编解码里用)能突破这个「整 bit」限制、更贴近熵。所以新格式常用它们替代 Huffman——但 Huffman 因为极快、实现简单,至今仍在海量场景里服役。

3 · 一句话总结这一系列

Huffman = greedy 地反复合并两个最小建出一棵树 → 树天生给出 prefix code → 高频字符离根近、codeword 短 → 整体逼近 Shannon entropy。它简单、最优(在「每符号整 bit、按单符号 frequency」的前提下)、且无处不在。而且这个 greedy 不止用于压缩——石子合并一页展示它在另一类问题中的应用。