史上最菜 FFT 讲解

回想到学了好多次 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 n1e5
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+m1
那么我们只需要求出 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,yi) 表示一个点的坐标
单位圆:显然一个单位圆上面的点可以被表示成 ( 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 ω2n2k=ωnk,证明显然

w n p ∗ w n q = w n p + q w_n^p*w_n^q=w_n^{p+q} wnpwnq=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=0d1(wdk)iai=i=0d/21(wdk)i2ai2+i=0d/21(wdk)i2+1ai2+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=0d/21(wd/2k)ia1(i)+wdki=0d/21(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} ai2 a i ∗ 2 + 1 a_{i*2+1} ai2+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/21a1(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,...,ad1}
乘上一个矩阵
[ ω 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)d1ωd1(ωd1)2(ωn1)d1............ωdd1(ωdd1)2(ωdd1)d1
考虑手动求它的逆矩阵
注意到下面这个矩阵就满足条件
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)d1ωd1(ωd1)2(ωn1)d1............ωd(d1)(ωd(d1))2(ωd(d1))d1

为什么是它的逆矩阵呢,这里利用到了一个性质

∑ i = 0 d − 1 w d i σ d = [ d ∣ σ ] \frac{\sum_{i=0}^{d-1}w_{d}^{i\sigma} }{d}=[d|\sigma] di=0d1wdiσ=[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=0d1f(ωdi)xi 的话
最后的第 i i i 项系数就是 g ( ω n − i ) g(\omega_n^{-i}) g(ωni)

是不是很妙!

一些小优化

递归版的分治已经凉了(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;
			}
		} 
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值