前言
其实很早就看懂了FFT是如何工作的
只是懒癌晚期加上各科老师逼着我好好中考所以没时间来写
现在来补一发
由于博主很弱,您无法避免看到:偷懒,智障,意识流
请勿恶意拍打喂食=w=
前置技能
首先你需要知道什么是复数并且会表示复数
简单来说就是我们约定
i=−1−−−√
那么所有的复数都可以表示成
z=x+iy
的形式
于是我们在复平面上就用向量(x,y)来表示复数z
然后是复数的运算
根据需要我们只讨论加减乘三种情况
自己拆开来就会发现了。。。
接着是n次单位复数根
即方程
wn=1
的所有在复数集合中的解,用
win i=0..n−1
表示
不加证明的我们可以发现(其实是因为我不会),这个方程的根有n个,对于k=0..n-1,这些根是
,e2πik/n
并且这些根均匀地分布在复平面以原点为圆心单位长度为半径的圆周上
原谅我盗了张图
那么我们把
w1n
称作主n次单位复数根,可以发现
win=wi−1nw1n
归纳一下就是w是一个以
w1n
为公比的循环的等比数列。
那么我们有
wjnwkn=wj+kmodnn
w−1n=wn−1n
等等性质。
为了方便表示我们用三角函数来表示每一个复数
wkn=cos(2πk/n)+isin(2πk/n)
因为模长是1嘛~~
接着我们有一个消去引理
wdkdn=wkn
这个画张图出来应该就能理解了。
然后证明一个重要的引理—折半引理
(wkn)2所对应的集合是wkn/2,并且每个根恰好出现两次
因为
(wkn)2=(wk+n/2n)2=w2k+nn=w2knwnn=w2kn
再消去一个2就好了
FFT
首先我们的目标是用O(N log N)的时间做次数界为n的多项式乘法
即给出多项式A和B,求出
所以FFT一般用于做卷积运算
然后我们考虑一般的方法。
直接算估计很难优化,我们用点值和插值的方法来计算。
点值就是求出这个多项式在某些点上的值。
插值就是根据这个多项式在某些点上的值求出这个多项式。
简单来说我们需要对A和B都求出2n个点对(xi,ai),(xi,bi)
其中ai=A(xi),bi=B(xi),那么我们可以知道ci=C(xi)
因为C的次数界是2n所以我们至少需要2n个点。
可以发现这样点值和插值的复杂度都是N^2的。
但我们可以通过选择一些特别的数来优化这个算法。
这些数就是我们之前讲过的n次单位复数根
为了方便我们约定n是2的幂数
点值
我们现在需要求出一个多项式A在n次单位复数根上的值。
考虑分治,设原多项式
A=a0+a1x+a2x2+...+an−1xn−1
那么我们构造两个新的多项式
那么我们可以知道 A(x)=A[0](x2)+xA[1](x2)
注意我们的x^2所对应的集合就是 wn/2 所对应的集合,并且每个根都出现了两次。
所以我们可以递归地求出 A[0],A[1] 在 wn/2 上的值,设为 y[0]k,y[1]k
那么我们可以得出 yk=y[0]k+wkny[1]k,yk+n/2=y[0]k−wkny[1]k
原因的话因为 wn/2n=−1 就很显然了。
这样分治的复杂度显然就是O(N log N)了。
这样子的过程我们称作DFT——离散傅里叶变换。
插值
我们可以发现插值其实是点值的逆运算。
如果我们把点值想象成一个矩阵乘法(见下图)
好吧我又盗图了
即DFT的过程表示成
y=Va
那么我们插值也可以用
a=V−1y
即逆DFT来求。
我们可以发现
V−1
中(j,k)的位置的值为
w−jkn/n
证明不会就不写了只需要
VV−1=I
那么设最后乘出来的矩阵为C,那么
Ci,j=1n∑n−1k=0wiknw−kjn=1n∑n−1k=0wk(i−j)n
因为w是一个等比数列,所以我们可以用等比数列求和
设i-j=x,那么
当然当x=0时原式分母就是0了
所以当x=0时得直接算,也就是每一项都是1,加起来就是n,除掉n就是1啦~~
这样就证出来了。
这样的话我们的插值就只需要把每一个位置的
win
变成
w−in
,最后答案再除以n就好了。
但是如果我们真的去实现的话就会发现这样每次赋值数组是不切实际的。
初始化的复杂度很爆炸,所以我们需要:
实现优化
懒得找图直接盗用了=w=
假设我们现在对一个次数界为8的多项式进行DFT,如图
那么我们可以把这个过程看做最下面一层的每个位置还原回去的。
也就是我们只需要把原数组按下标的二进制从高位到低位排序。
继续观察,我们把每个区间叫做1组,那么每次合并都是两组之间的相对位置进行的。
也就是我们只需要枚举组的大小,然后从下往上做就好了。
这样的复杂度就严格O(NlogN)了,o(≧v≦)o~~好棒
到这里我们就把FFT讲(kou)解(hu)完了。
由于博主是蒟蒻有什么讲的不对的地方还请加以指正=w=