回想到学了好多次 FFT 第一次有感觉的时候,半懵半懵地写了板子
再到现在能熟练背诵 NTT 的板子,发现对 FFT 最初的原理渐渐淡漠了
好在有些东西越学越明白,于是来谈一谈
FFT,快速傅里叶变换
例:给出多项式
A(x),B(x) 求
A(x)∗B(x),
n≤1e5
令
A(x) 有
n 项,
B(x) 有
m 项
那么我们将
A(x),B(x) 用点值表示,求出点值的乘积
显然最后的项数是
n+m−1
那么我们只需要求出
n+m 个点值就可以把乘出来的多项式给高斯消元回去
这部分的复杂度是点值
O(n2),点乘
O(n),高斯消元
O(n3)
好似无法优化。。。
但是傅里叶想出了一种巧妙的方法
引入概念:复数域与单位根
一个坐标系,用
(x,y∗i) 表示一个点的坐标
单位圆:显然一个单位圆上面的点可以被表示成
(cos(θ),sin(θ)∗i)
单位根:定义
n 次单位根
ωnk 表示把单位圆等分成
n 的第
k 个点
标号如下,那么有
ωnk=(cos(k2∗π),sin(k2∗π)∗i)
单位根的性质:
ω2∗n2∗k=ωnk,证明显然
wnp∗wnq=wnp+q
证明:令
ωnp 的极角为
θ,即与 x 轴的夹角,
ωnq 的为
α
(cos(θ),sin(θ)∗i)∗(cos(α),sin(α)∗i)=(cos(θ)cos(α)−sin(θ)sin(α),(cos(θ)sin(α)+sin(θ)cos(α))∗i)
前面是
cos(α+θ),后面就是
sin(α+θ)
证毕!
好的,现在我们把点值带成
ωdk,对于
k∈[0,n+m] 求出
f(ωdk)
注意这里的
d 是第一个
≥n+m 的二的整次幂方便后面出了
然后
对于
k∈[0,n+m] 求出
f(ωdk)∗g(ωdk)=h(ωdk)
最后将
h 变回去
大致分为 3 部
第一部分是
O(n2) 的,考虑优化
注意到
f(ωdk)=i=0∑d−1(wdk)i∗ai=i=0∑d/2−1(wdk)i∗2ai∗2+i=0∑d/2−1(wdk)i∗2+1ai∗2+1
=i=0∑d/2−1(wd/2k)ia1(i)+wdk∗i=0∑d/2−1(wd/2k)ia2(i)
其中
a1(i),a2(i) 表示将
a 按奇偶性分类后的
ai∗2 和
ai∗2+1
那么如果我们继续将
f(x)=∑i=0d/2−1a1(i)∗xi 的多项式的话
前面那一项就是
f(ωd/2k),上面那个式子可以化成
f(ωdk)=f1(ωd/2k)+wdkf2(ωd/2k)
注意到这样就可以分治了
这样会有
log2(n) 层,每层需要对
O(n) 个
k 算出 对应的
f(ωdk)
复杂度
O(nlog2(n))
还需要优化一个转回去的过程
注意到这个变化的本质是一个向量
{a0,a1,...,ad−1}
乘上一个矩阵
⎣⎢⎢⎡ωd0(ωd0)2(ωn0)d−1ωd1(ωd1)2(ωn1)d−1............ωdd−1(ωdd−1)2(ωdd−1)d−1⎦⎥⎥⎤
考虑手动求它的逆矩阵
注意到下面这个矩阵就满足条件
d1∗⎣⎢⎢⎢⎡ωd0(ωd0)2(ωn0)d−1ωd−1(ωd−1)2(ωn−1)d−1............ωd−(d−1)(ωd−(d−1))2(ωd−(d−1))d−1⎦⎥⎥⎥⎤
为什么是它的逆矩阵呢,这里利用到了一个性质
d∑i=0d−1wdiσ=[d∣σ]
证明可以用等比数列,也可以理解为
ω 的正负抵消
观察一下矩阵的系数就可以发现只有对角线上是 1
同样分治进行变换即可
也就是说如果令
g(x)=∑i=0d−1f(ωdi)∗xi 的话
最后的第
i 项系数就是
g(ωn−i)
是不是很妙!
一些小优化
递归版的分治已经凉了(T飞),找一下规律可以发现
就是二进制反过来
还有一个优化是
f(ωdk)=f1(ωd/2k)+wdkf2(ωd/2k)
只需要对
k∈[0,d/2] 求出这样一个
f(ωdk),利用
f(ωdk+d/2)=f1(ωd/2k)−wdkf2(ωd/2k)
即可求出另一半,可以优化
1/2 的常数
void fft(cp *a, int inv){
for(int i=0; i<len; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int i = 1; i < len; i <<= 1){
cp wn(cos(PI/i), inv * sin(PI/i));
for(int j = 0; j < len; j += (i<<1)){
cp w = cp(1, 0);
for(int k = 0; k < i; k ++, w = w * wn){
cp x = a[k + j], y = w * a[k + j + i];
a[k + j] = x + y, a[k + j + i] = x - y;
}
}
}
}