快速傅里叶变换(FFT)
这几天在家没事慢慢研究了一下集训最后的一个专题FFT, 陈犇讲完还是有点不是很懂, 网上的很多资料都讲的不是很清晰, 由于这个算法太美了, 我有必要写点什么东西表示对前人智慧的赞美! (顺便练练打公式)
1.FFT这玩意是什么?有什么用?
FFT ( fast Fourier transform )的中文名是快速傅里叶变换, 在信号领域有很大的作用, 在ACM中我们常用FFT来求解多项式乘法
举个例子:
y1=2x4+3x2+5x+7
y2=5x3+7x2+11
FFT就是用来快速地计算
y1⋅y2
所得到的多项式
(
y1⋅y2=10x7+14x6+15x5+68x4+70x3+82x2+55x+77
)
2.FFT预备知识
1.多项式
1.多项式定义
A(x)=a0+a1x+a2x2+⋯+an−2xn−2+an−1xn−1
2.多项式表达方式
- 系数表达: (a0,a1,a2,⋯,an−2,an−1)
- 点值表达:选取
n
个(或以上)不同的值:
x0,x1,...xn−2,xn−1 , 对多项式 A(x) 求值得到 A(x0),A(x1),⋅⋅⋅,A(xn−1)
那么就称 {(xi,A(xi)),0≤i<n,i∈Z} 为多项式 A(x) 的点值表示
举个例子:
f(x)=2+3x+x2
这个多项式的系数表达式就是
(2,3,1)
, 点值表达式就是
{(0,2),(1,6),(2,12)}
或者
{(1,6),(2,12),(3,20),(4,30)}
n−1
次多项式只要选取任意不同的
n
个点(或以上)都是可以的
显然不管是点值表达式还是系数表达式都可以唯一确定一个
3.多项式的乘法
- 系数表达式下的乘法
A(x)=∑i=0n−1aixi,B(x)=∑i=0n−1bixi
那么 A(x)⋅B(x) 就可以表示为
C(x)=∑i=02n−2(∑j=0iajbi−j)xi - 点值表达式下的乘法
{xi,C(xi)}={xi,A(xi)⋅B(xi)}
举个例子:
已知
f1(x)=x2+1,f2(x)=x+3
f1(x)
的点值表达式是
{(0,1),(1,2),(2,5),(3,10)}
f2(x)
的点值表达式是
{(0,3),(1,4),(2,5),(3,6)}
那么
f1(x)⋅f2(x)
的点值表达式就是
{(0,3),(1,8),(2,25),(3,60)}
2.单位复根
0.引入
先不考虑什么叫做单位复根
先在平面直角坐标系下画一个单位圆, 即
{(x,y)|x2+y2=1}
现在我们要在这个圆上取
n
个点
举个例子:
当
n=3
时, 这些点的坐标按照逆时针顺序分别是
当 n=4 时, 这些点的坐标按照 逆时针顺序分别是
当 n=8 时, 这些点的坐标按照 逆时针顺序分别是
如果我们把 平面直角坐标系看做是 复平面, 那么上面那些 点就变成了一个 复数, 它们正是 单位复根
1.单位复根定义
方程
xn=1
在复数域内一定有
n
个不同的解
那么规定:
也就是对应了上面我们从圆上逆时针选取的
n
个点中的第
举个例子:
w02=1
(x2=1
的第
1
个根是
w12=1
(x2=1
的第
2
个根是
w04=1
(x4=1
的第
1
个根是
w14=i
(x4=1
的第
2
个根是
w24=1
(x4=1
的第
3
个根是
w34=i
(x4=1
的第
4
个根是
w08=1
(x8=1
的第
1
个根是
w18=i
(x8=1
的第
2
个根是
w28=1
(x8=1
的第
3
个根是
w38=i
(x8=1
的第
4
个根是
w48=1
(x8=1
的第
5
个根是
w58=i
(x8=1
的第
6
个根是
w68=1
(x8=1
的第
7
个根是
w78=i
(x8=1
的第
8
个根是
我们不难归纳得到:
规定: 当 k≥n 时, wkn=wk%nn
例如: w64=w24,w208=w48
2.单位复根性质
下面的只讨论
n
是
画图我们可以很容易的证明
例如: (w38)2=w34 , (w68)2=w64=w24
3.FFT原理
用一句话来说就是:将原多项式的系数表达式转换成点值表达式 (O(nlog2n)) , 再将多项式的点值表达式相乘得到目标表达式的点值表达式 (O(n)) , 再将点值表达式转换成为系数表达式 (O(nlog2n))
为了能够在
O(nlog2n)
时间内得到多项式的点值表达式, FFT采取了一些小技巧, 选择
n
个单位复根进行求值, 由于单位复根的一些性质, 我们可以在
现在我们有一个最高次幂是
m
的多项式:
1.先将这个多项式的最高次数扩充到 2 的次方
2.分别带入 n 个不同的单位复根
可以写成一个矩阵:
没错我们就是要在 O(nlog2n) 时间内得到 A(w0n),A(w1n),A(w2n),⋯,A(wn−2n),A(wn−1n) 这 n 个值 ! ! !
3.单独考虑其中任意一个方程
3.将奇数项和偶数项分开
得到
4.假设两个新的多项式:
A0(x)=a0+a2x+⋯+an−2xn2−1
A1(x)=a1+a3x+⋯+an−1xn2−1
那么 A(wkn)=A0(wkn/2)+wknA1(wkn/2)
我们只要再次用同样的方法计算 A0(x) 以及 A1(x) 就可以在 O(nlog2n) 时间内求出 A(x) 的所有点值表达式了
举个例子:
多项式
f(x)=a0+a1x+a2x2+a3x3
, 我们要计算
A(w04),A(w14),A(w24),A(w34)
这4个点的值
1.
A(w04)=A0(w02)+w04A1(w02)
A(w14)=A0(w12)+w14A1(w12)
A(w24)=A0(w02)+w24A1(w02)
A(w34)=A0(w12)+w34A1(w12)
2.
A(w02)=A0(w01)+w02A1(w01)
A(w12)=A0(w01)+w12A1(w01)
3.
因为
w01=1
所以可以直接得到它的值, 也就是当前的系数
上述过程就称为 离散傅里叶变换(Discrete Fourier Transform)
我们将多个多项式的点值表达式相乘, 获得了目标表达式的点值表达式
那么: 怎么获得目标表达式的系数表达式呢?
下面我们将要介绍 逆离散傅里叶变换(Inverse Discrete Fourier Transform)
上面的矩阵就是 DFT 的过程, 下面的矩阵就是 IDFT 的过程
其实差异并不大, 我们只需要将原来DFT中 wkn 换成 w−kn ,结果再乘上 1n 就从 DFT 变成了 IDFT