当前位置: 首页 > news >正文

9.3 快速傅里叶变换

一、概述

线性代数的很多应用都很难在短时间内介绍清楚,一般都会在完善理论和新增的应用之间做权衡。通常是理论介绍比较多,但是这一节不同,本节介绍的是 202020 世纪最有价值的数值计算算法。
在用傅里叶矩阵 F\pmb FF 和其逆矩阵 F−1\pmb{F^{-1}}F1 左乘其它矩阵或向量时,我们希望能更快的计算快速傅里叶变换(FFT:Fast Fourier Transform) 可以实现这一点。通常情况下计算乘积 FcF\boldsymbol cFc 需要 n2n^2n2 次乘法,这是因为 nnn 阶傅里叶矩阵 FFFn2n^2n2 个元素,而 FFT 只需要 12nlog⁡2n\dfrac{1}{2}n\log_2n21nlog2n 次乘法。后面会详细介绍。
FFT 彻底改变了信号处理领域,整个产业的发展也被这一思想加速推进。电气工程师首先感受到了这种变换 —— 只要碰到函数,它们就会进行傅里叶变换。傅里叶变换是将 fff 表示为谐波函数 ckeikxc_ke^{ikx}ckeikx 和的形式。函数 fff 通过系数 ckc_kck 被看成频率空间中的元素,而不是物理空间中的函数值 f(x)f(x)f(x). 联系系数 ccc 与函数 fff 之间的桥梁就是傅里叶变换,而 FFT 就是这种计算的快速算法。

二、单位根和傅里叶矩阵

二次方程有两个根(或是一个二重根),nnn 次方程有 nnn 个根(计算重数),这是代数的基本定理,为了使得该定理成立,我们允许有复根。这一节关注的是一个特殊的方程 zn=1z^n=1zn=1,它的解 nnn 是 “nnn 次单位根(nnnth roots of unity)”,在复平面上对应沿着单位圆均匀分布的 nnn 个点。
Figure 9.4 显示了 z8=1z^8=1z8=1 的八个根,它们的间隔是 18(360°)=45°\dfrac{1}{8}(360°)=45°81(360°)=45°. 第一个根位于 45°45°45°θ=2π8\theta=\dfrac{2π}{8}θ=82π 弧度处,它就是复数 w=eiθ=ei2π/8w=e^{i\theta}=\pmb{e^{i2π/8}}w=eiθ=ei2π/8. 为了强调这是八次方根,我们将其记为 w8w_8w8,也可以将其写成 cos⁡2π8\cos\dfrac{2π}{8}cos82πsin⁡2π8\sin\dfrac{2π}{8}sin82π 组合的形式,但是我们不这样写。沿着圆周绕行一周,我们可以得到另外七个八次方根 w2,w3,⋯ ,w8w^2,w^3,\cdots,w^8w2,w3,,w8。这些 www 的乘幂最好是用极形式表示,因为我们只会用到角度,这 888 个角度的弧度表示是 2π8,4π8,⋯ ,16π8=2π\dfrac{2π}{8},\dfrac{4π}{8},\cdots,\dfrac{16π}{8}=2π82π,84π,,816π=2π,度数表示就是 45°,90°,135°,⋯ ,360°45°,90°,135°,\cdots,360°45°,90°,135°,,360°.

