Bootstrap

快速傅里叶变换FFT总结

快速傅里叶变换,在竞赛中离散傅里叶变换DFT及其逆变换IDFT尤为常用,主要用于快速求多项式的乘积。形式化地说,多项式就是某个 f ( x ) = ∑ i = 0 n a i x i f(x)=\sum\limits^n_{i=0}a_ix^i f(x)=i=0naixi,两个系数分别为 a i a_i ai b i b_i bi,那么 ( f × g ) ( x ) = f ( x ) g ( x ) = ∑ i = 0 n ∑ j = 0 n a i b j x i + j (f\times g)(x)=f(x)g(x)=\sum\limits^{n}_{i=0}\sum\limits_{j=0}^{n}a_ib_jx^{i+j} (f×g)(x)=f(x)g(x)=i=0nj=0naibjxi+j,容易看出这个多项式是 2 n 2n 2n次的。
【补充知识1】我们接着先看一下一个叫卷积的东西。卷积实际上就是对应相乘, ( f ⊗ g ) ( x ) = ∑ i = 0 n a i b i x i (f\otimes g)(x)=\sum\limits^{n}_{i=0}a_ib_ix^i (fg)(x)=i=0naibixi,可以稍微理解一下,实质上我们如果将乘法视为一种变换,卷积就是在向量上的变换的叠加。
【补充知识2】
其实,对于一条 n n n次的多项式曲线,我们可以通过类比认识到我们需要 n + 1 n+1 n+1个不同的点就可以唯一确定这条曲线。比如说一次函数需两个点,二次函数需三个点,等等。我们考虑到,我们表达这些函数为 f ( x ) = ∑ i = 0 n a i x i f(x)=\sum\limits^n_{i=0}a_ix^i f(x)=i=0naixi,那么如果我们现在得知了 n + 1 n+1 n+1个函数上的点,直接代入就可以相应得出 n + 1 n+1 n+1个方程,由于点两两不同,所以这个方程组一定恰有一组解,这也就确定了一条唯一的曲线。

