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