史上最菜 FFT 讲解

回想到学了好多次 FFT 第一次有感觉的时候,半懵半懵地写了板子
再到现在能熟练背诵 NTT 的板子,发现对 FFT 最初的原理渐渐淡漠了
好在有些东西越学越明白,于是来谈一谈


FFT,快速傅里叶变换

例:给出多项式 A ( x ) , B ( x ) A(x),B(x) A ( x ) B ( x ) A(x)*B(x) n 1 e 5 n\le 1e5
A ( x ) A(x) n n 项, B ( x ) B(x) m m

那么我们将 A ( x ) , B ( x ) A(x),B(x) 用点值表示,求出点值的乘积
显然最后的项数是 n + m 1 n+m-1
那么我们只需要求出 n + m n+m 个点值就可以把乘出来的多项式给高斯消元回去
这部分的复杂度是点值 O ( n 2 ) O(n^2) ,点乘 O ( n ) O(n) ,高斯消元 O ( n 3 ) O(n^3)

好似无法优化。。。

但是傅里叶想出了一种巧妙的方法

引入概念:复数域与单位根

一个坐标系,用 ( x , y i ) (x,y*i) 表示一个点的坐标
单位圆:显然一个单位圆上面的点可以被表示成 ( c o s ( θ ) , s i n ( θ ) i ) (cos(\theta),sin(\theta)*i)
单位根:定义 n n 次单位根 ω n k \omega_n^k 表示把单位圆等分成 n n 的第 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)
在这里插入图片描述
单位根的性质:
ω 2 n 2 k = ω n k \omega_{2*n}^{2*k}=\omega_n^k ,证明显然

w n p w n q = w n p + q w_n^p*w_n^q=w_n^{p+q}
证明:令 ω n p \omega_n^p 的极角为 θ \theta ,即与 x 轴的夹角, ω n q \omega_n^q 的为 α \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)
前面是 c o s ( α + θ ) cos(\alpha+\theta) ,后面就是 s i n ( α + θ ) sin(\alpha+\theta)

证毕!

好的,现在我们把点值带成 ω d k \omega_d^k ,对于 k [ 0 , n + m ] k\in[0,n+m] 求出 f ( ω d k ) f(\omega_d^k)
注意这里的 d d 是第一个 n + m \ge n+m 的二的整次幂方便后面出了

然后
对于 k [ 0 , n + m ] k\in[0,n+m] 求出 f ( ω d k ) g ( ω d k ) = h ( ω d k ) f(\omega_d^k)*g(\omega_d^k)=h(\omega_d^k)
最后将 h h 变回去

大致分为 3 部
第一部分是 O ( n 2 ) O(n^2) 的,考虑优化

注意到
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}
= 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)

其中 a 1 ( i ) , a 2 ( i ) a_1(i),a_2(i) 表示将 a a 按奇偶性分类后的 a i 2 a_{i*2} a i 2 + 1 a_{i*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 ( ω d / 2 k ) f(\omega_{d/2}^k) ,上面那个式子可以化成
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)
注意到这样就可以分治了
这样会有 l o g 2 ( n ) log_2(n) 层,每层需要对 O ( n ) O(n) k k 算出 对应的 f ( ω d k ) f(\omega_d^k)
复杂度 O ( n l o g 2 ( n ) ) O(nlog_2(n))

还需要优化一个转回去的过程
注意到这个变化的本质是一个向量
{ a 0 , a 1 , . . . , a d 1 } \{a_0,a_1,...,a_{d-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}
考虑手动求它的逆矩阵
注意到下面这个矩阵就满足条件
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}

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

i = 0 d 1 w d i σ d = [ d σ ] \frac{\sum_{i=0}^{d-1}w_{d}^{i\sigma} }{d}=[d|\sigma]

证明可以用等比数列,也可以理解为 ω \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 的话
最后的第 i i 项系数就是 g ( ω n i ) g(\omega_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)
只需要对 k [ 0 , d / 2 ] k\in[0,d/2] 求出这样一个 f ( ω d k ) f(\omega_d^k) ,利用
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)
即可求出另一半,可以优化 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;
			}
		} 
	}
}
发布了610 篇原创文章 · 获赞 94 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/103737220
FFT