快速傅里叶变换 (fast Fourier transform),即利用计算机计算离散傅里叶变换(DFT)的高效、快速计算方法的统称,简称FFT。快速傅里叶变换是1965年由J.W.库利和T.W.图基提出的。采用这种算法能使计算机计算离散傅里叶变换所需要的乘法次数大为减少,特别是被变换的抽样点数N越多,FFT算法计算量的节省就越显著。
FFT(Fast Fourier Transformation) 是离散傅氏变换(DFT)的快速算法。即为快速傅氏变换。它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。
FFT(Fast Fourier Transformation),中文名快速傅里叶变换,用来加速多项式乘法,
朴素高精度乘法时间O(n2),但FFT能O(nlog2n)的时间解决。
一、多项式的系数表示法和点值表示法
FFT其实是一个用O(nlog2n)的时间将一个用系数表示的多项式转换成它的点值表示的算法
1.1 系数表示法
一个n−1次n项多项式f(x)可以表示为:f(x)=∑i=0n−1aixi
也可以用每一项的系数来表示f(x),即:f(x)={a0,a1,a2,⋯,an−1}
1.2 点值表示法
- 把多项式放到平面直角坐标系里面,看成一个函数
- 把n个不同的x代入,会得出n个不同的y,在坐标系内就是n个不同的点
- 那么这n个点唯一确定该多项式,也就是有且仅有一个多项式满足∀k,f(xk)=yk
- 理由很简单,把n条式子联立起来成为一个有n条方程的n元方程组,每一项的系数都可以解出来
那么f(x)还可以用 f(x)={(x0,f(x0)),(x1,f(x1)),(x2,f(x2)),⋯,(xn−1,f(xn−1))} 来表示
这就是点值表示法。
1.3 高精度乘法下两种多项式表示法的区别
对于两个用系数表示的多项式,我们把它们相乘,设这两个多项式分别为 A(x),B(x),
我们要枚举 A 每一位系数 与 B 每一位的系数相乘,那么系数表示法做多项式乘法时间复杂序 O(n2)。
但用两个点值表示的多项式相乘,只需要 O(n) 的时间。
理解如下:
设两个点值多项式分别为:
f(x)={(x0,f(x0)),(x1,f(x1)),(x2,f(x2)),⋯,(xn−1,f(xn−1))}
g(x)={(x0,g(x0)),(x1,g(x1)),(x2,g(x2)),⋯,(xn−1,g(xn−1))}
设它们的乘积是h(x),那么:
h(x)={(x0,f(x0)⋅g(x0)),(x1,f(x1)⋅g(x1)),(x2,f(x2)⋅g(x2)),⋯,(xn−1,f(xn−1)⋅g(xn−1))}
所以这里的时间复杂度只有一个枚举的O(n)
- 朴素系数转点值的算法叫DFT(离散傅里叶变换),点值转系数叫IDFT(离散傅里叶逆变换)
二、DFT 前置知识&技能
2.1 复数
我们把形如a+bi(a,b均为实数)的数称为复数,其中a称为实部,b称为虚部,i称为虚数单位。当虚部等于零时,这个复数可以视为实数;当z的虚部不等于零时,实部等于零时,常称z为纯虚数。
初中数学老师会告诉你没有 −1, 但仅限 R 实数域
扩展至复数集 C,定义 i2=−1,一个复数 z 可以表示为 z=a+bi(a,b∈R)
其中 a 为实部,b 为虚部,i 称为虚数单位
- 在复数集中就可以用 i 表示负数的平方根,如 −7=7i
还可以把复数看作复平面直角坐标系上的一个点,比如下面:

这个点(2,3) 表示的就是复数 2+3i,或者想象它代表的向量(2,3)
一个复数z 的模定义为它到原点的距离,记为∣z∣=a2+b3
一个复数 z=a+bi 的共轭复数为 a−bi(虚部取反),记为 z=a−bi
2.2 复数的运算
设两个复数分别为z1=a+bi,z2=c+di 那么,
z1+z2=(a+c)+(b+d)i
z1⋅z2=(ac−bd)+(ad+bc)i
复数相加也满足平行四边形法则:

即 AB+AD=AC
复数相乘还有一个值得注意的小性质: 模长相乘,极角相加
(a1,θ1)⋅(a2,θ2)=(a1a2,θ1+θ2)
三、DFT (离散傅里叶变换)
- 一定注意从这里开始所有的nn都默认为22的整数次幂
对于任意系数多项式转点值,当然可以随便取任意n个x值代入计算,
但是暴力计算 xk0,xk1,⋯,xkn−1(k∈[0,n)) 当然是O(n2) 的时间。
其实可以代入一组神奇的x,代入后不用做那么多的次方运算,这些 x 当然不是乱取的,而且取这些 x 值应该就是 傅里叶 的主意了。
考虑一下,如果我们代入一些x,使每个x的若干次方等于 1,我们就不用做全部的运算了。
±1 是可以的,考虑虚数的话 ±i 也可以,但只有这四个数远远不够。
- 傅里叶说:这个圆圈上面的点都可以做到
以原点为圆心,画一个半径为1的单位圆,那么单位圆上所有的点都可以经过若干次次方得到1,
傅里叶说还要把它给nn等分了,比如此时n=8

橙色点为 n=8时要取的点,从(1,0) 点开始,
逆时针从 0 号开始标号,标到 7 号记偏号为 k 的点代表的复数为ωnk,
那么由模长相乘,极角相加可知 (ωn1)k=ωnk,其中 (ωn1) 称为 n次单位根,而且每一个ω 都可以求出
ωnk=cos(nk2π)+isin(nk2π)
或者说也可以认这样解释这条式子:

注意sin2θ+cos2θ=1什么的,就容易理解了
那么 ωn0,ωn1,⋯,ωnn−1 即为我们要代入的 x0,x1,x2,⋯,xn−1
3.1 单位根的一些性质
推FFT 的过程需要用到 ω 的一些性质
ωnk=ω2n2k
它位表示的点(或向量) 表示的复数是相同的,证明如下:
ωnk=cosnk2π+isinnk2π=cos2n2k2π+isin2n2k2π=ω2n2k
ωnk+2n=−ωnk
ωn0=ωnn=1
eix=cosx+isinx
eiπ=cosπ+isinπ=−1⟶eiπ+1=0
3.2 FFT (快速傅里叶变换)
虽然 DFT 搞出来一堆很牛逼的 ω 作为代入多项式的 x 值
但只是代入这类特殊xx值法的变换叫做 DFT 而已,还是要代入单位根暴力计算
-
DFT 还是暴力 O(n2)
但 DFT 可以分治来做,于是 FFT(快速傅里叶变换) 就出来了。
首先设一个多项式A(x)
A(x)=i=0∑n−1aixi=a0+a1x+a2x2+⋯+anxn−1
按 A(x) 下标的奇偶性把 A(x) 分成两半,右边再提一个x

两边好像非常相似,于是再设两个多项式A1(x),A2(x),令

比较明显得出:
A(x)=A1(x2)+xA2(x2)
再设 k<2n,把 ωnk 作为 x 代入A(x):

那么对于 A(ωnk+2n) 的话,代入 ωnk+2n

我们可以发现
A(ωnk) 和 A(ωnk+2n) 两个多项式后面 一坨只有符号不同,
也就是说,如果已知 A1(ω2nk) 和 A2(ω2nk) 的值,
我们就可以同时知道 A1(ωnk) 和 A1(ωnk+2n)
每次回溯时只扫当前前面一半的序列,即可得出后面一半序列的答案
n==1 时只有一个常数项,直接 return
时间复杂序O(nlog2n)
3.3 IFFT (快速傅里叶逆变换)
想一下,我们不仅要会FFT,还要会 IFFT(快速傅里叶逆变换)
我们把两个多项式相乘 (也叫求卷积),做完两遍FFT 也知道了积的多项式的点值表示,
可我们平时系数表示的多项式,点值表示没有意义。
你有没有想过为什么傅里叶是把ωnk 作为x 代入而不是别的什么数?
- 一个多项式在分治的过程中乘上单位根的共轭复数,分治完的每一项除以 n 即为原多项式的每一项系数
本文:《十分简明易懂的FFT(快速傅里叶变换)》
《小学生都能看懂的FFT!!!》
《浅入浅出理解傅里叶变换》
《如何理解离散傅里叶变换(一)实数形式傅里叶变换》
《快速傅里叶变换(FFT)》
《网上找的纯C实现的FFT,与matlab计算结果完全一样》
《用c语言实现的FFT》