好的,接下来是真正的内容。
我们可以发现,要确定最终的 ( f ⊗ g ) ( x ) (f\otimes g)(x) (fg)(x),需要使用 2 n + 1 2n+1 2n+1个点值表达,也就是在最终结果曲线上的 2 n + 1 2n+1 2n+1个点。我们如果考虑在 f ( x ) f(x) f(x) g ( x ) g(x) g(x)上取 2 n + 1 2n+1 2n+1个点,设为 ( x i , y f i ) (x_i,{y_f}_i) (xi,yfi) ( x i , y g i ) (x_i,{y_g}_i) (xi,ygi),我们发现只要将其卷积一下,我们就能够找到这 2 n + 1 2n+1 2n+1个点。这是很好理解的,因为将 y f i {y_f}_i yfi y g i {y_g}_i ygi卷积后作为新的纵坐标所得到的点,显然就在 ( f ⊗ g ) ( x ) (f\otimes g)(x) (fg)(x)上。
那么我们的问题就只剩下怎样计算和如何再次快速确定了。
我们考虑下分治,如果我们将系数向量中奇数项和偶数项分开来,我们会发现所得的两个式子间有一定的相似性: a 1 x 1 + a 3 x 3 + ⋯ + a 2 k + 1 x 2 k + 1 a_1x^1+a_3x^3+\cdots+a_{2k+1}x^{2k+1} a1x1+a3x3++a2k+1x2k+1 a 0 x 0 + a 2 x 2 + ⋯ + a 2 k x 2 k + ⋯ a_0x^0+a_2x^2+\cdots+a_{2k}x^{2k}+\cdots a0x0+a2x2++a2kx2k+
实际上,我们考虑将上面的式子变为 x ( a 1 x 0 + a 3 x 2 + ⋯ + a 2 k + 1 x 2 k + ⋯   ) x(a_1x^0+a_3x^2+\cdots+a_{2k+1}x^{2k}+\cdots) x(a1x0+a3x2++a2k+1x2k+),再将各项的次数折半(因为是偶数),我们现在就能够得到两个规模相对于原来的问题减半的子问题,可以递归求解,这里我们就得出了DFT的算法,由主定理对于 T ( n ) = 2 T ( n 2 ) + O ( n ) T(n)=2T(\frac{n}{2})+\text O(n) T(n)=2T(2n)+O(n),马上看出其复杂度 O ( n lg ⁡ n ) \text O(n\lg n) O(nlgn)
但是,在还原的时候,我们却遇到了一个困难:我们如何将点值表达快速变为系数表达?难道我们去用高斯消元法?这样 O ( n 3 ) \text O(n^3) O(n3)岂不是比原来还慢?我们来观察一下DFT的实质。我们将DFT的效果写成这样一个矩阵:
[ 1 x 0 ⋯ x 0 n 1 x 1 ⋯ x 1 n ⋮ ⋮ ⋱ ⋮ 1 x n ⋯ x n n ] \begin{bmatrix} 1 & x_0 & \cdots x_0^n \\ 1 & x_1 & \cdots x_1^n \\ \vdots & \vdots & \ddots & \vdots\\ 1& x_n & \cdots x_n^n\\ \end{bmatrix} 111x0x1xnx0nx1nxnn
稍微解释下,根据线性代数,这个东西乘上我们的系数向量之后所得就是对应的 x = x 0 , 1 , ⋯   , n x=x_{0,1,\cdots,n} x=x0,1,,n ( f ⊗ g ) ( x ) (f\otimes g)(x) (fg)(x)的结果。这个东西是很显然的,考虑用线性变换的角度理解。
然后我们考虑DFT的逆变换,即所谓IDFT,其的实质就是乘上DFT所乘上的这个矩阵的一个逆矩阵。由逆矩阵定义,我们可以考虑猜想在逆矩阵中 ( j , k ) (j,k) (j,k)处,数为 x k − j × C x_k^{-j}\times C xkj×C,其中 C C C是一个常数。那么如果将两个矩阵乘起来,再考虑 ( j , j ′ ) (j,j') (j,j)处的值,为 ∑ k = 0 n x k j ′ − j C \sum^{n}_{k=0}x_k^{j'-j}C k=0nxkjjC要满足逆矩阵条件 V V − 1 = I n VV^{-1}=I_n VV1=In,我们需要当且仅当 j ′ = j j'=j j=j的时候上式为 1 1 1,其余情况为 0 0 0。先看第一个条件,代进去得到 ∑ k = 0 n C = 1 \sum^n_{k=0}C=1 k=0nC=1,得 C = 1 n C=\frac{1}{n} C=n1。代回去并根据第二个条件我们有 ∑ k = 0 n x k j ′ − j = 0 ( j ′ ≠ j ) \sum^n_{k=0}x_k^{j'-j}=0(j'\neq j) k=0nxkjj=0(j=j)
然后我并不知道怎么一直推下去,于是我们直接来一个构造法。我们令 x k = w n k x_k=w_n^k xk=wnk,然后我们就得到了一个等比数列,按照上式求和根据公式可以得到 ∑ k = 0 n ( w n k ) j ′ − j = ∑ k = 0 n ( w n j ′ − j ) k = ( w n j ′ − j ) n − 1 w n j ′ − j − 1 = 0 \sum\limits_{k=0}^n(w_n^k)^{j'-j}=\sum\limits_{k=0}^n(w_n^{j'-j})^k=\frac{(w_n^{j'-j})^n-1}{w_n^{j'-j}-1}=0 k=0n(wnk)jj=k=0n(wnjj)k=wnjj1(wnjj)n1=0,即(这里跳了一步移项) ( w n j ′ − j ) n = 1 (w_n^{j'-j})^n=1 (wnjj)n=1我们又考虑到, j ′ j' j j j j取遍 0 ⋯ n 0\cdots n 0n间的所有值,所以上式可以改为 ( w n k ) n = w n k n = 1 (w^k_n)^n=w_n^{kn}=1 (wnk)n=wnkn=1,其中 k k k的值域为从 − n -n n n n n
然后,我们考虑先取 k = 1 k=1 k=1,本质上这里说的就是我们需要这样的一类不同的 2 n 2n 2n个数使得 ( w n ) n = 1 (w_n)^n=1 (wn)n=1。显然在实数域中只能有 1 1 1(以及 − 1 -1 1,因为我们在分治的时候预先补了零使得次数是 2 d 2^d 2d)满足这个性质,显然太少了,怎么办?
我们考虑扩展取值范围。一种方法,就是不只考虑实轴,我们可以考虑下复平面。也就是说,我们可以不只考虑 a ∈ R a\in \mathbb{R} aR,而是考虑所有 a + b i ∈ C a+bi\in \mathbb{C} a+biC,其中 a , b ∈ R a,b\in \mathbb{R} a,bR,而这里的 i i i,是一个曾经数学中往往避开的事物,满足 i 2 = − 1 i^2=-1 i2=1。实质上来说,复数可以理解为一种向量,比如说两个复数相加就是向量作用的叠加,而两个复数的积,则是另一种程度上的叠加。比方说我们考虑到,我们用极坐标也可以确定向量,进而确定复数,我们由变换 f ( x ) = { x = ρ cos ⁡ θ y = ρ sin ⁡ θ f(x)=\left\{ \begin{aligned} x & = &\rho \cos\theta \\ y & = & \rho\sin\theta \end{aligned} \right. f(x)={xy==ρcosθρsinθ
我们可以得出在极坐标意义下 a + b i a+bi a+bi可以写成 ρ ( cos ⁡ θ + i sin ⁡ θ ) \rho(\cos\theta+i\sin\theta) ρ(cosθ+isinθ),考虑两个复数相乘的时候,我们有(涉及高中三角学知识,若无基础可以跳过) ρ 1 ( cos ⁡ θ 1 + i sin ⁡ θ 1 ) × ρ 2 ( cos ⁡ θ 2 + i sin ⁡ θ 2 ) = ρ 1 ρ 2 ( cos ⁡ θ 1 c o s θ 2 + i sin ⁡ θ 1 cos ⁡ θ 2 + i sin ⁡ θ 2 cos ⁡ θ 1 − sin ⁡ θ 1 sin ⁡ θ 2 ) \rho_1(\cos\theta_1+i\sin\theta_1)\times \rho_2(\cos\theta_2+i\sin\theta_2)=\rho_1\rho_2(\cos\theta_1 cos\theta_2+i\sin\theta_1\cos\theta_2+i\sin\theta_2\cos\theta_1-\sin\theta_1\sin\theta_2) ρ1(cosθ1+isinθ1)×ρ2(cosθ2+isinθ2)=ρ1ρ2(cosθ1cosθ2+isinθ1cosθ2+isinθ2cosθ1sinθ1sinθ2)考虑利用各种和差化积公式(具体推导这里略去),得到乘积为 ρ 1 ρ 2 ( cos ⁡ ( θ 1 + θ 2 ) + i sin ⁡ ( θ 1 + θ 2 ) ) \rho_1\rho_2(\cos(\theta_1+\theta_2)+i\sin(\theta_1+\theta_2)) ρ1ρ2(cos(θ1+θ2)+isin(θ1+θ2)),实质上就是长度相乘,旋转角相加。
我在继续来考量下这个概念有什么用。上面的 ( w n ) n = 1 (w_n)^n=1 (wn)n=1在变换群里面其实对应的是一种循环,称作生成元。我们考虑到,如果对应在复平面上,我们考虑令 ρ = 1 \rho=1 ρ=1,然后长度上不管怎么乘都不会变,最后也就是 1 1 1(由于 ρ \rho ρ被要求是非负实数),然后神奇的东西来了,我们令 θ = 2 π / n \theta=2\pi/n θ=2π/n,也就是将整个复平面划分成 n n n份,那么根据我们刚才的相乘原则,这个复数向量 cos ⁡ ( 2 π n + i sin ⁡ ( 2 π n ) ) \cos(\frac{2\pi}{n}+i\sin(\frac{2\pi}{n})) cos(n2π+isin(n2π))实际上就是我们所要求的 w n w_n wn。继续考量下它的性质,如果考虑 w n k ( 0 ≤ k < n ) w_n^k(0\leq k< n) wnk(0k<n),其的 n n n次方也是 1 1 1,因为 ( w n k ) n = ( w n n ) k = 1 k = 1 (w_n^k)^n=(w_n^n)^k=1^k=1 (wnk)n=(wnn)k=1k=1,同时在复数意义下,这 n n n个数也是两两不同的,所以我们如果代入这 n n n个数,我们就可以在 O ( n lg ⁡ n ) \text O(n\lg n) O(nlgn)的时间内计算出IDFT,而且代码上和DFT差不多,因为只是最后多除了个 n n n,同时把 w n k w_n^k wnk换成了 w n − k w_n^{-k} wnk,因此也可以写在一起,传入参数在某几步上加个特判。
同时复数还有性质 w n k + n / 2 = w n k × w n n / 2 w_n^{k+n/2}=w_n^k\times w_n^{n/2} wnk+n/2=wnk×wnn/2,而又容易由角度看出 w n n / 2 = − 1 w_n^{n/2}=-1 wnn/2=1,所以 w n k + n / 2 = − w n k w_n^{k+n/2}=-w_n^k wnk+n/2=wnk。这个东西可以用来稍微改进下DFT,这样我们的计算量大概减少了一半左右,代码也是更容易写了。
不过,虽然说DFT在理论上是没有误差的,但是鉴于三角函数浮点数上计算有不小的误差,所以说如果多项式的系数向量都是整数的话,显然最后的系数向量也是整数,那么我们完全可以不在复数域下进行,而是在另一种同样具有群性质的数系下进行,比如说我们同样熟悉的模域下。而这就要在数论上进行讨论了,所以并不写,坑等以后再填……
好吧,最后再提一下,其实是有四种傅里叶变换的,分别对应着时域和频域上的离散和连续,其中如果两个域都是连续的,实质上就是傅里叶级数,也就是将一个函数展开成许多三角函数构成的无穷级数。再换句话说,就是用许多正(余)弦波的叠加去不断拟合某条给定的曲线。当然,你也可以用方波去拟合,这就涉及到快速沃尔什变换(FWT)了。

;