Huffman 编码与 DEFLATE:最优前缀码、规范码表与比特的去向
内容提要
DEFLATE压缩格式中,Huffman编码在整数码长下最优,规范码只需传码长;15位限长在Canterbury语料上从未触发。实测zlib -9输出中,距离额外比特占41.9%,码表仅0.39%,Huffman码仅比经验熵多0.64%。zopfli靠优化解析省8.0%,其中87%来自距离额外比特,说明解析比熵编码更重要。
延伸解读
Huffman 码在 DEFLATE 中接近最优
文章实测显示,zlib -9 动态块中 Huffman 码仅比每块经验熵多 0.64%,换算到整个输出最多可省 0.35%。这意味着在 DEFLATE 的字母表分布下,整数码长带来的损失很小,换用算术编码或 ANS 等熵编码器收益有限。
距离额外比特是压缩率的主要瓶颈
在 zlib -9 输出中,距离额外比特占 41.9%,而码表仅占 0.39%。这些额外比特不经熵编码,直接以原始比特写出。zopfli 通过优化解析,使距离额外比特平均从 9.19 位降至 7.74 位,贡献了其 8.0% 节省中的 87%。
15 位码长限制在实际语料中很少触发
Canterbury 语料上,zlib -9 的实际码长与不限长 Huffman 完全相同,15 位上限从未生效。即使人为构造 Fibonacci 频数使码长达到 18 位,限长代价也仅多 3 比特,zlib 的启发式修补比最优 package-merge 多 5 比特(0.0175%)。
解析优化比熵编码改进更有效
zopfli 在同一 DEFLATE 格式内比 zlib -9 小 8.0%,其中 87% 的节省来自距离额外比特的减少。这表明在 DEFLATE 中,改进 LZ77 解析(如使用代价模型选择更近的匹配)比更换熵编码器更能提升压缩率,尽管代价是压缩时间大幅增加。
Q&A
Huffman 编码在什么条件下是最优的?
Huffman 编码在“每个符号单独编成整数长度码字、且码字构成前缀码”的约束下最优。一旦允许把多个符号合在一起编码(如算术编码)或符号间存在依赖(上下文),其最优性不再适用。
DEFLATE 的动态块为什么只传码长而不传 Huffman 树?
因为 DEFLATE 使用规范 Huffman 码:只要约定码字分配规则(同一长度按符号顺序取连续值,短码字在字典序上排在长码字之前),码长向量本身就能唯一确定一套码。因此块头只需传码长,解码器可据此重建相同的码表。
DEFLATE 的 15 位码长上限在实际中经常触发吗?
在 Canterbury 语料上从未触发。zlib -9 的动态块中,实际码长与不限长 Huffman 码长代价完全相同。要触发上限需构造接近 Fibonacci 分布的频数,此时 zlib 的启发式限长比最优 package-merge 多 5 比特(0.0175%)。
在 zlib -9 的 DEFLATE 输出中,比特主要花在哪里?
距离额外比特占 41.9%,字面量码占 25.0%,距离码占 16.2%,长度码占 14.6%,长度额外比特占 1.9%,动态块码表仅占 0.39%。经过 Huffman 编码的部分共占 55.8%,原样写出的额外比特占 43.8%。
zopfli 比 zlib -9 压缩率提升主要来自哪里?
zopfli 比 zlib -9 小 8.0%,其中 87% 的节省来自距离额外比特。zopfli 通过迭代优化解析,在代价模型下选择更近的匹配,使每个匹配的距离额外比特从平均 9.19 位降到 7.74 位,而匹配个数和平均长度几乎不变。
Huffman 编码离香农熵有多远?在 DEFLATE 中换用算术编码能省多少?
在 zlib -9 的动态块中,Huffman 码比每块经验熵多 0.64%。即使换成能精确达到经验熵的熵编码器(如算术编码),保持 LZ77 符号与分块不变,最多省整个输出的 0.35%。因此在这个格式中换熵编码器几乎无利可图。