FFT

应某大巨神·红太阳斯基·FSYo之邀来写FFT的解释。

用途

FFT,快速傅里叶变换,处理两个多项式乘积。

众所周知两个多项式相乘复杂度是FFT的,参照高精度。

而FFT可以降至nlogn。

 

前置知识及概念

一、多项式的表示方法

平常常用的多项式是系数表示法。就是通过将x的幂次方分类,记录系数。

形如FFT

而众所周知一个n次多项式可以被n个点唯一表示。

所以还有一种是点值表示法

形如FFT

而处理两个多项式f和g的卷积时,用系数表示法,是O(n^2)的。

但用点值表示法,可以做到O(n)。就是两个多项式都用同样的一组x来求点值,然后对应相乘。

但很可惜,将系数表示转化成点值表示以及转换回去都是O(n^2)的。所以复杂度没变。

系数表示转到点值表示叫DFT(离散傅里叶变换),反过来叫IDFT(离散傅里叶逆变换)

 

二、复数

扩展复数集,定义i^2=-1。也就是FFT

然后有一个复数域的平面直角坐标系,横坐标是实数,纵坐标是复数。

这样坐标系上每个点都可以用a+bi来表示。

这样的一个点,它的模长定义为FFT

它的共轭复数定义为a-bi,就是后面取反。

复数的四则运算:

加减法参考向量,乘法其实也一样,不过i^2=-1,所以(a+bi)(c+di)=(ac-bd)+(ad+bc)i;

如果你的数学思维够敏锐,你会发现ac-bd和ad+bc神似和差化积的cos和sin两个公式。

所以如果我们用模长+极角的方式来表示复数向量的话,就会得到复数相乘的一个性质。

令(a,β)表示一个模长为a,极角为β的复数点。

则(a,β)*(c,δ)=(ac,β+δ)

俗称模长相乘,极角相加

 

DFT(离散傅里叶变换)

我们在做高精度的时候可以发现,x的幂次方不同导致的多次计算是很tm烦人的。

但如果x的多少次方都不变,那就可以省事。对于一个复数平面,能做到这样省事的,其实就是单位圆上的点。

因为模长相乘,极角相加。只要是单位圆上的点,无论怎么自乘,都只能在圆上面转圈圈。

可以画个图感受一下。

 

我们打算将这个圆等分。接下来,我们会把这个圆分成2的整数幂个部分。

假设分成了n等份。从极角为0(也就是x正半轴那个点开始)的开始,编号为0。得到了0~n-1这n个点。

FFT

我们将k号点表示为FFT。令FFT为单位根。这个东西自乘可以得到圆上所有点。

FFT到底是怎么降低复杂度的呢?就是通过取一些特殊的点来转换点值表示法。

我们选取的点,就是这些等分点。

 

关于这些omega,很明显的有几个性质(想象自己在圆上转圈圈)

FFT

多转几圈就懂了。

 

FFT(快速傅里叶变换)

我们虽然得到了这些点值。但复杂度还是n^2,没有任何用处。

那我们开始推导,怎么做FFT。

提示:再次确定FFT的意义:将系数表达式转换成点值表达式。

再次回顾:我们的多项式一定是2的整数个项数的。不够就补齐。

多项式有多少项,我的omega就分多少个出来。

 

我们要搞的多项式令为FFT

将其按照x的次数的奇偶性分类,得到FFT

倘若我们令两个多项式分别为FFT,FFT

这两个多项式分别有n/2项。

那明显的是FFT

设k,然后将FFT作为x代入F。

FFT

再将FFT代入,得到

FFT

可以手推一下。

那事情很明显了,两个点值只有后面那一坨不一样。

那我们已经得到了朴素的FFT,递归处理版本。代码不放了,因为还有更好的。

复杂度nlog^2n。

 

递归处理是很毒瘤的,凭空多一个log。我们其实还有更好的方法。

很明显,这些系数将在递归过程中被不断按照奇偶性分类,分类在分类,然后变得很混乱。

如果我们一开始就能确定最后谁在哪个地方,就可以省去递归向下的过程,直接从下往上迭代上来。

FFT

持续盗图,,

发现最后位置就是二进制翻转后的地方。

通过for(int i=0;i<limit;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));来运算。

像之前那样一加一减弄上来就行。变成nlogn。

 

IDFT

忘了怎么推得了,,构造omega矩阵的逆矩阵。构造出来就发现其实就是FFT变成FFT

所以就在FFT的时候虚数部分乘-1,然后最后全部除以n就行。

 

大概完了,,代码什么的网上有很多,都挺好看的。