卷 I · 转起来CH 03深度 3/20

凭什么拆得开

上一章那台机器有一个还没堵上的漏洞:缠错速度的时候,那些「往外推」的力气真的会严格抵消吗?只要不严格,3 Hz 的答案里就会渗进一点 5 Hz,5 Hz 的答案里也会渗进一点 3 Hz,整台机器立刻从「测量」退化成「估计」。这一章堵这个漏洞,用的是全书唯一一条需要动笔的恒等式。

2016 对,最大 1.6e−13正交性为什么便宜

▷ 先猜一下

你手里有 64 个采样点,一秒钟采完。上一章那台机器可以用任何速度去缠——1 圈/秒、2 圈/秒、3.7 圈/秒、100 圈/秒,都能算出一个质心。

问:从这 64 个数里,你最多能问出多少个互相独立的频率?(「独立」的意思是:知道了前面那些的答案,也推不出这一个的答案。)

A 32 个。因为一半是负频率,是另一半的镜像,没有新信息 B 64 个。跟数据点数一样多,一个不多一个不少 C 无限多。速度可以取任意实数,想问多少个就问多少个 D 63 个。0 圈/秒那个不算,它只是所有数的和

漏洞在哪

把上一章的动作写清楚一点。假设信号里同时有 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⁻¹³ 不是数学的锅,是 cossin 在 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 章会给出唯一的应对手段(加窗),以及那个手段要付的价钱。判据是:看到一个漂亮的频谱,先问「我这一段的长度,是不是刚好被那个频率整除」——如果不是,你看到的形状里有一部分是你自己造出来的。

◇ 揭晓

正确答案是 B64 个。你能无限多个,但只有 64 个答案是独立的。

A 「一半是负频率的镜像」——这句话本身完全正确,但前提是信号是实数。实信号的谱确实共轭对称,64 个复数里只有 64 个独立的实数(不是 128 个)。可如果你的信号本来就是复数(无线电里的 I/Q 采样、光学里的复振幅、量子态的振幅),那 64 根线各是各的,一根都不冗余。「一半没用」是实信号的特例,不是傅里叶的性质。 C 「速度可以取任意实数」——可以取,机器也会给你数,但那些数是前 64 个的确定组合,没有新信息。第 7 章会做一个直接的实验:把 1024 个点补零到 8192 个再变换,谱线密了 8 倍,能分辨的东西一样多。补零买到的是插值,不是分辨率。 D 「0 圈/秒那个不算」——它算。第 0 根谱线是所有样本的和,也就是信号的平均值(直流分量)。它一点也不特殊,只是那个「圈」退化成了不转而已。在图像里它是整幅图的平均亮度,是 JPEG 里最先被编码、也最舍不得扔的那个系数(第 18 章)。
⟳ 换个域看
64 个采样值,就是 64 个数——它们本来就是「一组坐标」:第 0 个坐标是「时刻 0 处的值」,第 1 个坐标是「时刻 1 处的值」……这一组基向量当然也是两两垂直的。 64 根谱线,也是 64 个坐标,用的是另一组两两垂直的基向量:各种转速的圈。

看清楚这一点,这本书的世界观就完整了:时域和频域是同一个 64 维空间里的两套坐标轴,互相之间转了个角度。数据没有变多也没有变少(这件事有个名字叫帕塞瓦尔定理:两边的平方和相等),变的只是哪些结构在这套轴上是稀疏的。一记敲击在时域轴上只占一个坐标,在频域轴上要占满 64 个;一个纯音正相反。第 12 章会把这句话变成一张可以对账的表。

这一章的一句话

这台机器之所以是测量而不是估计,全靠一条恒等式:均匀铺满圆周的一圈向量,加起来严格是零;而这条恒等式的三个前提被破坏时,就分别长出了混叠、泄漏和分辨率这三个后面要专门收拾的麻烦。

下一章是这本书的招牌实验,也是卷 I 的最后一章。质心是一个平面上的点,它有长度也有方向;到目前为止我们只看了长度。下一章去看那个被所有人扔掉的方向——把一张 F 的幅度谱和一张 T 的相位谱拼起来重建,出来的图和 T 有 98.93% 的格子对得上。幅度谱是所有教材封面上那张图,而图像的内容几乎全在另一半里。