凭什么拆得开
上一章那台机器有一个还没堵上的漏洞:缠错速度的时候,那些「往外推」的力气真的会严格抵消吗?只要不严格,3 Hz 的答案里就会渗进一点 5 Hz,5 Hz 的答案里也会渗进一点 3 Hz,整台机器立刻从「测量」退化成「估计」。这一章堵这个漏洞,用的是全书唯一一条需要动笔的恒等式。
你手里有 64 个采样点,一秒钟采完。上一章那台机器可以用任何速度去缠——1 圈/秒、2 圈/秒、3.7 圈/秒、100 圈/秒,都能算出一个质心。
问:从这 64 个数里,你最多能问出多少个互相独立的频率?(「独立」的意思是:知道了前面那些的答案,也推不出这一个的答案。)
漏洞在哪
把上一章的动作写清楚一点。假设信号里同时有 3 Hz 和 5 Hz,我们要查 3 Hz,于是拿 3 圈/秒去缠。质心里其实有两笔账:
c(3) = 〈 信号里的 3 Hz 成分 , 3 圈/秒的圈 〉 ← 我们想要的
+ 〈 信号里的 5 Hz 成分 , 3 圈/秒的圈 〉 ← 串进来的
只有当第二项 == 0,这台机器才是「测量」。
如果第二项只是「比较小」,那它就是「估计」,
而且信号越复杂,串进来的东西越多,估得越离谱。
那第二项到底等于多少?把它写开:一个 5 Hz 的旋转,被一个 3 圈/秒的圈缠住,等价于一个净转速为 5 − 3 = 2 圈/秒的旋转。于是问题变成了:
一个匀速转动的向量,在一整秒里转了整整 2 圈,把它每一刻的位置加起来,等于多少?
一句话的证明
不用算。想象一下:这些向量首尾相接,长度都一样,方向均匀地铺满整个圆周。首尾相接地走一圈,你回到了出发点。所以和是零。
写成算式也就三行(等比数列求和):
N−1
Σ e^(i2πm·n/N) m 是整数,且 m 不是 N 的倍数
n=0
设 z = e^(i2πm/N),这是一个 N 次单位根,z ≠ 1,而 z^N = 1。
z^N − 1 1 − 1
和 = ────────── = ─────── = 0
z − 1 z − 1
分子严格为零(z^N = 1),分母不为零(z ≠ 1)。于是和严格为零。
三个条件缺一不可,而这三个条件正好对应这本书后面所有麻烦的来源:
| 条件 | 它保证了什么 | 破坏它会怎样 |
|---|---|---|
m 是整数 | 转过的是整数圈,首尾相接正好回到起点 | → 第 8 章:泄漏 |
m 不是 N 的倍数 | 这两个频率在采样点上确实不同 | → 第 6 章:混叠 |
| 加起来的是完整的 N 个点 | 圆周被均匀铺满,没有缺口 | → 第 7 章:分辨率 |
这张表值得记住。后面三章讲的三个大麻烦,其实是同一件事的三种破法:这个和不再严格为零了。
本机把 2016 对全算了一遍
数学说它严格为零。浮点数说什么?把 N = 64 的全部 2016 对不同频率(C(64,2) = 2016)两两算一遍内积:
N = 64,一共 C(64,2) = 2016 对不同频率 逐对计算 |Σ e^(i2π(k−l)n/64)| 最大的一个残留:1.6 × 10⁻¹³ (出现在 k=0, l=60) 数学上的正确值:0(严格)
1.6 × 10⁻¹³ 不是数学的锅,是 cos 和 sin 在 64 次累加里攒下的舍入误差。数学上它是零,浮点上它是 10⁻¹³。这两者的区别在这本书里会反复出现,第 8 章的对照实验尤其依赖它——那里我们得能分清「本来就是零」和「被算糊了」。
正交(orthogonal)这个词吓人,意思却很朴素:两样东西的内积是零。而内积是零的几何意思是垂直——你在一个方向上走多远,都不会让另一个方向上的坐标变一点点。
用日常的话说:互相看不见。东西南北里,「往东走」和「往北走」互相看不见——你往东走一百米,北向坐标纹丝不动。这本书里那些不同转速的圈,彼此就是这个关系。
注意别和概率论里的独立混了。正交是几何的(这一组数据上内积为零),独立是概率的(联合分布等于边缘分布之积)。两个随机变量可以正交但不独立。它们只是碰巧共用了「互不影响」这个中文说法。
正交换来的东西:这台机器为什么这么便宜
「把信号写成一堆旋转的和」,本质上是解一个方程组:N 个采样值是已知数,N 个分量的强度是未知数。一般来说,解一个 N 元线性方程组要 O(N³) 次运算——N = 1024 的时候是十亿次,还没到 FFT 就已经算不动了。
正交把这件事整个抹掉了。因为基向量两两垂直,每个未知数都可以单独解出来,不用管别的:
要求第 k 个分量有多大? 拿第 k 个圈去缠一遍,取质心。完事。 不需要知道别的分量是多少,不需要联立,不需要消元。 代价:从 O(N³) 掉到 O(N²)。(第 17 章会再砍到 O(N log N)。)
实测一下这个「单独解出来」有多准。造一个信号,里面按 2、7、11 圈/秒放三个余弦,振幅分别是 0.7、0.2、1.3。然后各缠一遍:
放进去: 0.7 0.2 1.3 解出来: 0.7000000000 0.2000000000 1.3000000000 最大误差:6.7 × 10⁻¹⁶
三个分量互相之间一点都没有渗。这是整本书立得住的地基:频域里的每一根谱线,都是可以单独相信的一个数。
那到底有多少个独立频率
回到开头那个问题。你确实可以用任意实数速度去缠,比如 3.7 圈/秒——机器不会拒绝你,它会给出一个数。但那个数不是新信息:它是前面 N 个整数速度答案的一个(确定的)组合。
原因就是刚才那条恒等式:只有 m 取整数时那个和才为零。在 64 个采样点上,「64 个整数速度的圈」正好构成一组两两垂直、且刚好铺满整个空间的基——64 个数进去,64 个坐标出来,不多不少。
顺手做个实验:拿 3 圈/秒的圈去缠一个 3.5 Hz 的成分,内积是多少?
〈 3.5 Hz , 3 圈/秒 〉 = 0.0156 ← 不是零
不是零。所以「不同频率一定正交」这句话是错的:只有在整数圈的意义下才正交。这个 0.0156 是第 8 章那场大麻烦的第一滴水。
把 2016 对全算一遍,十行:
import math
N = 64
worst, pair = 0.0, None
for k in range(N):
for l in range(k + 1, N):
sr = sum(math.cos(2*math.pi*(k-l)*n/N) for n in range(N))
si = sum(math.sin(2*math.pi*(k-l)*n/N) for n in range(N))
m = math.hypot(sr, si)
if m > worst:
worst, pair = m, (k, l)
print(N*(N-1)//2, '对', ' 最大残留 %.1e' % worst, ' 出现在', pair)
# 2016 对 最大残留 1.6e-13 出现在 (0, 60)
再看看「正交换来的东西」:三个分量各自单独解,互不干扰。
amp, freq = [0.7, 0.2, 1.3], [2, 7, 11]
x = [sum(a*math.cos(2*math.pi*f*n/N) for a, f in zip(amp, freq)) for n in range(N)]
for f in freq:
print('%.10f' % (2 * sum(x[n]*math.cos(2*math.pi*f*n/N) for n in range(N)) / N))
# 0.7000000000 / 0.2000000000 / 1.3000000000
把 freq 里任何一个换成非整数(比如 [2, 7.5, 11]),三个数会同时变得不对——串扰立刻出现。这个小改动值得亲手做一次。
python3 -c "import math;N=64;print(max(abs(complex(sum(math.cos(2*math.pi*m*n/N) for n in range(N)),sum(math.sin(2*math.pi*m*n/N) for n in range(N)))) for m in range(1,N)))"
在线跑:python.org/shell,纯标准库,不需要 numpy。
- 你手机上的每一个数据包。4G/5G/Wi-Fi 用的 OFDM 全称是「正交频分复用」,做法是把带宽切成几百上千个子载波,每个子载波的频率间隔正好是符号长度的倒数——也就是刚好落在「整数圈」上。这样它们两两正交,虽然频谱重重叠叠,接收端却能一根一根分开。这一整套协议就架在这一章那条恒等式上。
- GPS 的码分复用。三十多颗卫星在同一个频率上同时喊,接收机能分开,靠的是每颗星的伪随机码互相近似正交(不是严格正交,所以有个「码间干扰」的指标)。第 16 章会用同一套东西把信号从噪声里捞出来。
- PCA 和特征向量。主成分分析做的事和这一章一模一样:找一组两两垂直的方向,把数据投影上去,每个方向单独解。区别只是这一章的方向是事先定好的(各种转速的圈),PCA 的方向是从数据里学出来的。
- 正交实验设计。做 A/B 测试的时候如果同时改两个变量,标准做法是让两个变量的取值表「正交」,这样两个效应可以分别估出来而不互相污染。同一个词,同一个理由。
「频率不同就互不干扰。所以只要两个信号频率不一样,频谱上就一定分得开。」
只有当它们在你的观测窗口里都转了整数圈时才互不干扰。上面刚测过:3 Hz 的圈和 3.5 Hz 的成分,内积是 0.0156,不是零。
而现实中你观测到的信号,频率几乎永远不是整数圈——你不可能事先知道那个音是 440.0 Hz 还是 440.3 Hz,然后据此挑一个刚好整除的窗口长度。所以「串扰」是常态,「干净」是例外。
第 8 章会算出这笔账有多大:一个纯音只要挪半个格子,装下它 99% 能量的谱线就从 1 根变成 41 根。第 9 章会给出唯一的应对手段(加窗),以及那个手段要付的价钱。判据是:看到一个漂亮的频谱,先问「我这一段的长度,是不是刚好被那个频率整除」——如果不是,你看到的形状里有一部分是你自己造出来的。
正确答案是 B:64 个。你能问无限多个,但只有 64 个答案是独立的。
A 「一半是负频率的镜像」——这句话本身完全正确,但前提是信号是实数。实信号的谱确实共轭对称,64 个复数里只有 64 个独立的实数(不是 128 个)。可如果你的信号本来就是复数(无线电里的 I/Q 采样、光学里的复振幅、量子态的振幅),那 64 根线各是各的,一根都不冗余。「一半没用」是实信号的特例,不是傅里叶的性质。 C 「速度可以取任意实数」——可以取,机器也会给你数,但那些数是前 64 个的确定组合,没有新信息。第 7 章会做一个直接的实验:把 1024 个点补零到 8192 个再变换,谱线密了 8 倍,能分辨的东西一样多。补零买到的是插值,不是分辨率。 D 「0 圈/秒那个不算」——它算。第 0 根谱线是所有样本的和,也就是信号的平均值(直流分量)。它一点也不特殊,只是那个「圈」退化成了不转而已。在图像里它是整幅图的平均亮度,是 JPEG 里最先被编码、也最舍不得扔的那个系数(第 18 章)。看清楚这一点,这本书的世界观就完整了:时域和频域是同一个 64 维空间里的两套坐标轴,互相之间转了个角度。数据没有变多也没有变少(这件事有个名字叫帕塞瓦尔定理:两边的平方和相等),变的只是哪些结构在这套轴上是稀疏的。一记敲击在时域轴上只占一个坐标,在频域轴上要占满 64 个;一个纯音正相反。第 12 章会把这句话变成一张可以对账的表。
这一章的一句话
这台机器之所以是测量而不是估计,全靠一条恒等式:均匀铺满圆周的一圈向量,加起来严格是零;而这条恒等式的三个前提被破坏时,就分别长出了混叠、泄漏和分辨率这三个后面要专门收拾的麻烦。
下一章是这本书的招牌实验,也是卷 I 的最后一章。质心是一个平面上的点,它有长度也有方向;到目前为止我们只看了长度。下一章去看那个被所有人扔掉的方向——把一张 F 的幅度谱和一张 T 的相位谱拼起来重建,出来的图和 T 有 98.93% 的格子对得上。幅度谱是所有教材封面上那张图,而图像的内容几乎全在另一半里。