在这里插入图片描述
111 的四次方根也在图中,它们分别是 i,−1,−i,1i,-1,-i,1i,1,i,1,间隔的角度是 2π4\dfrac{2π}{4}42π90°90°90°,第一个根 w4=e2πi/4w_4=e^{2πi/4}w4=e2πi/4 就是 iii;甚至 111 的平方根也在图中,第一个根 w2=ei2π/2=−1w_2=e^{i2π/2}=-1w2=ei2π/2=1,这里我们不要小瞧了平方根 111−1-11. FFT 背后的思想是将 8×8\pmb{8\times8}8×8 的傅里叶矩阵(包含八次方根 w8w_8w8 的幂)变换成下面的 4×4\pmb{4\times4}4×4 傅里叶矩阵(包含四次方根 w4=iw_4=iw4=i 的幂),同样的思想将四阶变换成二阶。通过探究 F8F_8F8 降阶到 F4F_4F4 以及升阶到 F16F_{16}F16(甚至更高阶)的规律,FFT 使得用 F1024F_{1024}F1024 左乘非常迅速。
首先是阶数 n=4n=4n=4 的傅里叶矩阵,它的行包含 1,w,w21,w,w^21,w,w2w3w^3w3 以及它们的幂,它们是 111 的四次方根,它们及其幂的排列很特别。傅里叶矩阵Fourier matrixn=4,w=iF=[11111ww2w31w2w4w61w3w6w9]=[11111ii2i31i2i4i61i3i6i9]\begin{array}{l}\pmb{傅里叶矩阵}\\\textrm{\pmb{Fourier matrix}}\\\pmb{n=4,w=i}\end{array}\kern 15ptF=\begin{bmatrix}1&1&1&1\\1&w&w^2&w^3\\1&w^2&w^4&w^6\\1&w^3&w^6&w^9\end{bmatrix}=\begin{bmatrix}1&1&1&1\\1&i&i^2&i^3\\1&i^2&i^4&i^6\\1&i^3&i^6&i^9\end{bmatrix}傅里叶矩阵Fourier matrixn=4,w=iF=11111ww2w31w2w4w61w3w6w9=11111ii2i31i2i4i61i3i6i9这个矩阵是一个对称矩阵,F=FTF=F^TF=FT,它不是一个埃尔米特矩阵,因为它的对角线元素不全是实数。但是由于 (12FH)(12F)=I(\dfrac{1}{2}F^H)(\dfrac{1}{2}F)=I(21FH)(21F)=I,所以 12F\dfrac{1}{2}F21F 是一个酉矩阵

F\pmb FF 的列向量可得 FHF=4I\color{blue}\pmb{F^HF=4I}FHF=4IFFF 的逆矩阵是 14FH\pmb{\dfrac{1}{4}F^H}41FH,即 F−1=14F‾\color{blue}\pmb{F^{-1}=\dfrac{1}{4}\overline F}F1=41F.

逆矩阵中将 w=iw=iw=i 变成 w‾=−i\overline w=-iw=i,这就将 FFF 变成了 F‾\overline FF,即可轻松求得逆矩阵 F−1F^{-1}F1. 当 FFT 实现了用 FFF 快速左乘时,也就同样快速的实现了用 F‾\overline FFF−1F^{-1}F1 的左乘。
nnn 阶傅里叶矩阵的每列长度是 n\sqrt nn,所以酉矩阵是 Q=FnQ=\dfrac{F}{\sqrt n}Q=nFQ−1=F‾nQ^{-1}=\dfrac{\overline F}{\sqrt n}Q1=nF,我们为了避免使用 n\sqrt nn,所以直接使用 FFFF−1=F‾n\pmb{F^{-1}=\dfrac{\overline F}{n}}F1=nF. 重点是用 FFF 左乘 (c0,c1,c2,c3)(c_0,c_1,c_2,c_3)(c0,c1,c2,c3)4点傅里叶级数4-point Fourier series[y0y1y2y3]=Fc=[11111ww2w31w2w4w61w3w6w9][c0c1c2c3](9.3.1)\begin{array}{l}\pmb{4点傅里叶级数}\\\pmb{\textrm{4-point Fourier series}}\end{array}\kern 15pt\begin{bmatrix}y_0\\y_1\\y_2\\y_3\end{bmatrix}=F\boldsymbol c=\begin{bmatrix}1&1&1&1\\1&w&w^2&w^3\\1&w^2&w^4&w^6\\1&w^3&w^6&w^9\end{bmatrix}\begin{bmatrix}c_0\\c_1\\c_2\\c_3\end{bmatrix}\kern 15pt(9.3.1)4点傅里叶级数4-point Fourier seriesy0y1y2y3=Fc=11111ww2w31w2w4w61w3w6w9c0c1c2c3(9.3.1)输入是四个复系数 c0,c1,c2,c3c_0,c_1,c_2,c_3c0,c1,c2,c3,输出是四个函数值 y0,y1,y2,y3y_0,y_1,y_2,y_3y0,y1,y2,y3. 第一个输出是 y0=c0+c1+c2+c3y_0=c_0+c_1+c_2+c_3y0=c0+c1+c2+c3 是傅里叶级数 ∑k=03ckeikx\sum\limits_{k=0}^3c_ke^{ikx}k=03ckeikxx=0x=0x=0 处的值,第二个输出是级数 ∑k=03ckeikx\sum\limits_{k=0}^3c_ke^{ikx}k=03ckeikxx=2π4x=\dfrac{2π}{4}x=42π 处的值:y1=c0+c1ei2π/4+c2ei4π/4+c3ei6π/4=c0+c1w+c2w2+c3w3y_1=c_0+c_1e^{i2π/4}+c_2e^{i4π/4}+c_3e^{i6π/4}=c_0+c_1w+c_2w^2+c_3w^3y1=c0+c1ei2π/4+c2ei4π/4+c3ei6π/4=c0+c1w+c2w2+c3w3第三个输出 y2y_2y2 和第四个输出 y3y_3y3∑k=03ckeikx\sum\limits_{k=0}^3c_ke^{ikx}k=03ckeikx 分别在 x=4π4x=\dfrac{4π}{4}x=44πx=6π4x=\dfrac{6π}{4}x=46π 处的值。这些是有限项傅里叶级数!它们包含 n=4n=4n=4 项且为等距分布的 n=4n=4n=4 个点,即点 x=0,2π4,4π4,6π4x=0,\dfrac{2π}{4},\dfrac{4π}{4},\dfrac{6π}{4}x=0,42π,44π,46π 处取值。

