[FFT]快速傅里叶变换

引入

首先,FFT是快速傅里叶变换的简称。傅里叶变换是一个数学中使用的对函数的变换,而离散傅里叶变换顾名思义就是对于离散点的傅里叶变换。
那么,在OI中,傅里叶变换的最大意义,就在于加速向量卷积,即多项式乘法。

多项式乘法

系数表示法

现有两个多项式

A(x)=i=0naixiB(x)=i=0nbixi

我们这样定义它们的乘法
C(x)=i=02n1(j=0iajbij)xi=A(x)B(x)

计算它们的乘法,朴素的做法需要 O(n2) 的复杂度
我们将上述 A(x) 中的系数写作一个向量 A ,这种方法称为系数表示法

点值表示法

现在来考虑另一种方法:点值表示法
设向量 A=(A(x1),A(x2)...A(xn)) A 称作 A(x) 的点值表示法
点值表示法有如下性质

性质一
唯一性:一个n维点值与一个n维系数一一对应

性质二

C(x)=i=02n1(j=0iajbij)xi

=i=02n1j=0i(ajxj)(bijxij)

=j=02n1(ajxj)i=02n1(bixi)

点值下的乘法复杂度是 O(n)

快速傅里叶变换

单位根

满足

xn=1

的复数,称为单位根,记作 ωn
它可以看作是复平面上x轴绕逆时针方向旋转 2πn 的复数
单位根有一些显而易见的性质
ω2m2n=ωmn

ωm2n=ωn+m2n

蝴蝶变换

我们利用单位根作特殊值进行系数表示与点值表示的变换
A0(x)A(x)A1(x)A(x)
那么有

A(ωmn)=A0((ωmn)2)+ωmnA1((ωmn)2)

=A0(ωmn/2)+ωmnA1(ωmn/2)

A(ωm+n/2n)=A0((ωm+n/2n)2)+ωm+n/2nA1((ωm+n/2n)2)

=A0(ωmn/2)+ωm+n/2nA1(ωmn/2)

=A0(ωmn/2)ωmnA1(ωmn/2)

显然 A0,A1 都是子问题,我们知道了它们的点值表示,就能线性地求出 A(x) 的点值表示
每一层对于一个确定的 m ,我们用对应的两个值来更新出当前的值,称这个操作为“蝴蝶变换”
这样我们可以用 O(nlogn) 的时间完成系数到点值的转换

逆离散傅里叶变换

从定义出发,从系数表示到点值表示的变换可以看成下面一个矩阵乘法

 111...11w1nw2n...wn1n1w2nw4n...w2(n1)n...............1wnnw2nn...wn(n1)na0a1a2...an1=y0y1y2...yn1 

假如我们想通过 y 得到 a 关键在于得到左边的矩阵的逆矩阵
这个矩阵是这样的

 111...11w1nw2n...w(n1)n1w2nw4n...w2(n1)n...............1wnnw2nn...wn(n1)n 

这样,我们只需要把代进去的向量翻转一下,然后进行FFT即可

迭代/非迭代

根据上面的理论,我们已经可以进行迭代版的FFT了
但是经过观察,我们发现:

我们每次把当前序列的偶数项,奇数项分别提取出来分别列在左右,这样序列里的每个数最后都会在一个位置上,我们可以通过这个循环把每一项最终的位置算出来

for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));

然后我们就可以实现非迭代的FFT了,相比之下会快很多

你可能感兴趣的:(快速傅里叶变换)