卷 V · 算得动CH 20深度 20/20

你手里那台机器

十九章下来,你手里只有一台机器:把一段东西缠到圆上,看质心跑到哪。这一章把前面所有的账单收成一张自查表,给出往下走的岔路口,然后回到第一张图——你现在知道那根针指的是什么了。

二十条自查表继续往下走回到第一张图

▷ 先猜一下

最后一次。回到第 2 章那段信号:一秒钟,3 Hz 振幅 1.0 + 5 Hz 振幅 0.5,256 Hz 采样。

这一次用 5 圈/秒去缠。问:质心离圆心多远?

A 0.5000。5 Hz 确实在信号里,所以和 3 Hz 那次一样 B 0.2500 C 大约 0(10⁻¹⁷ 量级)。5 圈/秒不是主成分 D 0.2315。这是第 2 章出现过的那个数

二十条自查表

前十九章每一章都有一个「✗ 这个直觉是错的」。全部收在这里,加上最后一条。下次你在频谱前面卡住的时候,从上往下过一遍这张表,大概率能找到卡在哪。

#听起来很对的那句话实际情况
1频率就是音高频率是每单位横轴转几圈。横轴是像素的时候,它和听觉毫无关系
2谱线有多高,正弦就有多大振幅差一个因子,取决于有没有除 N、信号是实是复。先拿一个已知振幅的信号标定一遍
3频率不同就互不干扰只有整数圈才严格正交。3 与 3.5 的内积是 0.0156,不是 0
4幅度谱就是信号的特征,相位可以扔换脸实验:相位以 98.93% 对 81.54% 赢下整张图
5两个采样点之间的信息丢了,只能猜带限时那是,不是猜。实测重建误差 3.86 × 10⁻¹³
6采样率低点没事,事后滤掉假高频折回来的东西就低频区。1400 Hz 与 400 Hz 采出来逐点差 2.39 × 10⁻¹²
7补零能提高分辨率补零是插值不是显微镜。分辨率只由观测时长决定,Δf = 1/T
8频谱上的小尖尖是噪声,多平均几次就没了泄漏是确定性的,平均一万次一模一样。判据:再采一段,位置变不变
9严谨就该用原始数据,不加窗「不加窗」等于选了矩形窗——旁瓣最高、扇贝损失最大的那一个
10测不准原理是因为测量会干扰系统它是傅里叶的性质。一台不做任何测量的计算机就能算出这条限制
11频谱图糊就调高重叠率重叠改变列数,不改变每列有多糊。和补零是同一个错的两种穿法
12小波比傅里叶先进,绕过了不确定性一点没绕过,只是重排了像素形状。而且它没有卷积定理
13卷积是信号处理的一个技巧它是「线性 + 平移不变」的唯一形状。CNN 里那个还是互相关(不翻转)
14既然频域快 189 倍,卷积都该走频域核短(3×3、5×5)时直接卷压倒性地快。交叉点大约在核长 30–100
15滤波器把某些频率删掉了,阶数越高越好阻带不是 0 是 −35 dB。加阶数买的是过渡带,不是平通带
16信噪比小于 1 就没救了知道信号长什么样,就还有 10·log₁₀(N) 可以拿。GPS 靠这个活着
17FFT 是 DFT 的快速近似,精度差一点同一个数学量,而且浮点上通常更准(累加深度从 N 降到 log N)
18DCT 是比 DFT 更先进的变换DCT 就是「先镜像再 DFT」。优势全部来自边界怎么接
19样本够多,什么都会变成正态要求方差存在,而且是「相加」出来的。收入是乘出来的,所以不是
20换到频域总是更好换域是一次赌博:赌你的数据在新坐标系里更整齐。第 12 章那个阶跃信号就赌输了

这二十条里,有八条的根子是同一件事:边界。泄漏(8)、窗(9)、循环卷积的绕回(14)、吉布斯(15)、DCT 的镜像(18)——全都发生在数据的两端。如果只让我给一条经验:处理任何有限长的数据时,先想清楚它的两端接给了谁。

继续往下走

如果你想把这本书补严

  • 《The Scientist and Engineer's Guide to Digital Signal Processing》(Steven Smith)——全书免费在线。工程口味,图多公式少,和这本书的角度最接近,可以当作直接的下一本。
  • 《Understanding Digital Signal Processing》(Richard Lyons)——工程师圈子里公认最好读的那本 DSP 教材,第 3 版。这本书里所有「工程细节」(滤波器设计、多速率、抽取插值)它都讲透了。
  • 《Discrete-Time Signal Processing》(Oppenheim & Schafer)——领域的标准教科书,严格版。第 4 章那个换脸实验的原始出处(Oppenheim & Lim, The Importance of Phase in Signals, 1981)也在这条线上。
  • 3Blue1Brown 的《But what is the Fourier Transform?》——第 2 章那个「缠绕」直觉的动画版,二十分钟。如果那一章你看得不够踏实,去看这个。

