十分简明易懂的FFT(快速傅里叶变换)

快速傅里叶变换 (fast Fourier transform),即利用计算机计算离散傅里叶变换(DFT)的高效、快速计算方法的统称,简称FFT。快速傅里叶变换是1965年由J.W.库利和T.W.图基提出的。采用这种算法能使计算机计算离散傅里叶变换所需要的乘法次数大为减少,特别是被变换的抽样点数N越多,FFT算法计算量的节省就越显著。
FFT(Fast Fourier Transformation) 是离散傅氏变换(DFT)的快速算法。即为快速傅氏变换。它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。

FFTFFT(Fast Fourier Transformation),中文名快速傅里叶变换,用来加速多项式乘法,
朴素高精度乘法时间O(n2)O(n^2),但FFTFFTO(nlog2n)O(nlog{_2}n)的时间解决。

一、多项式的系数表示法和点值表示法

FFTFFT其实是一个用O(nlog2n)O(nlog{_2}n)的时间将一个用系数表示的多项式转换成它的点值表示的算法

1.1 系数表示法

一个n1n-1nn项多项式f(x)f(x)可以表示为:f(x)=i=0n1aixif(x)=\sum_{i=0}^{n-1}{a_i x^i}

也可以用每一项的系数来表示f(x)f(x),即:f(x)={a0,a1,a2,,an1}f(x)=\lbrace{a_0, a_1, a_2,\cdots, a_{n-1}}\rbrace


1.2 点值表示法

  • 把多项式放到平面直角坐标系里面,看成一个函数
  • nn个不同的xx代入,会得出nn个不同的yy,在坐标系内就是nn个不同的点
  • 那么这nn个点唯一确定该多项式,也就是有且仅有一个多项式满足k,f(xk)=yk∀k,f(x_k)=y_k
  • 理由很简单,把nn条式子联立起来成为一个有nn条方程的nn元方程组,每一项的系数都可以解出来

那么f(x)f(x)还可以用 f(x)={(x0,f(x0)),(x1,f(x1)),(x2,f(x2)),,(xn1,f(xn1))}f(x)=\lbrace(x_0,f(x_0)),(x_1,f(x_1)),(x_2,f(x_2)),\cdots,(x_{n−1},f(x_{n−1}))\rbrace 来表示
这就是点值表示法。


1.3 高精度乘法下两种多项式表示法的区别

对于两个用系数表示的多项式,我们把它们相乘,设这两个多项式分别为 A(x),B(x)A(x), B(x)
我们要枚举 AA 每一位系数 与 BB 每一位的系数相乘,那么系数表示法做多项式乘法时间复杂序 O(n2)O(n^2)

但用两个点值表示的多项式相乘,只需要 O(n)O(n) 的时间。

理解如下:
设两个点值多项式分别为:
f(x)={(x0,f(x0)),(x1,f(x1)),(x2,f(x2)),,(xn1,f(xn1))}f(x) = \lbrace(x_0,f(x_0)), (x_1,f(x_1)), (x_2,f(x_2)),\cdots,(x_{n-1},f(x_{n-1}))\rbrace
g(x)={(x0,g(x0)),(x1,g(x1)),(x2,g(x2)),,(xn1,g(xn1))}g(x) = \lbrace(x_0,g(x_0)), (x_1,g(x_1)), (x_2,g(x_2)),\cdots,(x_{n-1},g(x_{n-1}))\rbrace

设它们的乘积是h(x)h(x),那么:
h(x)={(x0,f(x0)g(x0)),(x1,f(x1)g(x1)),(x2,f(x2)g(x2)),,(xn1,f(xn1)g(xn1))}\bf h(x) = \lbrace (x_0, f(x_0)\cdot{g(x_0)}) , (x_1, f(x_1)\cdot{g(x_1)}), (x_2, f(x_2)\cdot{g(x_2)}) , \cdots, (x_{n-1}, f(x_{n-1})\cdot{g(x_{n-1})})\rbrace

所以这里的时间复杂度只有一个枚举的O(n)O(n)

  • 朴素系数转点值的算法叫DFTDFT(离散傅里叶变换),点值转系数叫IDFTIDFT(离散傅里叶逆变换)

二、DFTDFT 前置知识&技能

2.1 复数

