5.7 幂级数、Fourier 分析与正交展开
上一节讨论函数项级数的一般理论,本节处理两类最重要的函数项级数。第一类是幂级数
正交展开的实际价值来自计算。Fourier 级数的系数可以用快速 Fourier 变换(FFT)在
5.7.1 幂级数与解析函数
收敛半径
形如
级数在
Abel 定理说明收敛集合的内部性质:幂级数在收敛区间内的任意闭子区间
逐项运算
在收敛区间内部,幂级数可以像多项式一样运算。
逐项求导:
求导后的级数半径不变(
这两条性质给出了求幂级数和函数的标准方法:把级数认成某个已知展开的导数或积分。例:
例:
逐项运算的条件
逐项求导需要 5.6 节的求导交换定理:导函数列在收敛区间内部一致收敛即满足。幂级数在内部的任意闭子区间上一致收敛,导数级数同样如此(半径相同),条件自动满足。这是幂级数比其他函数项级数好用的根本原因:把收敛半径算出来,逐项运算就都合法。端点处的行为需要单独讨论,端点上的逐项运算可能失效(
Taylor 级数与解析性
解析与任意阶可导不等价。标准例子是
这个函数在
实验一用三组系数验证 Cauchy-Hadamard 公式。
5.7.2 内积空间与正交展开
从向量到函数
第一章的内积把「夹角」与「投影」引入有限维向量空间。函数之间也可以定义内积,取
对应的范数
若
不需要解线性方程组。原因是一元二次函数
最佳均方逼近与 Bessel 不等式
设
由
左边是展开系数的能量,右边是函数本身的能量。推论:
完备性与 Parseval 等式
若对每个
Parseval 等式的读法是能量守恒:函数在原始空间的能量等于它在各个正交方向上的投影能量之和。这条等式把函数的积分(连续量)化成级数求和(离散量),在概率论中对应方差分解(第二章的方差与条件期望),在信号处理中对应功率谱。三角多项式系与多项式系(Legendre、Chebyshev 等)都是完备的正交系,各自给出一个具体的 Parseval 等式。
5.7.3 Fourier 级数
系数与基本性质
在
是标准正交系(由三角函数的正交关系
系数由投影给出:
三条基本性质决定了展开的形态。奇偶性简化:偶函数的
收敛性
Fourier 级数的收敛有三种含义,强弱不同。
均方收敛:对任何
逐点收敛与一致收敛:若
发散的存在:存在连续函数的 Fourier 级数在某点发散(du Bois-Reymond 构造),因此「连续」不足以保证逐点收敛。均方收敛则没有这个问题,这也是
Gibbs 现象
方波
实验二给出了部分和的过冲数据。
即
作为对照,三角波(连续但导数有跳跃)的系数按
均方误差与收敛速率
方波的部分和均方误差从
因此
三种收敛的关系
在有限区间上,一致收敛蕴含均方收敛(
5.7.4 离散 Fourier 变换与快速算法
DFT 的定义与性质
实际数据是有限个采样点,连续 Fourier 变换的对应物是离散 Fourier 变换(DFT)。对长度
给出(正逆变换的差别只有符号与系数
三条常用性质使 DFT 成为计算工具。卷积定理:序列的循环卷积对应频谱的逐点相乘,因此滤波运算可以在频域完成(代价从
快速 Fourier 变换
DFT 的直接计算需要
其中
实验三的耗时数据把差距量化了(时间依赖机器,趋势是稳定的):朴素 DFT 的耗时随
FFT 的实用地位来自它的复杂度:
5.7.5 正交多项式
Legendre 与 Chebyshev 多项式
在
满足
取权函数
展开系数的衰减与光滑程度
把函数按正交多项式展开,
解析函数:系数按几何速率衰减,
只有连续性(含拐角):系数按
实验四用三组数据验证这个阶梯。
Legendre 展开(用均匀网格上的最小二乘拟合近似)给出同样的阶梯:
实用结论:看到展开系数按代数速率衰减,说明函数有间断或拐角,提高截断阶数收益有限(要降低一个数量级的误差,需要提高一个数量级的阶数);看到几何衰减,说明函数解析,提高阶数的收益是指数级的。这条判据在谱方法、压缩感知与模型逼近中被用作「选择基函数与截断阶数」的依据。
5.7.6 数值实验
实验一:收敛半径的经验测定
对三组系数计算
import math
def ratios(a, k):
"""返回 (|a_k|^{1/k}, |a_{k+1}/a_k|)"""
r1 = abs(a(k)) ** (1.0 / k)
r2 = abs(a(k + 1) / a(k))
return r1, r2
series = [
("a_k = 1/k!", lambda k: 1.0 / math.factorial(k), "理论半径 无穷大"),
("a_k = 1/2^k", lambda k: 1.0 / 2.0 ** k, "理论半径 2"),
("a_k = k^k/k!", lambda k: k ** k / math.factorial(k), "理论半径 1/e = 0.367879"),
]
for name, a, theory in series:
print(f"\n{name} ({theory})")
print(f"{'k':>6} {'|a_k|^(1/k)':>16} {'|a_(k+1)/a_k|':>18}")
for k in [5, 10, 20, 50, 100]:
r1, r2 = ratios(a, k)
print(f"{k:>6} {r1:>16.6f} {r2:>18.6f}")
print("部分和检验(递推计算,避免阶乘溢出)")
print(f"{'x':>8} {'N=10':>16} {'N=50':>16} {'N=200':>16} {'e^(-x) 精确值':>16}")
for x in [-2.0, -1.0, 0.5, 1.0, 2.0]:
vals = []
for N in [10, 50, 200]:
term, s = 1.0, 1.0
for k in range(1, N + 1):
term *= (-x) / k
s += term
vals.append(s)
print(f"{x:>8.1f} {vals[0]:>16.10f} {vals[1]:>16.10f} {vals[2]:>16.10f} {math.exp(-x):>16.10f}")
print("几何级数 sum x^k 在半径 1 两侧")
for x in [0.5, 0.9, 0.99, 1.0, 1.01, 1.1]:
s = sum(x ** k for k in range(101))
exact = 1.0 / (1.0 - x) if abs(x - 1) > 1e-15 else float("inf")
print(f" x = {x:>5.2f}: 部分和(N=100) = {s:>20.8f} 1/(1-x) = {exact:>20.8f}")输出(节选主要部分):
a_k = 1/k! (理论半径 无穷大)
k |a_k|^(1/k) |a_(k+1)/a_k|
5 0.383852 0.166667
10 0.220813 0.090909
20 0.120419 0.047619
50 0.051326 0.019608
100 0.026321 0.009901
a_k = 1/2^k (理论半径 2)
k |a_k|^(1/k) |a_(k+1)/a_k|
5 0.500000 0.500000
10 0.500000 0.500000
20 0.500000 0.500000
50 0.500000 0.500000
100 0.500000 0.500000
a_k = k^k/k! (理论半径 1/e = 0.367879)
k |a_k|^(1/k) |a_(k+1)/a_k|
5 1.919260 2.488320
10 2.208125 2.593742
20 2.408373 2.653298
50 2.566306 2.691588
100 2.632085 2.704814
部分和检验(递推计算,避免阶乘溢出)
x N=10 N=50 N=200 e^(-x) 精确值
-2.0 7.3889947090 7.3890560989 7.3890560989 7.3890560989
-1.0 2.7182818011 2.7182818285 2.7182818285 2.7182818285
0.5 0.6065306597 0.6065306597 0.6065306597 0.6065306597
1.0 0.3678794643 0.3678794412 0.3678794412 0.3678794412
2.0 0.1353791887 0.1353352832 0.1353352832 0.1353352832
几何级数 sum x^k 在半径 1 两侧
x = 0.50: 部分和(N=100) = 2.00000000 1/(1-x) = 2.00000000
x = 0.90: 部分和(N=100) = 9.99976095 1/(1-x) = 10.00000000
x = 0.99: 部分和(N=100) = 63.76279821 1/(1-x) = 100.00000000
x = 1.00: 部分和(N=100) = 101.00000000 1/(1-x) = inf
x = 1.01: 部分和(N=100) = 173.18619677 1/(1-x) = -100.00000000
x = 1.10: 部分和(N=100) = 151576.73573805 1/(1-x) = -10.00000000三组系数给出三种半径的形态。第一组的两个估计量都单调趋于零(
这个量以
部分和检验一行数据说明「半径无穷」的实际含义:
几何级数的六行数据把「半径内外」的差别演示得很直接。
实验二:Fourier 级数与 Gibbs 现象
计算方波与三角波的 Fourier 部分和,观察最大过冲与均方误差的收敛。
import math
import numpy as np
xg = np.linspace(-math.pi + 1e-9, math.pi - 1e-9, 400001)
f = np.where(xg >= 0, 1.0, -1.0)
dx = xg[1] - xg[0]
print(f"{'N':>6} {'最大过冲':>12} {'过冲比例':>12} {'相对跳跃的过冲':>16} {'L2 误差':>13}")
for N in [5, 11, 51, 101, 501]:
s = np.zeros_like(xg)
for k in range(1, N + 1, 2):
s += (4.0 / math.pi) * np.sin(k * xg) / k
over = float(np.max(s))
l2 = math.sqrt(float(np.sum((s - f) ** 2) * dx))
print(f"{N:>6} {over:>12.6f} {(over - 1.0):>12.4%} {(over - 1.0) / 2.0:>16.4%} {l2:>13.6f}")
tri = np.abs(xg) - math.pi / 2
print(f"{'N':>6} {'max|S_N|':>12} {'L2 误差':>13}")
for N in [3, 7, 15, 51]:
s = np.zeros_like(xg)
for k in range(1, N + 1, 2):
s += -(4.0 / math.pi) * np.cos(k * xg) / (k * k)
l2 = math.sqrt(float(np.sum((s - tri) ** 2) * dx))
print(f"{N:>6} {float(np.max(np.abs(s))):>12.6f} {l2:>13.6f}")输出:
N 最大过冲 过冲比例 相对跳跃的过冲 L2 误差
5 1.188357 18.8357% 9.4178% 0.648568
11 1.181302 18.1302% 9.0651% 0.460148
51 1.179103 17.9103% 8.9552% 0.221315
101 1.179012 17.9012% 8.9506% 0.158052
501 1.178980 17.8980% 8.9490% 0.071333
Wilbraham-Gibbs 常数的理论极限:(2/pi) * int_0^pi sin(t)/t dt = 1.178980
理论最大过冲限值:0.178980
N max|S_N| L2 误差
3 1.414711 0.108991
7 1.491625 0.040109
15 1.531059 0.014341
51 1.558555 0.002457方波的五行数据把 Gibbs 现象的三个特征都写出来了。过冲的幅度趋于一个非零极限:
均方误差的走向与过冲相反。它按预期收敛:
三角波的四行数据是对照。
实验三:朴素 DFT 与 FFT 的耗时
实现 numpy.fft 对照,同时验证结果一致性与频谱的对称性。耗时取三次运行的最小值,减少系统负载带来的波动。
import math
import time
import numpy as np
def naive_dft(x):
N = len(x)
n = np.arange(N)
k = n.reshape((N, 1))
return np.exp(-2j * np.pi * k * n / N) @ x
def time_call(fn, *args):
t0 = time.perf_counter()
fn(*args)
return time.perf_counter() - t0
rng = np.random.default_rng(0)
print(f"{'N':>6} {'朴素 DFT (s)':>16} {'FFT (s)':>14} {'比值':>10} {'最大偏差':>14}")
for N in [256, 512, 1024, 2048, 4096]:
x = rng.normal(size=N)
tn = min(time_call(naive_dft, x) for _ in range(3))
tf = min(time_call(np.fft.fft, x) for _ in range(3))
Xn = naive_dft(x)
Xf = np.fft.fft(x)
print(f"{N:>6} {tn:>16.6f} {tf:>14.6f} {tn / max(tf, 1e-9):>10.1f} "
f"{float(np.max(np.abs(Xn - Xf))):>14.2e}")
N = 64
t = np.arange(N) * 2 * math.pi / N
sig = 1.5 * np.sin(3 * t) + 0.8 * np.cos(7 * t) + 0.3
X = np.fft.fft(sig) / N
print("非零频率分量(幅度大于 0.05 的):")
for k in range(N):
if abs(X[k]) > 0.05:
freq = k if k <= N // 2 else k - N
print(f" k = {k:>3} (频率 {freq:>3}): 幅度 = {abs(X[k]):.6f}, "
f"相位 = {math.atan2(X[k].imag, X[k].real):+.6f}")输出:
N 朴素 DFT (s) FFT (s) 比值 最大偏差
256 0.001844 0.000007 283.7 3.68e-12
512 0.007328 0.000008 939.5 7.99e-12
1024 0.027501 0.000010 2696.2 2.70e-11
2048 0.107505 0.000015 6935.8 6.71e-11
4096 0.406679 0.000029 14023.4 2.29e-10
非零频率分量(幅度大于 0.05 的):
k = 0 (频率 0): 幅度 = 0.300000, 相位 = +0.000000
k = 3 (频率 3): 幅度 = 0.750000, 相位 = -1.570796
k = 7 (频率 7): 幅度 = 0.400000, 相位 = -0.000000
k = 57 (频率 -7): 幅度 = 0.400000, 相位 = +0.000000
k = 61 (频率 -3): 幅度 = 0.750000, 相位 = +1.570796耗时数据把复杂度差别量化了。朴素 DFT 的耗时随
频谱的例子验证了实信号 DFT 的共轭对称与幅度公式。输入
实验四:正交展开的误差衰减
用 Chebyshev 部分和与 Legendre 最小二乘拟合逼近三个函数,比较误差衰减的形态。
import math
import numpy as np
def cheb_coeffs(fun, n):
"""用 Chebyshev-Gauss 节点计算 Chebyshev 展开系数"""
x = np.cos(np.pi * (np.arange(2000) + 0.5) / 2000)
w = np.pi / 2000
a = np.zeros(n + 1)
for i in range(n + 1):
a[i] = (2.0 / math.pi) * float(np.sum(fun(x) * np.cos(i * np.arccos(x)) * w))
a[0] /= 2.0
return a
xfine = np.linspace(-0.999, 0.999, 2001)
funcs = [
("e^x", np.exp),
("|x|", np.abs),
("1/(1+25x^2)", lambda x: 1.0 / (1.0 + 25.0 * x * x)),
]
for name, fun in funcs:
print(f"\nf(x) = {name},Chebyshev 部分和的最大误差")
print(f"{'n':>6} {'误差':>14}")
for n in [2, 4, 8, 16, 32, 64]:
a = cheb_coeffs(fun, n)
err = float(np.max(np.abs(np.polynomial.chebyshev.chebval(xfine, a) - fun(xfine))))
print(f"{n:>6} {err:>14.4e}")
print("Legendre 展开(均匀网格上的最小二乘拟合)")
for name, fun in funcs:
print(f" f(x) = {name}")
for deg in [4, 8, 16, 32]:
c = np.polynomial.legendre.legfit(xfine, fun(xfine), deg)
err = float(np.max(np.abs(np.polynomial.legendre.legval(xfine, c) - fun(xfine))))
print(f" 度 {deg:>3}: 最大误差 = {err:.4e}")输出:
f(x) = e^x,Chebyshev 部分和的最大误差
n 误差
2 4.9901e-02
4 5.8133e-04
8 1.1575e-08
16 1.7764e-15
32 3.5527e-15
64 1.5987e-14
f(x) = |x|,Chebyshev 部分和的最大误差
n 误差
2 2.1221e-01
4 1.2732e-01
8 7.0736e-02
16 3.7449e-02
32 1.9294e-02
64 9.7984e-03
f(x) = 1/(1+25x^2),Chebyshev 部分和的最大误差
n 误差
2 5.4027e-01
4 3.6311e-01
8 1.6401e-01
16 3.3462e-02
32 1.3929e-03
64 2.4134e-06
Legendre 展开(均匀网格上的最小二乘拟合)
f(x) = e^x
度 4: 最大误差 = 1.1947e-03
度 8: 最大误差 = 3.0510e-08
度 16: 最大误差 = 5.8342e-14
度 32: 最大误差 = 1.7392e-13
f(x) = |x|
度 4: 最大误差 = 1.1713e-01
度 8: 最大误差 = 6.7257e-02
度 16: 最大误差 = 3.6403e-02
度 32: 最大误差 = 1.8998e-02
f(x) = 1/(1+25x^2)
度 4: 最大误差 = 3.3040e-01
度 8: 最大误差 = 1.4944e-01
度 16: 最大误差 = 3.0499e-02
度 32: 最大误差 = 1.3637e-03三组数据给出三种衰减形态,与光滑程度的阶梯一一对应。
Legendre 一列给出同样的阶梯,只是常数因子不同:
5.7.7 本节小结
正交展开的三层内容
本节把幂级数与 Fourier 分析组织为三层。幂级数层给出 Cauchy-Hadamard 公式
正交展开处理的是「用振动或多项式分解函数」,下一节把微积分推广到多元。梯度、Hesse 矩阵、隐函数定理与 Lagrange 乘子构成了最优化与机器学习中大量方法的数学基础,其中矩阵求导的内容与第一章的矩阵微积分一节直接对应。
练习题
第 1 题 概念推导
(a) 求
参考答案
(a) 令
(b) 取
而
这条推导是 Parseval 等式最经典的应用,也说明 Fourier 展开可以用来计算数项级数的和。取
(c) 实解析要求 Taylor 级数在展开点的某个邻域内收敛到函数本身,这是一条关于余项的条件;任意阶可导只要求各阶导数存在。
第 2 题 计算推理
(a) 求方波
参考答案
(a)
这就是 Leibniz 级数。它是交错级数,收敛很慢(误差按
(b) 实数序列满足
(c)
第 3 题 代码验证
用本节代码做三组实验并解释:(a) 在实验二里把方波换成周期为
参考答案
(a) 过冲的极限值不变。Gibbs 现象是局部的:跳跃处附近的振荡形状由截断的高频分量决定,与跳跃的位置、幅度以及函数在别处的取值无关。改变周期只改变 Fourier 系数的具体形式(频率间隔变成整数倍的
(b) 频率
(c)
第 4 题 综合应用
某音频处理程序需要对长度
参考答案
方案一:按定义计算。低通滤波是卷积运算,直接实现需要
方案二:FFT 加频域乘法。三条运算都可以用卷积定理统一处理:滤波与互相关是卷积的形式,通过
方案三:分段处理(overlap-add 或 overlap-save)。把长序列切成若干段(每段长度
推荐:
常见错误
错误 1 · 用解析公式的和函数判断幂级数在半径外的行为
原因:解析公式
解决:使用幂级数前先算收敛半径(Cauchy-Hadamard 或比值法),并检验所用
错误 2 · 认为增大 Fourier 部分和的项数可以消除过冲
原因:Gibbs 现象的过冲幅度趋于非零极限
解决:认识到过冲的根源是间断(Fourier 级数在跳跃处不可能一致收敛),处理方式是改变展开或改变函数:加窗压低高频系数的幅度(用
错误 3 · 把循环卷积当作线性卷积
原因:DFT 的卷积定理给出的是循环卷积:
解决:计算线性卷积时把两个序列零填充到长度
错误 4 · 把展开系数的衰减速率当作与函数无关的性质
原因:系数衰减速率由函数的光滑程度决定,同一套基对不同函数的效率差别很大。实验四的 Chebyshev 展开:
解决:先判断函数的光滑程度(有几阶连续导数、有没有间断、复奇点离区间多远),再据此估计收敛速率与所需项数。实用的检验方法是计算连续几档截断误差的比值:比值稳定在