如果你想动手做东西

  • 写一个调音器。麦克风采一段,找峰,换算成音名。三十行以内。做完你会立刻撞上第 7 章(要听多久)和第 8 章(峰值不准),然后自然地需要第 9 章。这是最好的第一个项目。
  • 做一个卷积混响。找一个免费的脉冲响应(教堂、洞穴、弹簧),用第 14 章的重叠相加做成实时的。你会撞上循环卷积的绕回。
  • 自己实现一遍 JPEG。读一张 PNG,切块、DCT、量化、之字形扫描、游程 + 霍夫曼。第 18 章那块方块可以当单元测试。做完你对「压缩」这件事的理解会完全不一样。
  • 从零写一个 FFT。第 17 章那十二行是起点。然后试着优化:迭代版、原地、预计算旋转因子、实数输入的一半优化。这是少见的「算法书上的东西真的能跑快十倍」的场合。
  • 用 GNU Radio 收一次真实信号。一个几十块钱的 RTL-SDR 电视棒就能收 FM 广播、飞机的 ADS-B、气象卫星。这本书里的每一个概念,在那里都是可见的、会咬人的。

如果你想往数学深处走

  • 调和分析:从傅里叶级数的收敛性开始(吉布斯现象在这里有严格的处理),到 Littlewood–Paley 理论、Calderón–Zygmund 算子。Stein 和 Shakarchi 的《Fourier Analysis》是最友好的入口。
  • 抽象调和分析:第 19 章那个「有平移就有傅里叶」的严格版本,叫 Pontryagin 对偶。非交换群上的版本就是表示论。这条路直通 Langlands 纲领,那是当代数学最大的工程之一。
  • 小波与稀疏表示:Mallat 的《A Wavelet Tour of Signal Processing》。往前一步就是压缩感知(Candès、Donoho、Tao),那是 2000 年代最漂亮的结果之一。
  • 信息论的接口:采样定理和信道容量之间有一条直接的线(都是香农的工作)。书架上的《意外》讲的是另一半。

还有争议的地方

这本书为了讲清楚,有几处说得比学界的共识更斩钉截铁,值得标出来:

  • 「相位比幅度重要」这个说法要限定场合。第 4 章那个实验在图像上极有说服力,在听觉上则相反——人耳对单个成分的绝对相位极不敏感,这正是 MP3 敢省相位精度的依据。「哪一半更重要」取决于下游是谁在看。
  • 「采样定理说 fs > 2B」是经典表述,不是全部。如果你事先知道信号是稀疏的(只有少数几个频率成分,但不知道是哪几个),压缩感知说你可以用远低于 2B 的采样率恢复它。这不违反定理——它换了一个前提。2B 是「对所有带限信号都成立」的下界,不是「对我这个信号」的下界。
  • 「频域方法在深度学习里过时了」是一个流行但站不住的说法。CNN 确实基本不用 FFT(核太小),但傅里叶神经算子(FNO)在偏微分方程上很成功,位置编码里的正弦基、扩散模型里的频域分析、以及所有的音频模型,全都在用。更准确的说法是:它从「加速手段」变回了「建模语言」。
  • 那条「n ≥ 30」的经验法则不该被当成定理教。第 19 章给了实测:对称有界的分布 n = 2 就很像了,重尾的分布三万个都不够。把一条为最坏情况准备的保守建议讲成普适规律,是统计教学里流传最广的一个问题。
⌨ 自己跑一遍

最后一次,也是最短的一次。把这本书浓缩成一个函数:

import math

def wind(x, fs, f):
    """把 x 以 f 圈/秒缠到圆上,返回质心。这就是整本书。"""
    sr = si = 0.0
    for n, v in enumerate(x):
        a = -2 * math.pi * f * n / fs
        sr += v * math.cos(a)
        si += v * math.sin(a)
    return sr / len(x), si / len(x)


fs = 256
x = [math.sin(2*math.pi*3*n/fs) + 0.5*math.sin(2*math.pi*5*n/fs)
     for n in range(fs)]

for f in (3, 4, 5):
    re, im = wind(x, fs, f)
    print(f, '|c| = %.4f   ∠c = %.3f 弧度' % (math.hypot(re, im), math.atan2(im, re)))
# 3 |c| = 0.5000   4 |c| = 0.0000   5 |c| = 0.2500

十四行,零依赖,和 numpy.fft.fft 算出来的东西差 5.6 × 10⁻¹⁶。剩下的十九章,全部是这十四行的直觉、它的账单,和让它跑得动的那个算法。