按照这个规律,下一个点是 x=8π4x=\dfrac{8π}{4}x=48π,即是 2π2π2π,而由于 e2πi=e0=1e^{2πi}=e^0=1e2πi=e0=1,所以级数又回到了 y0y_0y0,每轮循环都以 444 为周期,由于 (w2)(w2)=w0=1(w^2)(w^2)=w^0=1(w2)(w2)=w0=1,所以在这里指数 2+22+22+2 与指数 000 是相同的。依照惯例,规定行标 j\pmb jj 和列标 k\pmb kk 是从 0\pmb00n−1\pmb{n-1}n1(而不是 111nnn). FFF 的 “第零行” 和 “第零列” 都是全 111 向量。
n×nn\times nn×n 的傅里叶矩阵 FnF_nFn 包含 w=e2πi/nw=e^{2πi/n}w=e2πi/n 的幂:Fnc=[111⋯11ww2⋯wn−11w2w4⋯w2(n−1)⋮⋮⋮⋮1wn−1w2(n−1)⋯w(n−1)2][c0c1c2⋮cn−1]=[y0y1y2⋮yn−1]=y(9.3.2)F_n\boldsymbol c=\begin{bmatrix}1&1&1&\cdots&1\\1&w&w^2&\cdots&w^{n-1}\\1&w^2&w^4&\cdots&w^{2(n-1)}\\\vdots&\vdots&\vdots&&\vdots\\1&w^{n-1}&w^{2(n-1)}&\cdots&w^{(n-1)^2}\end{bmatrix}\begin{bmatrix}c_0\\c_1\\c_2\\\vdots\\c_{n-1}\end{bmatrix}=\begin{bmatrix}y_0\\y_1\\y_2\\\vdots\\y_{n-1}\end{bmatrix}=\pmb y\kern 15pt(9.3.2)Fnc=11111ww2wn11w2w4w2(n1)1wn1w2(n1)w(n1)2c0c1c2cn1=y0y1y2yn1=y(9.3.2)FnF_nFn 是对称矩阵,但不是埃尔米特矩阵。它的列是正交的,且 FnF‾n=nIF_n\overline F_n=nIFnFn=nI,其逆矩阵包含 w‾n=e−2πi/n\overline w_n=e^{-2πi/n}wn=e2πi/n 的幂。观察 FFF 中元素的特征:

jjj 行,第 kkk 列的元素为 wjkw^{jk}wjk. 零行零列的元素均为 w0=1w^0=1w0=1.

