一秒钟看几眼才不会看错
上一卷假装你能连续地盯着信号看。真机器不能——它每秒睁眼几万次,中间全是黑的。直觉上,两次睁眼之间发生的事应该就永远丢了。这一章说明这个直觉是错的,而且错得非常彻底:只要睁眼够勤,中间那些时刻的值,一个字节都没丢。
一段信号,由三个余弦叠成:3 Hz、11 Hz、19 Hz,相位随手选的。最高的成分是 19 Hz。
我用 51 Hz 采样,一秒钟只拿到 51 个数。然后我要问:在两个采样点正中间那一刻(那时候机器根本没在看),信号的真实值是多少?我只准用那 51 个数来回答。
问:我能答得多准?(信号的峰值大约是 2.02。)
为什么直觉说「丢了」
画一排点,问「这些点之间是什么」,答案当然是「什么都可能」。穿过任意一组点的曲线有无穷多条,这是插值这门课的第一句话。
所以采样看起来必然有损。这个直觉的漏洞在于它忘了加一个前提:
那些曲线里,绝大多数是「拐得太急」的。
如果我事先告诉你「这条曲线里没有任何超过 19 Hz 的成分」,你就等于知道了它拐弯的速度有上限。它不可能在两个采样点之间偷偷上去又下来——那么快的起伏本身就是一个高频成分,而我们已经排除了。
加上这个前提之后,穿过这些点的合法曲线只剩下一条。
采样不是「丢掉中间」,是「只记下必要的那几个数」。前提是信号带限——它的谱在某个频率 B 以上全是零。
门槛(香农–奈奎斯特):采样率 fs 必须大于 2B。达到了,中间的每一个时刻都能被精确算回来;没达到,丢掉的东西永久丢掉,而且丢得比你想的更难看(下一章)。
请注意这是一个阈值,不是一条斜坡。fs = 2.01B 和 fs = 100B 一样是无损的;fs = 1.99B 就已经错了。这跟「精度」这个词的直觉完全不同。
那条唯一的曲线怎么算出来
重建公式长这样:每个采样点上放一个特定形状的小山包,然后全部加起来。
sin(πu)
x(t) = Σ x[n] · sinc( fs·t − n ) 其中 sinc(u) = ───────
n πu
sinc 这个山包有一个关键性质:
在 u = 0 处等于 1,
在 u = ±1, ±2, ±3, … 处严格等于 0。
于是在第 n 个采样时刻,只有第 n 个山包出力(值 1),
其余所有山包都刚好过零。—— 这保证了重建曲线穿过每一个采样点。
两个采样点之间的值,由所有山包的尾巴共同决定。
「所有山包的尾巴」这句话要认真对待:sinc 的尾巴衰减得非常慢(按 1/u),所以重建某一时刻的值,理论上要用到全部采样点,包括很远的那些。这是理想重建在工程上难做的根本原因,也是第 15 章那个「理想滤波器造不出来」的另一副面孔。
本机实测。为了让「有限长记录」这件事不干扰结论,这里取一秒钟、51 个点(奇数),三个成分的频率都是 1 Hz 的整数倍,因此信号在这一秒里是严格周期的,此时 sinc 求和有闭式(狄利克雷核)。在这一秒里均匀取 20000 个时刻,逐个和真值比:
采样率 51 Hz,一秒 51 个点,最高成分 19 Hz,奈奎斯特 25.5 Hz
在 20000 个「机器没在看」的时刻上重建:
✓ 最大误差 3.86 × 10⁻¹³
信号峰值 2.0237
也就是说:误差是信号大小的 2 × 10⁻¹³ 倍。
这不是「插值得好」,这是一个恒等式,误差全部来自浮点舍入。
为什么门槛是「两倍」
两种看法,都值得知道。
数数看。一个频率为 f 的实正弦,有两个未知数:振幅和相位。要确定 B 以内的所有频率成分,如果一秒钟里有 B 个可能的频率(每个 1 Hz 一档),那就是 2B 个未知数。一秒钟采 fs 个点,就是 fs 个方程。方程数要不少于未知数:fs ≥ 2B。
看频域。这个看法更有力,因为它顺便解释了下一章的全部内容。采样这个动作,在频域里的效果是:把原来的频谱,每隔 fs 复制一份,无穷多份排开。
原信号的谱(只在 ±B 之间有东西):
╭──╮
───────────┤ ├───────────────────────────────────
−B 0 B
采样之后(每隔 fs 复制一份):
╭──╮ ╭──╮ ╭──╮
─────┤ ├────────┤ ├────────┤ ├────────────────
−fs 0 fs 2fs
要让这些副本互不重叠,需要: fs − B > B → fs > 2B
重叠了,就再也分不开了 —— 那正是「混叠」这个词的意思。
看懂这张图,你就同时看懂了采样定理和下一章的混叠:它们是同一件事的两面,区别只在于副本有没有压到一起。
不够会怎样:先看一眼账单
同一段信号(最高 19 Hz,需要 fs > 38),故意用 33 Hz 采样,再用同样的方法重建:
采样率 33 Hz(不够,需要 > 38) 最大误差 0.9000 ← 信号峰值才 2.0237,这是接近一半的错 而在 51 个采样时刻上呢?误差 1.13 × 10⁻¹⁴
请仔细看第二行。在采样点上,一切完美。你手里的数据没有任何异常,没有报警,没有毛刺。重建出来的曲线严丝合缝地穿过你测到的每一个点。
它只是不是原来那条曲线。那个 19 Hz 的成分,在这份数据里变成了一个 33 − 19 = 14 Hz 的成分,而且它长得和一个真的 14 Hz 一模一样。下一章会把这件事推到极致。
用 sinc 求和从采样点重建两点之间的值,二十行:
import math
fs = N = 51 # 一秒 51 个点(取奇数)
comps = [(3, 1.0, 0.3), (11, 0.6, 1.1), (19, 0.45, 2.2)]
f = lambda t: sum(a*math.cos(2*math.pi*fr*t + p) for fr, a, p in comps)
x = [f(n / fs) for n in range(N)]
def D(u): # 周期 sinc(狄利克雷核),N 为奇数
a, b = math.sin(math.pi*u), N*math.sin(math.pi*u/N)
return 1.0 if abs(b) < 1e-12 else a / b
worst = 0.0
for j in range(20000):
t = j / 20000
s = sum(x[n] * D(fs*t - n) for n in range(N))
worst = max(worst, abs(s - f(t)))
print('最大误差 %.2e' % worst) # 最大误差 3.86e-13
把 fs = N = 51 改成 33(低于 2 × 19 = 38),误差立刻跳到 0.9。这一行改动值得亲手做一次——它是这本书里最干净的一次「阈值不是斜坡」的演示:51 → 41 → 39 都还是 10⁻¹³,一跨过 38 就崩。
python3 -c "print('把上面那段存成 sample.py 再 python3 sample.py')"
在线跑:python.org/shell,纯标准库。想看动画版:Wikipedia 的 Nyquist–Shannon sampling theorem 词条里那几张频谱周期化的图,就是上面那张 ASCII 图的正规版本。
- CD 的 44100 Hz。人耳的上限大约 20 kHz,两倍是 40 kHz,为什么是 44.1?多出来的 4.1 kHz 是留给抗混叠滤波器的过渡带的——现实中的滤波器不可能在 20 kHz 处垂直落地(这正是第 15 章的内容)。那个具体的数字则来自更琐碎的历史原因:早期数字录音把音频存在录像带上,44100 = 3 × 5² × 7² × 2² 刚好能同时整除 NTSC 和 PAL 两种制式的行频。
- 电影的 24 帧/秒。这个采样率对「人物走动」够用,对「车轮转动」远远不够。所以银幕上的马车轮子经常在倒转——这是下一章的主角。
- 示波器的「等效采样」。高端示波器能显示比自己采样率高得多的信号,靠的是对重复信号在不同的相位上多次采样再拼起来。这不违反定理:它实际观测的总时长远不止一个周期。
- 你的埋点和报表。「每天统计一次日活」就是一次 1/86400 Hz 的采样。如果你的业务有明显的周内周期(周末低、周三高),而你按 7 天做平均,就正好把这个周期采没了——它会以「趋势」的形式出现在你的图上。这是混叠在数据分析里最常见的一次现身。
「两个采样点之间的东西丢了,重建只能靠插值猜,猜得再好也是猜。」
在带限这个前提下,那不是猜,是算。上面实测的 3.86 × 10⁻¹³ 说明它是一个恒等式,误差全部来自浮点数而不是方法。
关键在于「带限」这个前提有多强:它说的是「这条曲线不可能在两点之间偷偷拐一下」。加上这个约束之后,穿过这些点的合法曲线从「无穷多条」塌缩成了「恰好一条」。所有的信息都在,因为一开始就没有那么多信息。
反过来,这也说明了这个定理的真正风险在哪里:不在于采样率不够,而在于你以为它带限,其实不是。真实世界里没有任何信号是严格带限的(严格带限意味着在时间上无限长,第 10 章会说明为什么)。所以工程上的做法永远是:先用一个模拟滤波器把高频砍掉,再采样。顺序不能反——下一章会给出「反了会怎样」的完整账单。
正确答案是 C:最大误差 3.86 × 10⁻¹³,浮点意义上的严格零。
A、B 「插值总有误差」——这个直觉的来源是我们平时用的插值方法(线性、样条)确实有误差。它们之所以有误差,是因为它们没有用上「带限」这个前提,只用了邻近的几个点。sinc 重建用的是全部采样点,而且它的形状正是为「带限」这个约束量身定做的。换个说法:线性插值默认信号是折线,样条默认它是分段三次曲线,而 sinc 插值默认它是带限的——只有最后一条是真的。 D 「信息已经不在了」——这是最有教育意义的一个错。它把「我没有观测到那一刻」和「那一刻的信息不存在」混为一谈了。这两件事在带限前提下是不同的:那一刻的值是被别处的观测决定的,就像知道了一条直线上的两个点,就等于知道了这条直线上的所有点,哪怕你从没测过第三个点。这个换域一下子解释了三件事:为什么门槛是 2B(副本不能压到一起);为什么超过门槛就一点不丢(副本之间有空隙,用一个理想低通把中间那份挑出来就行,而「挑出来」这个乘法在时域里就是 sinc 卷积——正是上面那个重建公式);以及为什么不够的时候无可挽回(副本已经叠加成了一个数,加法不可逆)。
三个结论,一张图。这就是「换个域看」的价值——在时域里,这三件事是三个需要分别记住的知识点;在频域里,它们是同一张图的三种读法。
这一章的一句话
采样不是丢掉中间,是只记下必要的几个数;只要每秒看的次数超过最高频率的两倍,中间每一刻的值都能被精确算回来——而这是一个阈值,不是一条斜坡。
下一章处理跨过阈值之后的那一侧。它比「误差变大」难看得多:高频不会变模糊,也不会消失,它会换一个身份混进低频里,而且伪装得天衣无缝。下一章会给出这本书里最不讲道理的一组数——1400 Hz 的正弦和 400 Hz 的正弦,用 1000 Hz 采样,采出来的一千个数最大只差 2.39 × 10⁻¹²。不是「像」,是同一串数。