python3 -c " import math fs = 256 x = [math.sin(2*math.pi*3*n/fs) + 0.5*math.sin(2*math.pi*5*n/fs) for n in range(fs)] for f in (3, 4, 5): re = sum(x[n]*math.cos(-2*math.pi*f*n/fs) for n in range(fs)) / fs im = sum(x[n]*math.sin(-2*math.pi*f*n/fs) for n in range(fs)) / fs print(f, '%.4f' % math.hypot(re, im)) "

把这十四行存下来。以后遇到任何一份数据,先拿它扫一遍——扫之前记得回头看一眼上面那张二十条的表。

▸ 在现实里

最后一次「在现实里」,换个问法:读完这本书,你在什么场合会想起它?

  • 看到任何一条随时间变化的曲线——服务器指标、心率、股价、传感器读数——你会开始问「它里面有没有周期性的东西,而我的采样间隔和那个周期是什么关系」。
  • 看到任何一张频谱图,你会先问三句:窗多长、加了什么窗、这些小尖尖是真的吗。
  • 遇到任何一个又慢又拧巴的加权求和,你会问它是不是卷积;是的话,频域那边就有一条现成的高速路。
  • 听到「测不准」「不确定性」「时频分析」「相位」这些词,你不会再觉得它们属于别人。
  • 而最实用的那一条:下次你想「多采一点数据就能看清楚了」的时候,你会先问一句——我缺的是采样率,还是观测时长?这两个旋钮解决的是完全不同的问题,而搞混它们是这个领域最常见的一次错误。
✗ 这个直觉是错的

「学完了。以后遇到频域问题,直接调库就行。」

调库当然是对的——你不应该自己写 FFT(除非为了学)。问题在于,这本书里那二十条错误直觉,没有一条是调库能避开的

numpy.fft.fft 不会告诉你窗口太短所以两个音分不开,不会告诉你那些小尖尖是泄漏不是噪声,不会告诉你采样率不够所以你看到的 400 Hz 其实是 1400 Hz,更不会告诉你补零买不到分辨率。它会给你一个非常干净、非常可信、而且完全误导人的数组。

这本书的价值不在于让你能写出那十四行,而在于让你在看到那个数组时知道该怀疑什么判据是:你能不能对着一张频谱说出「这张图里有哪些东西是我自己造出来的」。能,这本书就没白读。

◇ 揭晓

正确答案是 B0.2500

A 「5 Hz 在信号里,所以和 3 Hz 一样」——在,但它的振幅是 0.5,不是 1.0。而第 2 章说过,实正弦的能量平分给 ±f 两个转速,所以质心是振幅的一半:0.5 / 2 = 0.2500如果你答了这一条,回去看第 2 章「✗ 这个直觉是错的」那一栏。 C 「不是主成分所以约等于 0」——这台机器不分主次。它对每一个转速给出的是精确的答案,而不是「显著的才报」。10⁻¹⁷ 只出现在信号里真的没有那个频率的时候。 D 「0.2315」——这是第 2 章里用 3.5 圈/秒缠出来的那个数,也就是泄漏(第 8 章)。它长得和 0.2500 很像,这本身就值得警惕:一个真实的分量和一次泄漏,在频谱上可以长得几乎一样。分辨它们的办法是换一个窗口长度再看一次——真实分量的位置不动,泄漏会变。
⟳ 换个域看
时域里,这本书是二十个 HTML 文件,几万个汉字,读了大概几个小时。 频域里,它是一句话:把东西缠到圆上,看质心跑到哪。剩下的全是这句话的账单、快法,和它在别处的样子。

这一栏出现了二十次,每次做的都是同一件事:把刚讲完的东西搬到另一个坐标系里,看看它在那边是不是更简单。如果这个动作已经变成了你的下意识——看到一个难算的东西,先问「有没有另一个地方,它在那里是乘法」——那这本书的目的就达到了。

因为说到底,傅里叶变换教给我们的不是频率。是「换一个坐标系,同一个问题可以从很难变成很简单」这件事本身,恰好在一个可以精确计算、可以画出来、可以在本机跑一遍的例子上,被完整地演示了一次。

这一章的一句话

你手里那台机器只有十四行;这本书真正给你的,是看到一张频谱时知道该怀疑什么,以及看到一个难算的东西时会先问一句——有没有另一个地方,它在那里是乘法。

回到第一张图。那是 3 Hz 加半个 5 Hz,缠了三圈,质心跑出去 0.5000。第 1 章的时候它只是一张漂亮的图;现在你知道那根针为什么会指在那儿,也知道如果记录长度不是整一秒会发生什么,还知道如果采样率低于 10 Hz 你会看到一个根本不存在的东西。

读完了。