当用 FFF 左乘 c\boldsymbol cc 时,相当于在 nnn 个点处求级数的和。当用 Fn−1F_n^{-1}Fn1 左乘 y\boldsymbol yy 时,即是由函数值 y\boldsymbol yy 求系数 c\boldsymbol cc. 在 MATLAB 中,对应的命令是 c=fft(y)\boldsymbol c=\textrm{fft}(\boldsymbol y)c=fft(y). 矩阵 FFF 能够将 “频率空间” 转换到 “物理空间” 即 “时域空间”。

重要注释: 很多作者更喜欢使用 ω=e−2πi/N\omega=e^{-2πi/N}ω=e2πi/N,也就是这里 www 的复共轭。(它们使用的是希腊字母 ω\omegaω,这里使用的是英文字母 www.)若使用这种选择,则 DFT 矩阵包含的是 ω\omegaω 的幂而不是 www 的幂,它就是 F‾\overline FFFFF 的共轭。F‾\overline FF 将时域空间变换到频率空间。
F‾\overline FF 是一个完全合理的选择!MATLAB 使用的是 ω=e−2πi/N\omega=e^{-2πi/N}ω=e2πi/N,DFT 矩阵 fft(eye(N)) 包含数值 w=w‾w=\overline ww=w 的幂。傅里叶矩阵 FFF 的元素是 www 的幂,它由 c\boldsymbol cc 重构 y\boldsymbol yy,而 F‾\overline FF 的元素是 ω\omegaω 的幂,它用 fft(y) 来计算傅里叶系数。
另一个重要注释: 当函数 f(x)f(x)f(x) 的周期是 2π2π2π 时,我们将 xxx 变为 eiθe^{i\theta}eiθ,则这个函数就被定义在单位圆上(这里 z=eiθz=e^{i\theta}z=eiθ). 离散傅里叶变换(Discrete Fourier Transform)等同于插值(interpolation),即求出在 nnn 个点 z=1,w,⋯ ,wn−1z=1,w,\cdots,w^{n-1}z=1,w,,wn1 处分别取 nnn 个值 f0,f1,⋯ ,fn−1f_0,f_1,\cdots,f_{n-1}f0,f1,,fn1 的多项式 p(z)=c0+c1z+⋯+cn−1zn−1p(z)=c_0+c_1z+\cdots+c_{n-1}z^{n-1}p(z)=c0+c1z++cn1zn1

插值 Interpolation求出 c0,c1,⋯ ,cn−1,使得在 n 个点 z=1,w,⋯ ,wn−1 处有 p(wj)=fj\kern 5pt\color{blue}求出\,c_0,c_1,\cdots,c_{n-1},使得在\,n\,个点\,z=1,w,\cdots,w^{n-1}\,处有\,p(w^j)=f_j求出c0,c1,,cn1,使得在n个点z=1,w,,wn1处有p(wj)=fj

傅里叶矩阵就是在 nnn 个特殊点处插值时所得方程组的范德蒙德矩阵(Vandermonde matrix).

三、快速傅里叶变换的一步