我们把形如a+bi(a,b均为实数)的数称为复数,其中a称为实部,b称为虚部,i称为虚数单位。当虚部等于零时,这个复数可以视为实数;当z的虚部不等于零时,实部等于零时,常称z为纯虚数。

初中数学老师会告诉你没有 1\sqrt{-1}, 但仅限 RR 实数域
扩展至复数集 CC,定义 i2=1i^2 = -1,一个复数 zz 可以表示为 z=a+bi(a,bR)z = a + bi(a, b∈R)
其中 aa 为实部,bb 为虚部,ii 称为虚数单位

  • 在复数集中就可以用 ii 表示负数的平方根,如 7=7i\sqrt{-7} = \sqrt{7}i

还可以把复数看作复平面直角坐标系上的一个点,比如下面:
十分简明易懂的FFT(快速傅里叶变换)
这个点(2,3)(2 , 3) 表示的就是复数 2+3i2 + 3i,或者想象它代表的向量(2,3)(2, 3)

一个复数zz 的模定义为它到原点的距离,记为z=a2+b3|z| = \sqrt{a^2 + b^3}
一个复数 z=a+biz = a + bi 的共轭复数为 abia - bi(虚部取反),记为 z=abi\overline z = a - bi

2.2 复数的运算

设两个复数分别为z1=a+bi,z2=c+diz_1=a+bi, z_2=c+di 那么,

z1+z2=(a+c)+(b+d)iz_1 + z_2 = (a + c) + (b + d)i
z1z2=(acbd)+(ad+bc)iz_1\cdot z_2 = (ac - bd) + (ad + bc)i

复数相加也满足平行四边形法则:
十分简明易懂的FFT(快速傅里叶变换)
AB+AD=ACAB + AD = AC

复数相乘还有一个值得注意的小性质: 模长相乘,极角相加
(a1,θ1)(a2,θ2)=(a1a2,θ1+θ2)(a_1, \theta_1 ) \cdot (a_2, \theta_2) = (a_1 a_2, \theta_1 + \theta_2)


三、DFTDFT (离散傅里叶变换)

  • 一定注意从这里开始所有的nn都默认为22的整数次幂

对于任意系数多项式转点值,当然可以随便取任意nnxx值代入计算,
但是暴力计算 xk0,xk1,,xkn1(k[0,n))x_k^0, x_k^1, \cdots, x_k^{n-1} (k \in [0,n)) 当然是O(n2)O(n^2) 的时间。

其实可以代入一组神奇的xx,代入后不用做那么多的次方运算,这些 xx 当然不是乱取的,而且取这些 xx 值应该就是 傅里叶 的主意了。

考虑一下,如果我们代入一些xx,使每个xx的若干次方等于 11,我们就不用做全部的运算了。
±1\bf \pm 1 是可以的,考虑虚数的话 ±i\pm i 也可以,但只有这四个数远远不够。

  • 傅里叶说:这个圆圈上面的点都可以做到
    十分简明易懂的FFT(快速傅里叶变换)

以原点为圆心,画一个半径为11的单位圆,那么单位圆上所有的点都可以经过若干次次方得到11
傅里叶说还要把它给nn等分了,比如此时n=8n=8

十分简明易懂的FFT(快速傅里叶变换)

橙色点为 n=8n = 8时要取的点,从(1,0)(1, 0) 点开始,
逆时针从 00 号开始标号,标到 77 号记偏号为 kk 的点代表的复数为ωnk\omega _n^k
那么由模长相乘,极角相加可知 (ωn1)k=ωnk(\omega _n^1)^k = \omega _n^k,其中 (ωn1)(\omega _n^1) 称为 nn次单位根,而且每一个ω\omega 都可以求出
ωnk=cos(kn2π)+isin(kn2π)\omega _n^k = \cos({\frac k n}2\pi) + i\sin({\frac k n}2\pi)

或者说也可以认这样解释这条式子:
十分简明易懂的FFT(快速傅里叶变换)

注意sin2θ+cos2θ=1{sin^2}θ+{cos^2}θ=1什么的,就容易理解了

那么 ωn0ωn1ωnn1\omega_n^0,\omega_n^1,\cdots,\omega_n^{n-1} 即为我们要代入的 x0x1,x2xn1x_0,x_1, x_2,\cdots,x_{n-1}

3.1 单位根的一些性质

FFTFFT 的过程需要用到 ω\omega 的一些性质
ωnk=ω2n2k\omega _n ^k = \omega _2n ^2k

