简单回顾
快速傅里叶变换,其实是离散傅里叶变换的一种改进算法
我们先来回忆一下离散傅里叶变换(DFT):
Ff[m]‾=∑n=0N−1f[n]‾ω[m]‾−n(1)\underline{\mathscr{F}f[m]} = \sum_{n=0}^{N-1} \underline{f[n]} \underline{\omega[m]}^{-n} \tag1Ff[m]=n=0∑N−1f[n]ω[m]−n(1)
其中 ω[m]‾=e2πimN\underline{\omega[m]} = e^{2\pi i\frac{m}{N}}ω[m]=e2πiNm
对应的矩阵形式为:
[Ff[0]‾Ff[1]‾⋮Ff[N−1]‾]=[11⋯11ω‾−1⋅1⋯ω‾−1⋅(N−1)⋮⋮⋱⋮1ω‾−(N−1)⋅1⋯ω‾−(N−1)2][f[0]‾f[1]‾⋮f[N−1]‾]\begin{bmatrix} \underline{\mathscr{F}f[0]}\\ \underline{\mathscr{F}f[1]}\\ \vdots\\ \underline{\mathscr{F}f[N-1]} \end{bmatrix} = \begin{bmatrix} 1 & 1 & \cdots & 1 \\ 1 & \underline{\omega}^{-1 \cdot 1} & \cdots & \underline{\omega}^{- 1 \cdot (N-1)} \\ \vdots & \vdots & \ddots & \vdots \\ 1 & \underline{\omega}^{-(N-1) \cdot 1} & \cdots & \underline{\omega}^{-(N-1)^2} \end{bmatrix} \begin{bmatrix} \underline{f[0] } \\ \underline{f[1]} \\ \vdots\\ \underline{f[N-1]} \end{bmatrix}⎣⎢⎢⎢⎡Ff[0]Ff[1]⋮Ff[N−1]⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡11⋮11ω−1⋅1⋮ω−(N−1)⋅1⋯⋯⋱⋯1ω−1⋅(N−1)⋮ω−(N−1)2⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡f[0]f[1]⋮f[N−1]⎦⎥⎥⎥⎤
不难发现,如果要对 NNN 个采样点进行DFT,则时间复杂度为 o(N2)o \left( N^2\right)o(N2)
接下来,我们将讲解时间复杂度为 o(NlogN)o \left( N \log N \right)o(NlogN) 的FFT算法
为了书写方便,我们记
ω‾[p,q]=e2πiqp(2)\underline{\omega}[p, q] = e^{2\pi i \frac{q}{p}} \tag 2ω[p,q]=e2πipq(2)
这样,ω[m]‾\underline{\omega[m]}ω[m]就可以改写为
ω[m]‾=e2πimN=ω‾[N,m]\underline{\omega[m]} = e^{2\pi i \frac{m}{N}} = \underline{\omega}[N, m]ω[m]=e2πiNm=ω[N,m]
同时,不难发现,
ω‾[N,n+m]=e2πin+mN=e2πinNe2πimN=ω‾[N,n]ω‾[N,m]\underline{\omega}[N, n+m] = e^{2\pi i \frac{n+m}{N}} = e^{2\pi i \frac{n}{N}}e^{2\pi i \frac{m}{N}} = \underline{\omega}[N, n]\underline{\omega}[N, m]ω[N,n+m]=e2πiNn+m=e2πiNne2πiNm=ω[N,n]ω[N,m]
ω‾[N2,−nm]=e−2πinmN2=e−2πi2nmN=ω‾[N,−2nm]\underline{\omega}[\frac{N}{2}, -nm] = e^{-2\pi i \frac{nm}{\frac{N}{2}}} = e^{-2\pi i \frac{2nm}{N}} = \underline{\omega}[N, -2nm]ω[2N,−nm]=e−2πi2Nnm=e−2πiN2nm=ω[N,−2nm]
FFT推导
FFT的核心思想就是:将DFT的N×NN \times NN×N矩阵该写为对几个N2×N2\frac{N}{2} \times \frac{N}{2}2N×2N的矩阵的操作,并用这种方式进行递归,最后得到FFT
首先,我们将(1)(1)(1)式拆分为奇数、偶数两部分,即
Ff[m]‾=∑n=0N−1f[n]‾ω[m]‾−n=(f[0]‾ω[m]‾−0+f[2]‾ω[m]‾−2+⋯+f[N−2]‾ω[m]‾−(N−2))+(f[1]‾ω[m]‾−1+f[3]‾ω[m]‾−3+⋯+f[N−1]‾ω[m]‾−(N−1))=∑n=0N2−1f[2n]‾ω[m]‾−2n+∑n=0N2−1f[2n+1]‾ω[m]‾−(2n+1)\begin{aligned} \underline{\mathscr{F}f[m]} &= \sum_{n=0}^{N-1} \underline{f[n]} \underline{\omega[m]}^{-n}\\ & = \left( \underline{f[0]} \underline{\omega[m]}^{-0} + \underline{f[2]} \underline{\omega[m]}^{-2} + \dots + \underline{f[N-2]} \underline{\omega[m]}^{-\left(N-2 \right)} \right) + \left( \underline{f[1]} \underline{\omega[m]}^{-1} + \underline{f[3]} \underline{\omega[m]}^{-3} + \dots + \underline{f[N-1]} \underline{\omega[m]}^{-\left(N-1 \right)} \right)\\ &=\sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega[m]}^{-2n} + \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega[m]}^{-(2n+1)} \end{aligned}Ff[m]=n=0∑N−1f[n]ω[m]−n=(f[0]ω[m]−0+f[2]ω[m]−2+⋯+f[N−2]ω[m]−(N−2))+(f[1]ω[m]−1+f[3]ω[m]−3+⋯+f[N−1]ω[m]−(N−1))=n=0∑2N−1f[2n]ω[m]−2n+n=0∑2N−1f[2n+1]ω[m]−(2n+1)
利用 (2)(2)(2) 式,可将上式改写为
Ff[m]‾=∑n=0N2−1f[2n]‾ω‾[N,−2nm]+∑n=0N2−1f[2n+1]‾ω‾[N,−(2n+1)m]=∑n=0N2−1f[2n]‾ω‾[N,−2nm]+∑n=0N2−1f[2n+1]‾ω‾[N,−2nm]ω‾[N,−m]=∑n=0N2−1f[2n]‾ω‾[N2,−nm]+ω‾[N,−m]∑n=0N2−1f[2n+1]‾ω‾[N2,−nm](3)\begin{aligned} \underline{\mathscr{F}f[m]} &=\sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega}[N, -2nm] + \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega}[N, -(2n+1)m]\\ &=\sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega}[N, -2nm] + \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega}[N, -2nm]\underline{\omega}[N, -m]\\ &=\sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega}[\frac{N}{2}, -nm] + \underline{\omega}[N, -m] \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega}[\frac{N}{2}, -nm]\tag 3 \end{aligned}Ff[m]=n=0∑2N−1f[2n]ω[N,−2nm]+n=0∑2N−1f[2n+1]ω[N,−(2n+1)m]=n=0∑2N−1f[2n]ω[N,−2nm]+n=0∑2N−1f[2n+1]ω[N,−2nm]ω[N,−m]=n=0∑2N−1f[2n]ω[2N,−nm]+ω[N,−m]n=0∑2N−1f[2n+1]ω[2N,−nm](3)
注意我们要使用递归来计算的话,必须把式子写成矩阵形式,那么(3)(3)(3)式就可以写为:
F‾Nf[m]‾=FN2feven[m]‾+ω‾[N,−m]FN2fodd[m]‾(4)\underline\mathscr{F}_N \underline{f[m]} = \underline{\mathscr{F}_{\frac{N}{2}}f_\text{even}[m]} + \underline{\omega}[N, -m]\underline{\mathscr{F}_{\frac{N}{2}}f_\text{odd}[m]} \tag4FNf[m]=F2Nfeven[m]+ω[N,−m]F2Nfodd[m](4)
我们仔细看看从 (3)(3)(3) 式的求和形式到 (4)(4)(4) 的矩阵矩阵形式,(3)(3)(3) 式完全是没有问题的,但是 (4)(4)(4) 式却有问题: (4)(4)(4) 式中的 F‾N2\underline\mathscr{F}_{\frac{N}{2}}F2N 是 N2×N2\frac{N}{2} \times \frac{N}{2}2N×2N 的矩阵,那么与之相乘的矩阵必然有 N2\frac{N}{2}2N 行,也就是说 m≤N2−1m \leq \frac{N}{2}-1m≤2N−1。换句话说,(4)(4)(4) 式计算出的其实是 F‾N\underline\mathscr{F}_NFN 的前半部分。
那么对于 m∈[N2,N−1]m \in [\frac{N}{2}, N-1]m∈[2N,N−1] 的情况,应该如何写出相应的矩阵形式呢?我们将 (3)(3)(3) 式左边的系数写成 m+N2m+\frac{N}{2}m+2N,这样一来,mmm 的取值依然是 [0,N2−1][0, \frac{N}{2}-1][0,2N−1]。于是就有:
Ff[m+N2]‾=∑n=0N2−1f[2n]‾ω‾[N2,−n(m+N2)]+ω‾[N,−(m+N2)]∑n=0N2−1f[2n+1]‾ω‾[N2,−n(m+N2)]=∑n=0N2−1f[2n]‾ω‾[N2,−nm]ω‾[N2,−nN2]+ω‾[N,−m]ω‾[N,−N2]∑n=0N2−1f[2n+1]‾ω‾[N2,−nm]ω‾[N2,−nN2](5)\begin{aligned} \underline{\mathscr{F}f[m+\frac{N}{2}]} & = \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega}[\frac{N}{2}, -n \left(m+\frac{N}{2}\right) ] \\ & +\underline{\omega}[N, -\left(m+\frac{N}{2}\right)] \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega}[\frac{N}{2}, -n\left(m+\frac{N}{2}\right)] \\ & = \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega}[\frac{N}{2}, -nm ] \underline{\omega}[\frac{N}{2}, -n \frac{N}{2}]\\ &+\underline{\omega}[N, -m] \underline{\omega}[N, -\frac{N}{2}] \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega}[\frac{N}{2}, -nm] \underline{\omega}[\frac{N}{2}, -n\frac{N}{2}] \tag5 \end{aligned}Ff[m+2N]=n=0∑2N−1f[2n]ω[2N,−n(m+2N)]+ω[N,−(m+2N)]n=0∑2N−1f[2n+1]ω[2N,−n(m+2N)]=n=0∑2N−1f[2n]ω[2N,−nm]ω[2N,−n2N]+ω[N,−m]ω[N,−2N]n=0∑2N−1f[2n+1]ω[2N,−nm]ω[2N,−n2N](5)
注意到,
ω‾[N2,−nN2]=e−2πin=1\underline{\omega}[\frac{N}{2}, -n\frac{N}{2}] = e^{-2\pi in} = 1ω[2N,−n2N]=e−2πin=1
ω‾[N,−N2]=e−πi=−1\underline{\omega}[N, -\frac{N}{2}] = e^{-\pi i} = -1ω[N,−2N]=e−πi=−1
带入 (5)(5)(5) 式,有:
Ff[m+N2]‾=∑n=0N2−1f[2n]‾ω‾[N2,−nm]−ω‾[N,−m]∑n=0N2−1f[2n+1]‾ω‾[N2,−nm]\underline{\mathscr{F}f[m+\frac{N}{2}]} = \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n]} \underline{\omega}[\frac{N}{2}, -nm ] - \underline{\omega}[N, -m] \sum_{n=0}^{\frac{N}{2}-1} \underline{f[2n+1]} \underline{\omega}[\frac{N}{2}, -nm]Ff[m+2N]=n=0∑2N−1f[2n]ω[2N,−nm]−ω[N,−m]n=0∑2N−1f[2n+1]ω[2N,−nm]
写成矩阵的形式,即
F‾Nf[m+N2]‾=FN2feven[m]‾−ω‾[N,−m]FN2fodd[m]‾(6)\underline\mathscr{F}_N \underline{f[m+\frac{N}{2}]} = \underline{\mathscr{F}_{\frac{N}{2}}f_{even}[m]} - \underline{\omega}[N, -m]\underline{\mathscr{F}_{\frac{N}{2}}f_{odd}[m]} \tag6FNf[m+2N]=F2Nfeven[m]−ω[N,−m]F2Nfodd[m](6)
结合 (4)(4)(4),(6)(6)(6) 两式,我们可以得到FFT的矩阵递归式:
F‾Nf[m]‾=FN2feven[m]‾+ω‾[N,−m]FN2fodd[m]‾\huge\underline\mathscr{F}_N \underline{f[m]} = \underline{\mathscr{F}_{\frac{N}{2}}f_\text{even}[m]} + \underline{\omega}[N, -m]\underline{\mathscr{F}_{\frac{N}{2}}f_\text{odd}[m]}FNf[m]=F2Nfeven[m]+ω[N,−m]F2Nfodd[m]
F‾Nf[m+N2]‾=FN2feven[m]‾−ω‾[N,−m]FN2fodd[m]‾\huge\underline\mathscr{F}_N \underline{f[m+\frac{N}{2}]} = \underline{\mathscr{F}_{\frac{N}{2}}f_{even}[m]} - \underline{\omega}[N, -m]\underline{\mathscr{F}_{\frac{N}{2}}f_\text{odd}[m]}FNf[m+2N]=F2Nfeven[m]−ω[N,−m]F2Nfodd[m]
其中 m∈[0,N2−1]m\in [0, \frac{N}{2}-1]m∈[0,2N−1],第一个等式为前 N2\frac{N}{2}2N 项,第二个等式为后 N2\frac{N}{2}2N 项
这个式子很明确地告诉我们:
FFT实际上就是将DFT的矩阵,通过一定的方式进行拆分,从而降低时间复杂度的一种算法
FFT的时间复杂度
有了递归式,我们就可以来算时间复杂度。
方便起见,我们将矩阵写成 [n×m]\left[ n \times m \right][n×m],其中 nnn,mmm 分别表示行列数。并设 N=2kN = 2^kN=2k
- 那么前 N2\frac{N}{2}2N 项就可以写成
[N2×N2]+CN[N2×N2]\left[ \frac{N}{2} \times \frac{N}{2} \right] + C_N \left[ \frac{N}{2} \times \frac{N}{2} \right][2N×2N]+CN[2N×2N]
其中 CNC_NCN 为一常数,该式花费的时间为 o([N2×N2])+1+o([N2×N2])o\left( \left[ \frac{N}{2} \times \frac{N}{2} \right] \right) + 1 + o\left( \left[ \frac{N}{2} \times \frac{N}{2} \right] \right)o([2N×2N])+1+o([2N×2N]),其中 111 就是常数相乘的操作
对于后 N2\frac{N}{2}2N 项,由于对应的矩阵、常数都是相同的,因此只需要额外进行 111 次操作即可
- 我们把 n×nn \times nn×n 的矩阵记为 [n][n][n],并把 mmm 个不同的常数相乘记为 CmC^mCm,则如下图
注意图中 1+2⋯+2k−11+2 \dots + 2^{k-1}1+2⋯+2k−1 是前文提到的后 N2\frac{N}{2}2N 项的计算量,需要进行累加计算。到最后一项时,它的累积值为 2n−12^n-12n−1
不难发现,递归至最后一项时,总计算次数应为(图中最后一行以及虚线框内):
0⋅(0k)+1⋅(1k)+⋯+k⋅(kk)+(2k)+(2k−1)+(2k)(7)0\cdot\binom{0}{k} + 1\cdot\binom{1}{k} + \dots + k\cdot \binom{k}{k} + (2^k) + (2^k-1) +(2^k)\tag70⋅(k0)+1⋅(k1)+⋯+k⋅(kk)+(2k)+(2k−1)+(2k)(7)
简单解释一下上式
- 前面的各项排列组合表示的是各个常数 CxC^xCx 所需的计算时间,如系数 CkC^kCk 只在最后一个一阶矩阵前出现,计算量为 k⋅(kk)=kk\cdot \binom{k}{k}=kk⋅(kk)=k 次,又如系数 C1C^1C1 必然会出现 (1k)\binom{1}{k}(k1) 次,因此计算量为 1⋅(1k)=k1\cdot \binom{1}{k}=k1⋅(k1)=k;
- 第一个 2n2^n2n 表示的是所有 111 阶矩阵的计算时间总和;
- 2n−12^n-12n−1 表示的是后 N2\frac{N}{2}2N 项的计算量;
- 第二个 2n2^n2n 表示的是计算所有加号花费的时间
(7)(7)(7) 式不容易计算,但是我们可以直观地估计一下,对于前面的阶乘项,最多用时为 kkk 次,最少为 111 次,那么不严格地估计一下,我们取平均 k+12\frac{k+1}{2}2k+1 次,这样共需要计算 NNN 次(有 NNN 个 [1][1][1]),因此时间复杂度应为:
o((Nk2)+3N−1)=o(12Nlog2N+3N−1)=o(Nlog2N)o\left( \left( N\frac{k}{2} \right) +3N-1 \right) = o\left( \frac{1}{2} N \log _2 N +3N-1 \right) = o\left( N \log _2 N \right) o((N2k)+3N−1)=o(21Nlog2N+3N−1)=o(Nlog2N)
因此,FFT的时间复杂度就是
o(NlgN) o\left( N \lg N \right) o(NlgN)