我们希望用 FFF 左乘 c\boldsymbol cc 时的计算越快越好。正常情况下,一个 nnn 阶方阵有 n2n^2n2 个元素,所以这个矩阵乘一个向量需要计算 n2n^2n2 次乘法。如果矩阵中含有零元素,则对应的乘法可以被忽略,但是傅里叶矩阵里没有零元素!所以我们可能以为该算法不可能再得到改进了。而通过将其中的元素改写成特殊的形式 wjkw^{jk}wjk,将 FFF 进行分解,从而产生很多零元素,这就是 FFT .
关键思想是将 Fn\pmb{F_n}Fn 与阶数减半的傅里叶矩阵 Fn/2\pmb{F_{n/2}}Fn/2 联系起来。假设 nnn222 的幂(比如说 n=210=1024n=2^{10}=1024n=210=1024),下面我们将要把 F1024F_{1024}F1024两个 F512\pmb{F_{512}}F512 联系起来。
先从 n=4n=4n=4 开始,关键是建立 F4F_4F4 和两个 F2F_2F2 之间的联系:F4=[11111ii2i31i2i4i61i3i6i9],[F2F2]=[111i2111i2]\pmb{F_4}=\begin{bmatrix}1&1&1&1\\1&i&i^2&i^3\\1&i^2&i^4&i^6\\1&i^3&i^6&i^9\end{bmatrix},\kern 15pt\begin{bmatrix}\pmb{F_2}\\&\pmb{F_2}\end{bmatrix}=\begin{bmatrix}1&1\\1&i^2\\&&1&1\\&&1&i^2\end{bmatrix}F4=11111ii2i31i2i4i61i3i6i9,[F2F2]=111i2111i2左边的 F4F_4F4 中没有零元素,右边的矩阵有一半的零元素,因此计算量会减半。但是,这两个矩阵还不相等,我们需要两个稀疏且简单的矩阵来完成 FFT 分解:FFT 分解F4=[111i1−11−i][111i2111i2][1111](9.3.3)\pmb{\textrm{FFT}\,分解}\kern 18ptF_4=\begin{bmatrix}1&&1\\&1&&i\\1&&-1\\&1&&-i\end{bmatrix}\begin{bmatrix}1&1\\1&i^2\\&&1&1\\&&1&i^2\end{bmatrix}\begin{bmatrix}1\\&&1\\&1\\&&&1\end{bmatrix}\kern 20pt(9.3.3)FFT分解F4=111111ii111i2111i21111(9.3.3)最后一个矩阵是置换矩阵,它将偶数号系数(c0c_0c0c2c_2c2)放在了奇数号系数(c1c_1c1c3c_3c3)之前;中间的矩阵分别在偶数号和奇数号系数上执行规模减半的变换 F2F_2F2F2F_2F2;左边的矩阵将两个规模减半的输出合并起来,这样就可以得到正确的完整输出 y=F4c\boldsymbol y=F_4\boldsymbol cy=F4c.
同样的思想也可以应用在 n=1024n=1024n=1024m=12n=512m=\dfrac{1}{2}n=512m=21n=512 的情形下。此时数 wwwe2πi/1024e^{2πi/1024}e2πi/1024,它在单位圆弧度 θ=2π1024\theta=\dfrac{2π}{1024}θ=10242π 处,傅里叶矩阵 F1024F_{1024}F1024 中的元素全是 www 的幂。FFT 的第一步就是这项伟大的分解,它是由 Cooley 和 Tukey 给出的(1805 年高斯曾经预言过):

F1024=[I512D512I512−D512][F512F512][even-oddpermutation](9.3.4)F_{1024}=\begin{bmatrix}I_{512}&\kern 7ptD_{512}\\I_{512}&-D_{512}\end{bmatrix}\begin{bmatrix}\pmb{F_{512}}\\&\pmb{F_{512}}\end{bmatrix}\begin{bmatrix}\textrm{even-odd}\\\textrm{permutation}\end{bmatrix}\kern 20pt(9.3.4)F1024=[I512I512D512D512][F512F512][even-oddpermutation](9.3.4)

I512I_{512}I512 是单位矩阵,D512D_{512}D512 是对角矩阵,其对角元素为 1,w,⋯ ,w5111,w,\cdots,w^{511}1,w,,w511;两个 F512F_{512}F512 正是我们所期望的,注意这里使用的是 512512512 次单位根(即 w2w^2w2 !!);置换矩阵将输入向量 c\boldsymbol cc 分成偶数号部分 c′=(c0,c2,⋯ ,c1022)\boldsymbol c'=(c_0,c_2,\cdots,c_{1022})c=(c0,c2,,c1022)和奇数号部分 c′′=(c1,c3,⋯ ,c1023)\boldsymbol c''=(c_1,c_3,\cdots,c_{1023})c′′=(c1,c3,,c1023).
下面是代数公式,它与 F1024F_{1024}F1024 的分解描述是一样的:

(FFT 的一步)令 m=12n\pmb{m=\dfrac{1}{2}n}m=21ny=Fnc\boldsymbol y=F_n\boldsymbol cy=Fnc 的前 mmm 位分量和后 mmm 位分量分别为规模减半的变换 y′=Fmc′\boldsymbol y'=F_m\boldsymbol c'y=Fmcy′′=Fmc′′\boldsymbol y''=F_m\boldsymbol c''y′′=Fmc′′. 这对应式(9.3.4)中的 Iy′+Dy′′I\boldsymbol y'+D\boldsymbol y''Iy+Dy′′Iy′−Dy′′I\boldsymbol y'-D\boldsymbol y''IyDy′′,这个等式展示了从 nnnm=n2m=\dfrac{n}{2}m=2n 的步骤:yj=yj′+(wn)jyj′′,j=0,1,⋯ ,m−1yj+m=yj′−(wn)jyj′′,j=0,1,⋯ ,m−1(9.3.5)\begin{array}{r}\color{blue}y_j=y_j'+(w_n)^jy_j'',\kern 10ptj=0,1,\cdots,m-1\\\color{blue}y_{j+m}=y'_j-(w_n)^jy''_j,\kern 10ptj=0,1,\cdots,m-1\end{array}\kern 15pt(9.3.5)yj=yj+(wn)jyj′′,j=0,1,,m1yj+m=yj(wn)jyj′′,j=0,1,,m1(9.3.5)c\boldsymbol cc 分为 c′\boldsymbol c'cc′′\boldsymbol c''c′′,用 FmF_mFm 分别将它们变换为 y′\boldsymbol y'yy′′\boldsymbol y''y′′,从而(9.3.5)重构了 y\boldsymbol yy.

这些公式来自于将 c0,c1,⋯ ,cn−1c_0,c_1,\cdots,c_{n-1}c0,c1,,cn1 分成偶数号 c2kc_{2k}c2k 和奇数号 c2k+1c_{2k+1}c2k+1,这里的 wwwwnw_nwny=Fcyj=∑k=0n−1wjkck=∑k=0m−1w2jkc2k+∑k=0m−1wj(2k+1)c2k+1,其中 m=12n(9.3.6)\boldsymbol y=F\boldsymbol c\kern 15pty_j=\sum_{k=0}^{n-1}w^{jk}c_k=\sum_{k=0}^{m-1}w^{2jk}c_{2k}+\sum_{k=0}^{m-1}w^{j(2k+1)}c_{2k+1},\kern 3pt其中\,m=\dfrac{1}{2}n\kern 15pt(9.3.6)y=Fcyj=k=0n1wjkck=k=0m1w2jkc2k+k=0m1wj(2k+1)c2k+1,其中m=21n(9.3.6)上式即(9.3.2)的展开形式,偶数号分量是 c′=(c0,c2,⋯ ,cn−2)\boldsymbol c'=(c_0,c_2,\cdots,c_{n-2})c=(c0,c2,,cn2),奇数号分量是 c′′=(c1,c3,⋯ ,cn−1)\boldsymbol c''=(c_1,c_3,\cdots,c_{n-1})c′′=(c1,c3,,cn1),然后进行变换 Fmc′F_m\boldsymbol c'FmcFmc′′F_m\boldsymbol c''Fmc′′关键是 wn2=wm\pmb{w^2_n=w_m}wn2=wm,这得到 wn2jk=wmjkw_n^{2jk}=w_m^{jk}wn2jk=wmjk.重写 (9.2.6)yj=∑k=0m−1(wm)jkck′+(wn)j∑k=0m−1(wm)jkck′′=yj′+(wn)jyj′′(9.3.7)\pmb{重写\,(9.2.6)}\kern 15pty_j=\sum_{k=0}^{m-1}(w_m)^{jk}c'_k+(w_n)^j\sum_{k=0}^{m-1}(w_m)^{jk}c''_k=y_j'+(w_n)^jy''_j\kern 15pt(9.3.7)重写(9.2.6)yj=k=0m1(wm)jkck+(wn)jk=0m1(wm)jkck′′=yj+(wn)jyj′′(9.3.7)j≥mj\ge mjm 时,(9.3.5)中的负号都来自于 (wn)j(w_n)^j(wn)j 所分解出的 (wn)m=−1(w_n)^m=-1(wn)m=1.
MATLAB 中可以使用 conj(F) 或傅里叶逆变换 ifft 轻易的将偶数号与奇数号分量分开,再乘上 wnjw_n^jwnj,因为 fft 是基于 ω=w‾=e−2πi/n\omega=\overline w=e^{-2πi/n}ω=w=e2πi/n. FFF 和 conj(F) 可以通过行置换联系起来。MATLAB 中从n 到 n/2 的 FFT步骤y′=ifft(c(0:2:n−2))∗n/2;y′′=ifft(c(1:2:n−1))∗n/2;d=w.^(0:n/2−1)′;y=[y′+d.∗y′′;y′−d.∗y′′];\begin{array}{l}\pmb{\textrm{MATLAB}\,中从}\\\pmb{n\,到\,n/2\,的\,\textrm{FFT}}\\\pmb{步骤}\end{array}\kern 20pt\begin{array}{l}y'=\textrm{ifft}(c(0:2:n-2))*n/2;\\y''=\textrm{ifft}(c(1:2:n-1))*n/2;\\d=w. \verb|^|(0:n/2-1)';\\y=[y'+d.*y'';y'-d.*y''];\end{array}MATLAB中从nn/2FFT步骤y=ifft(c(0:2:n2))n/2;y′′=ifft(c(1:2:n1))n/2;d=w.^(0:n/21);y=[y+d.y′′;yd.y′′];上述代码需要注意的地方:实际上 MATLAB 的标号是从 1 开始的,上述仅仅是伪代码;之所以后面会乘上 n/2n/2n/2,这是因为 MATLAB 中的 ifft 会自动归一化,即它会除以 n/2n/2n/2,这里再乘回来,不做归一化。
下面的流程图表明了用规模减半的矩阵 F2F_2F2 左乘 c′\boldsymbol c'cc′′\boldsymbol c''c′′. 根据这个形状,我们称这个步骤称为 “蝴蝶形 butterflies”,然后将输出 y′\boldsymbol y'yy′′\boldsymbol y''y′′ 结合起来(用 DDD 中的 1,i1,i1,iy′′\boldsymbol y''y′′,再用 −D-DD 中的 −1,−i-1,-i1,iy′′\boldsymbol y''y′′),从而得到 y=F4c\boldsymbol y=F_4\boldsymbol cy=F4c.