它位表示的点(或向量) 表示的复数是相同的,证明如下:
ωnk=cosnk2π+isinnk2π=cos2n2k2π+isin2n2k2π=ω2n2k\omega _n ^k = \cos{_n^k}2\pi + i \sin{_n^k}2\pi = \cos{_2n^2k}2\pi + i \sin{_2n^2k}2\pi = \omega _2n ^2k


ωnk+n2=ωnk\bf \omega_n^{k+\frac n 2} = -\omega _n ^k

ωn0=ωnn=1\omega_n^0 = \omega_n^n = 1

eix=cosx+isinxe^{ix} = \cos x + i \sin x
eiπ=cosπ+isinπ=1eiπ+1=0e^{i\pi} = \cos \pi + i \sin \pi = -1 \longrightarrow e^{i\pi} +1 = 0


3.2 FFTFFT (快速傅里叶变换)

虽然 DFTDFT 搞出来一堆很牛逼的 ωω 作为代入多项式的 xx

但只是代入这类特殊xx值法的变换叫做 DFTDFT 而已,还是要代入单位根暴力计算

  • DFTDFT 还是暴力 O(n2)O(n^2)

DFTDFT 可以分治来做,于是 FFTFFT(快速傅里叶变换) 就出来了。

首先设一个多项式A(x)A(x)
A(x)=i=0n1aixi=a0+a1x+a2x2++anxn1A(x)=\sum_{i=0}^{n-1} a_i{x^i} = a_0 + a_1x+a_2x^2 + \cdots + a_n x^{n-1}

A(x)A(x) 下标的奇偶性把 A(x)A(x) 分成两半,右边再提一个xx
十分简明易懂的FFT(快速傅里叶变换)
两边好像非常相似,于是再设两个多项式A1(x)A2(x)A_1(x),A_2(x),令

十分简明易懂的FFT(快速傅里叶变换)
比较明显得出:
A(x)=A1(x2)+xA2(x2)A(x) = A_1(x^2) + xA_2(x^2)

再设 k<n2k < \frac n 2,把 ωnk\omega _n^k 作为 xx 代入A(x)A(x)

十分简明易懂的FFT(快速傅里叶变换)

那么对于 A(ωnk+n2)A(\omega_n^{k+\frac n 2}) 的话,代入 ωnk+n2\omega_n^{k+\frac n 2}

十分简明易懂的FFT(快速傅里叶变换)
我们可以发现

A(ωnk)A(\omega_n^{k})A(ωnk+n2)A(\omega_n^{k+\frac n 2}) 两个多项式后面 一坨只有符号不同,
也就是说,如果已知 A1(ωn2k)A_1(\omega^k_{\frac n 2})A2(ωn2k)A_2(\omega^k_{\frac n 2}) 的值,
我们就可以同时知道 A1(ωnk)A_1(\omega^k_{n})A1(ωnk+n2)A_1(\omega_n^{k+\frac n 2})


每次回溯时只扫当前前面一半的序列,即可得出后面一半序列的答案
n==1n == 1 时只有一个常数项,直接 returnreturn
时间复杂序O(nlog2n)O(n \log _2 n)


3.3 IFFTIFFT (快速傅里叶逆变换)

想一下,我们不仅要会FFTFFT,还要会 IFFTIFFT(快速傅里叶逆变换)

我们把两个多项式相乘 (也叫求卷积),做完两遍FFTFFT 也知道了积的多项式的点值表示,
可我们平时系数表示的多项式,点值表示没有意义。

  • 怎么把点值表示的多项式快速转回系数表示法?

  • IDFTIDFT 暴力O(n2)O(n^2) 做? 其实也可以 FFTFFTO(nlog2n)O(n log_2 n)的时间搞

你有没有想过为什么傅里叶是把ωnk\omega_n^k 作为xx 代入而不是别的什么数?

  • 一个多项式在分治的过程中乘上单位根的共轭复数,分治完的每一项除以 nn 即为原多项式的每一项系数



本文:《十分简明易懂的FFT(快速傅里叶变换)

小学生都能看懂的FFT!!!

浅入浅出理解傅里叶变换
如何理解离散傅里叶变换(一)实数形式傅里叶变换
快速傅里叶变换(FFT)

网上找的纯C实现的FFT,与matlab计算结果完全一样
用c语言实现的FFT