2026-11-01 信息熵:我算了一段中文的「信息量」
《数学之美》讲信息熵那一节,我的概括是:一段消息的信息量等于它的不确定性,可以精确算出来。我一直觉得"信息量"是个虚词,直到把它算成数字。
一、书里的主张
香农给的定义很朴素:一件事发生的概率越小,它发生带来的信息量越大。
单个事件的信息量是 -log2(p)。整个分布的平均信息量就是熵:
H = - Σ p_i · log2(p_i)
底数取 2,单位是 bit——也就是"平均需要几个二进制位来表达"。
这句"需要几个位"是关键。它不是比喻,是字面意思:一个序列的熵,就是编码它的理论下界。
二、先把最小的零件验一下
单事件的信息量 -log2(p):
概率 0.5 信息量 1.00 bit
概率 0.25 信息量 2.00 bit
概率 0.125 信息量 3.00 bit
概率 0.1 信息量 3.32 bit
概率 0.01 信息量 6.64 bit
概率 0.001 信息量 9.97 bit
概率每减半,信息量加 1 bit。这条曲线很符合直觉:掷一次硬币 1 bit,猜中一个千分之一的事件能拿到近 10 bit。
这部分没什么意外,公式就长这样。
三、然后算真东西
我写了一段 277 字的中文,和一段 530 字符的英文,按字符统计频率:
中文段落:277 个字符,用到 159 种不同字符
熵 H = 6.909 bit/字符 (理论上界 log2(159) = 7.313)
英文段落:530 个字符,用到 33 种不同字符
熵 H = 4.187 bit/字符 (理论上界 log2(33) = 5.044)
两个数都落在 0 和 log2(种类数) 之间,符合公式。中文 6.909 很接近上界 7.313,说明这段中文里字符分布相当均匀——因为 159 种字符摊在 277 个位置上,每种平均不到 2 次。
这就是样本小带来的问题:字符种类被高估,熵也是高估的。 真拿一本书来统计,常见字会明显集中,熵会比这个低不少。这一点我得说清楚,不能拿 6.9 当"中文的熵"。
四、真正有意思的是这个对比
把熵和"实际存储开销"放一起看:
中文:UTF-8 存一个字符固定 3 字节 = 24 bit
熵只有 6.909 bit/字符
理论压缩空间 = 24 / 6.909 = 3.47 倍
英文:ASCII 存一个字符固定 8 bit
熵只有 4.187 bit/字符
理论压缩空间 = 8 / 4.187 = 1.91 倍
昨天那篇写字符编码,算出"中文"两个字在 UTF-8 里是 6 个字节,每个字节 8 bit,一个汉字固定 24 bit。当时的结论是"这是个映射表,没有对错"。
今天这个数给了另一面:24 bit 里,真正有用的平均只有 6.9 bit。 剩下的 17 个 bit 是固定长度编码的浪费。
压缩算法干的就是这件事——把那 17 bit 里可恢复的部分去掉。所以"UTF-8 不是压缩格式"这句话,现在有了具体的数字支撑。
五、熵为零是什么样
26 个字母各一次 26 字符 熵 4.700 bit
26 个 a 26 字符 熵 0.000 bit
真实中文 277 字符 熵 6.909 bit
"26 个 a"的熵是 0。它当然有 26 个字符、占 26 字节,但它一点信息量都没有——因为你能完全预测它,看到第一个 a 就知道后面全是什么。
这就是熵容易被误读的地方:它衡量的不是长度,是猜不猜得到。
一个 10 MB 的文件如果全是 0,熵是 0,压缩后可能只有几 KB。反过来,一段 100 字的随机乱码,熵接近 log2(字符集),几乎压不动。这就是"加密后的数据不能被压缩"的原因。
六、一个我必须承认的坑
中文 6.909 和 英文 4.187 这两个数,不能直接说"中文信息量比英文大"。
因为单位不一样:一个汉字大致相当于一个语素或半句话的分量,一个英文字母只是半个音节。2500 个常用汉字 vs 26 个字母,字符集完全不同,字符熵没有可比性。
要比的话得按"每句话"或者"每个词"来算,那是另一套统计(词的熵、二元组的条件熵)。
书里那种"一个汉字的信息量是 X bit"的说法,我现在的理解是:它有前提,前提是统计口径一致。 脱离口径单看数字,很容易得出错误结论。
七、我的判断
熵这个公式本身没什么可怀疑的,跑一遍就对上了。真正有价值的是它给了一个「下界」的思路:
- 存储的实际开销 vs 熵,差值就是可压缩的空间
- 熵越低的序列越可预测,也就是越好压
- 完全不可预测(随机)的序列,压不动
这三点解释了压缩软件、加密数据、以及"为什么日志能压得很小"。
至于"信息量"这个词,我现在能接受它了——它不是文艺说法,是"平均需要多少 bit 来表达"的另一种讲法。
附:核心代码
import math
from collections import Counter
def entropy(text):
n = len(text)
counts = Counter(text)
h = 0.0
for c, k in counts.items():
p = k / n
h -= p * math.log2(p)
return h, len(counts), n
h, alpha, n = entropy("一段中文……")
print(f"{n} 个字符,{alpha} 种,熵 {h:.3f} bit/字符,上界 {math.log2(alpha):.3f}")
浙公网安备 33010602011771号