在这里插入图片描述
FnF_nFn 化成两个 FmF_mFm 几乎将计算量减半 —— 从分解的矩阵中出现的很多零元素可以看出。这种减半虽然很好但是不够彻底,FFT 的完整思想要更明显,它要节省出远超过一半的时间。

四、利用递归的完整版 FFT

到目前为止,我们只是将 FnF_nFn 减少到了 Fn/2F_{n/2}Fn/2,我们还要继续将其减少至 Fn/4\pmb{F_{n/4}}Fn/4. 将每个 F512F_{512}F512 都化成两个 F256F_{256}F256,然后将每个 F256F_{256}F256 化成两个 F128F_{128}F128. 这就是递归(recursion).
递归是很多快速算法的底层原理。我们下面进行第 222 步,此时出现了四个 F256F_{256}F256DDD(包含 ω512\omega_{512}ω512256256256 个幂). 偶数号中的偶数号 c0,c4,c8,⋯c_0,c_4,c_8,\cdotsc0,c4,c8, 会首先出现:[F512F512]=[IDI−DIDI−D][FFFF][pick0,4,8,⋯pick2,6,10,⋯pick1,5,9,⋯pick3,7,11,⋯]\begin{bmatrix}F_{512}\\&F_{512}\end{bmatrix}=\begin{bmatrix}I&\kern 7ptD\\I&-D\\&&I&\kern 7ptD\\&&I&-D\end{bmatrix}\begin{bmatrix}F\\&F\\&&F\\&&&F\end{bmatrix}\begin{bmatrix}\textrm{pick}&0,4,8,\cdots\\\textrm{pick}&2,6,10,\cdots\\\textrm{pick}&1,5,9,\cdots\\\textrm{pick}&3,7,11,\cdots\end{bmatrix}[F512F512]=IIDDIIDDFFFFpickpickpickpick0,4,8,2,6,10,1,5,9,3,7,11,下面我们来计算单个乘法的次数,以搞清节省了多少计算量。在 FFT 发现之前,一般情况下我们需要 n2=(1024)2n^2=(1024)^2n2=(1024)2,这是有超过一百万次的乘法,可能这样计算并不会花费太长的时间,但是如果需要做很多次变换时(这是比较普遍的),时间的消耗就会非常巨大。而通过 FFT 我们可以节省相当可观的计算量:对于阶数 n=2l 的矩阵,最终的乘法次数由 n2 减少到 12nl\pmb{对于阶数\,n=2^l\,的矩阵,最终的乘法次数由\,n^2\,减少到\,\dfrac{1}{2}nl}对于阶数n=2l的矩阵,最终的乘法次数由n2减少到21nl102410241024 就是 2102^{10}210,所以 l=10l=10l=10. 那么乘法次数就由最初的 (1024)2(1024)^2(1024)2 减少到了 (5)(1024)(5)(1024)(5)(1024) ,即从一百万次减少到了五千多次,减少为原来的 1200\dfrac{1}{200}2001 了。这也就是为什么说 FFT 在彻底改变了信号处理了。
下面解释乘法次数为什么 12nl\dfrac{1}{2}nl21nl。由 n=2ln=2^ln=2l 降到 n=1n=1n=1 的过程中,总共有 lll 个层级。每一级为了重新组合从更低层级而来的规模减半的输出,关于对角矩阵 DDD2n\dfrac{2}{n}n2 次乘法,所以可以由此得到最终的 12nl\dfrac{1}{2}nl21nl 次乘法,这个就是 12nlog⁡2n\dfrac{1}{2}n\log_2n21nlog2n.
关于 FFT 这个意义非凡的算法最后一条注记,在对向量分量进行全部偶-奇置换后,这些分量进行 FFT 时的次序有一条令人惊叹的规则。将数字 000n−1n-1n1 写成二进制形式(如 n=4n=4n=4 时的 00,01,10,1100,01,10,1100,01,10,11),然后反转每个数字的次序得到:00,10,01,1100,10,01,1100,10,01,11,这样得到了位反转排序(bit-reversed order)0,2,1,3,其中偶数排在奇数之前。向量分量按位反转排序,共有 l=log⁡2nl=\log_2nl=log2n 次递归步骤,最终的输出 y0,y1,⋯ ,yn−1y_0,y_1,\cdots,y_{n-1}y0,y1,,yn1 就是 FnF_nFn 左乘 c\boldsymbol cc.
这一节展示了矩阵左乘向量这一基本运算,也有可能对其计算进行优化。

http://www.lryc.cn/news/600757.html

相关文章:

  • Docker常用命令详解:以Nginx为例
  • gig-gitignore工具实战开发(五):gig add完善
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 热词评论查询功能实现
  • Spring Boot 单元测试进阶:JUnit5 + Mock测试与切片测试实战及覆盖率报告生成
  • Android ADB命令之内存统计与分析
  • Java学习|黑马笔记|Day23】网络编程、反射、动态代理
  • 深入理解C语言快速排序与自省排序(Introsort)
  • 安卓服务与多线程
  • 学习嵌入式的第三十天-数据结构-(2025.7.21)网络编程
  • 系统性学习C语言-第二十三讲-文件操作
  • 台式电脑有多个风扇开机只有部分转动的原因
  • Matlab自学笔记六十五:解方程的数值解法(代码速成)
  • Nacos-服务注册,服务发现(二)
  • 八股文整理——计算机网络
  • 容器化成本优化:K8s资源请求与限制的黄金法则——从资源画像分析到25%成本削减的实战指南
  • 记录和分享抓取的数字货币和大A时序数据
  • 什么是ICMP报文?有什么用?
  • Matlab学习笔记:自定义函数
  • java基础(day16)set-map
  • DAY24 元组和OS模块
  • 【安全漏洞】网络守门员:深入理解与应用iptables,守护Linux服务器安全
  • Java基础-文件操作
  • spring Could 高频面试题
  • 面试问题总结——关于OpenCV(二)
  • 详解力扣高频SQL50题之619. 只出现一次的最大数字【简单】
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——6. 传统算法实战:用OpenCV测量螺丝尺寸
  • 人工智能之数学基础:概率论之韦恩图的应用
  • Java 镜像减肥记:Docker 多阶段构建全攻略
  • 统计学08:概率分布
  • 【SSM】第二章 网上蛋糕项目商城-首页