「学习笔记」Fast Fourier Transform 快速傅里叶变换

版权声明:本文为hzy原创文章,未经博主允许不可随意转载。 https://blog.csdn.net/Binary_Heap/article/details/82918827

快速傅里叶变换( Fast Fourier Transform,FFT \text{Fast Fourier Transform,FFT} )是一种能在 O ( n log n ) O(n \log n) 的时间内完成多项式乘法的算法,在 O I OI 中的应用很多,是多项式相关内容的基础。下面从头开始介绍 FFT \text{FFT}

前置技能:弧度制、三角函数、平面向量。

多项式

形如 f ( x ) = a 0 + a 1 x + a 2 x 2 + . . . + a n x n f(x)=a_0+a_1x+a_2x^2+...+a_nx^n 的式子称为 x x n n 次多项式。其中 a 0 , a 1 , . . . , a n a_0,a_1,...,a_n 称为多项式的系数。

系数表达法

上面定义中的表示就是系数表达法。其系数可看成 n + 1 n+1 维向量 a = ( a 0 , a 1 , . . . , a n ) \vec a=(a_0,a_1,...,a_n)

点值表达法

把多项式看成一个函数,点值表示就用它图像上的 n + 1 n+1 个不同的点 ( x 0 , y 0 ) , . . . , ( x n , y n ) (x_0,y_0),...,(x_n,y_n) 来确定这个多项式。多项式有不止一个点值表示,可以证明每个点值表示确定唯一的系数表达多项式。

复数

虚数单位

i i 被称为虚数单位。规定 i = 1 i=\sqrt {-1}

复平面

复数的平面由 x , y x,y 轴组成。 x x 轴称为实轴, y y 轴称为虚轴。平面内的每一个从原点到某个点 ( a , b ) (a,b) 的向量 a = ( a , b ) \vec a=(a,b) 表示复数 a + b i a+bi .

复数的模长: a 2 + b 2 \sqrt {a^2+b^2} .实轴到复数向量的转角 θ \theta 称为幅角。

复数的基本运算

  • 复数的加(减)法: ( a + b i ) + ( c + d i ) = ( a + c ) + ( b + d ) i (a+bi)+(c+di)=(a+c)+(b+d)i

  • 复数的乘法: ( a + b i ) ( c + d i ) = ( a c b d ) + ( b c + a d ) i (a+bi)(c+di)=(ac-bd)+(bc+ad)i

  • 一个结论:复数乘法,模长相乘,幅角相加

共轭复数

a + b i a+bi a b i a-bi 互为共轭复数。

单位根

n n 次单位根是满足 z n = 1 z^n=1 n n 个复数,它们均分复平面的单位圆。

这些复数满足模长为 1 1 ,幅角的 n n 倍是 2 π 2\pi 的倍数

根据欧拉公式:

欧拉公式 e x i = cos x + i sin x e^{xi}=\cos x+i \sin x ,其中 e e 为自然对数的底数, i i 为虚数单位。

可得 n n 次单位根为 e 2 π k i n , k [ 0 , n 1 ] e^{\frac{2\pi ki}{n}},k\in [0,n-1]

得:记 ω n = e 2 π i n , \omega_n=e^{\frac{2\pi i}{n}}, n n 次单位根为 ω n 0 , . . . , ω n n 1 \omega_n^0,...,\omega_n^{n-1}

单位根的性质

性质 1 1 :根据定义得到: ω 2 n 2 k = ω n k \omega_{2n}^{2k}=\omega_{n}^{k}

性质 2 2 ω n n 2 + k = ω n k \omega_{n}^{\frac{n}{2}+k}=-\omega_n^k

证明:

ω n n 2 = e 2 π i n n 2 = e π i = cos π + i sin π = 1 \omega_{n}^{\frac{n}{2}}=e^{\frac{2\pi i}{n} \frac{n}{2}}=e^{\pi i}=\cos \pi+i \sin \pi=-1

ω n n 2 + k = ω n n 2 ω n k = ω n k \omega_{n}^{\frac{n}{2}+k}=\omega_{n}^{\frac{n}{2}}\omega_{n}^{k}=-\omega_n^k

Fast Fourier Transform

多项式乘法

系数表达的多项式乘法: c ( x ) = a ( x ) b ( x ) c(x)=a(x)b(x) ,则 c ( x ) = i = 0 2 n c i x i c(x)=\sum_{i=0}^{2n} c_i x^i

其中 c i = j = 0 n a j b i j c_i=\sum_{j=0}^{n}a_jb_{i-j} .

时间复杂度 O ( n 2 ) O(n^2)

点值表达的多项式乘法:

时间复杂度 O ( n ) O(n)

因此多项式乘法的基本思路是先插值得到点值表达,再 O ( n ) O(n) 乘,最后求值得到系数表达。

DFT

n n 次单位根 ω n 0 , . . . , ω n n 1 \omega_n^0,...,\omega_n^{n-1} 带入多项式 A ( x ) = a 0 + a 1 x + . . . + a n x n A(x)=a_0+a_1x+...+a_nx^n

得到点值向量 y = ( A ( ω n 0 ) , A ( ω n 1 ) , . . . , A ( ω n n 1 ) ) \vec y=(A(\omega_n^0),A(\omega_n^1),...,A(\omega_n^{n-1}))

称为系数向量 a = ( a 0 , a 1 , . . . , a n ) \vec a=(a_0,a_1,...,a_n) 离散傅里叶变换 Discrete Fourier Transform, DFT \text{Discrete Fourier Transform, DFT} ),写作 y = DFT n ( a ) \vec y=\text{DFT}_n(\vec a)

直接求 DFT \text{DFT} O ( n 2 ) O(n^2) 的。 FFT \text{FFT} 的常用算法 Cooley-Tukey \text{Cooley-Tukey} 使用分治方法做到 O ( n log n ) O(n\log n) .

以下讨论基于 n = 2 m , m N n=2^m,m \in \N ,若不足则高位系数补 0 0 .

考虑点值向量的第 k + 1 k+1 维:

A ( ω n k ) = i = 0 n 1 a i ( ω n k ) i = i = 0 n 1 a i ω n k i A(\omega_{n}^{k})=\sum_{i=0}^{n-1}a_i(\omega_{n}^{k})^{i}=\sum_{i=0}^{n-1}a_i\omega_{n}^{ki}

= i = 0 n 2 1 a 2 i ω n 2 k i + i = 0 n 2 1 a 2 i ω n 2 k i + k =\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki}+\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki+k}

= i = 0 n 2 1 a 2 i ω n 2 k i + ω n k i = 0 n 2 1 a 2 i ω n 2 k i =\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki}+\omega_{n}^{k}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki}

利用性质1: ω 2 n 2 k = ω n k \omega_{2n}^{2k}=\omega_{n}^{k}

k < n 2 k<\frac{n}{2} 时:

A ( ω n k ) = i = 0 n 2 1 a 2 i ω n 2 k i + ω n k i = 0 n 2 1 a 2 i ω n 2 k i A(\omega_{n}^{k})=\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}+\omega_{n}^{k}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}

并且可以推出:

A ( ω n k + n 2 ) = ( 1 ) n 2 i = 0 n 2 1 a 2 i ω n 2 k i + ( 1 ) n 2 ω n k + n 2 i = 0 n 2 1 a 2 i ω n 2 k i A(\omega_{n}^{k+\frac{n}{2}})=(-1)^{\frac{n}{2}}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}+(-1)^{\frac{n}{2}}\omega_{n}^{k+\frac{n}{2}}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}

利用性质2: ω n n 2 + k = ω n k \omega_{n}^{\frac{n}{2}+k}=-\omega_n^k

A ( ω n k + n 2 ) = i = 0 n 2 1 a 2 i ω n 2 k i ω n k i = 0 n 2 1 a 2 i ω n 2 k i A(\omega_{n}^{k+\frac{n}{2}})=\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}-\omega_n^k\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}

上面把求和分成 0 , 2 , . . . , n 2 0,2,...,n-2 1 , 3 , . . . , n 1 1,3,...,n-1 两部分,把大小为 n n 的问题转化成两个规模为 n 2 \frac{n}{2} 的子问题,可以进行分治求解了。

IDFT

求值过程使用离散傅里叶逆变换 Inverse Discrete Fourier Transform, IDFT \text{Inverse Discrete Fourier Transform, IDFT}

结论:只要把 DFT \text{DFT} ω n \omega_n 都取倒数,最后除以 n n 即可。

证明:

Y = ( y 0 , y 1 , . . . , y n ) \vec Y=(y_0,y_1,...,y_n) A = ( a 0 , a 1 , . . . , a n ) \vec A = (a_0,a_1,...,a_n) 的离散傅里叶变换。

考虑一个向量: C = ( c 0 , c 1 , . . . , c n ) \vec C=(c_0,c_1,...,c_n) 满足 c k = i = 0 n 1 y i ( ω n k ) i c_k=\sum_{i=0}^{n-1}y_i(\omega_n^{-k})^i

(即 C \vec C 是多项式 Y \vec Y ω n 0 , ω n 1 , . . . , ω n ( n 1 ) \omega_n^0,\omega_n^{-1},...,\omega_n^{-(n-1)} 处的点值)

将上式展开:

c k = i = 0 n 1 y i ( ω n k ) i c_k=\sum_{i=0}^{n-1}y_i(\omega_n^{-k})^i

= i = 0 n 1 ( j = 0 n 1 a j ( ω n i ) j ) ( ω n k ) i =\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j(\omega_n^i)^j)(\omega_n^{-k})^i

= i = 0 n 1 ( j = 0 n 1 a j ( ω n j ) i ) ( ω n k ) i =\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j(\omega_n^j)^i)(\omega_n^{-k})^i

= i = 0 n 1 j = 0 n 1 a j ( ω n j ) i ( ω n k ) i =\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(\omega_n^j)^i(\omega_n^{-k})^i

= i = 0 n 1 j = 0 n 1 a j ( ω n j k ) i =\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(\omega_n^{j-k})^i

= j = 0 n 1 a j ( i = 0 n 1 ( ω n j k ) i ) =\sum_{j=0}^{n-1}a_j(\sum_{i=0}^{n-1}(\omega_n^{j-k})^i)

考虑一个前缀和 S ( ω n k ) = 1 + ω n k + ( ω n k ) 2 + . . . + ( ω n k ) n 1 S(\omega_n^k)=1+\omega_n^k+(\omega_n^k)^2+...+(\omega_n^k)^{n-1}

( ω n k ) ̸ = 1 (\omega_n^k)\not = 1 k ̸ = 0 k\not = 0 时,使用等比数列求和方法:

ω n k S ( ω n k ) = ω n k + ( ω n k ) 2 + ( ω n k ) 3 + . . . + ( ω n k ) n \omega_n^kS(\omega_n^k)=\omega_n^k+(\omega_n^k)^2+(\omega_n^k)^3+...+(\omega_n^k)^{n}

ω n k S ( ω n k ) S ( ω n k ) = ( ω n k ) n 1 \omega_n^kS(\omega_n^k)-S(\omega_n^k)=(\omega_n^k)^{n}-1

S ( ω n k ) = ( ω n k ) n 1 ω n k 1 S(\omega_n^k)=\frac{(\omega_n^k)^{n}-1}{\omega_n^k-1}

分母不为 0 0 ,分子 ( ω n k ) n 1 = ( ω n n ) k 1 = 1 k 1 = 0 (\omega_n^k)^{n}-1=(\omega_n^n)^{k}-1=1^{k}-1=0

因此 k ̸ = 0 k\not = 0 时, S ( ω n k ) = 0 S(\omega_n^k)=0

k = 0 k=0 时, S ( ω n k ) = n ( ω n k ) 0 = n S(\omega_n^k)=n(\omega_n^k)^0=n

继续考虑刚刚那个式子

c k = j = 0 n 1 a j ( i = 0 n 1 ( ω n j k ) i ) c_k=\sum_{j=0}^{n-1}a_j(\sum_{i=0}^{n-1}(\omega_n^{j-k})^i)

只有 j k = 0 j-k=0 i = 0 n 1 ( ω n j k ) i \sum_{i=0}^{n-1}(\omega_n^{j-k})^i 才为 n n ,否则为 0 0

c k = a k n c_k=a_kn

得到结论: a k = c k n a_k=\frac{c_k}{n}

再次总结一下,离散傅里叶逆变换就是先求出多项式在 ω n 0 , ω n 1 , . . . , ω n ( n 1 ) \omega_n^0,\omega_n^{-1},...,\omega_n^{-(n-1)} 处的点值表示,再每一项除以 n n

递归版代码

//Luogu P3803 多项式乘法
#include <complex>
#include <cstdio>
using namespace std;

typedef complex<double> comp;

const int N = (1 << 20) + 10 << 1;
const double PI2 = 2.0 * acos(-1.0);

int read() {
	int x = 0; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar()) ;
	for(; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + (c & 15);
	return x;
}

int n, m;
comp a[N], b[N];

void fft(int n, comp * a, int type) {
	if(n == 1) return ;
	comp a1[n >> 1], a2[n >> 1];
	for(int i = 0; i < n; i += 2)
		a1[i >> 1] = a[i], a2[i >> 1] = a[i + 1];
	fft(n >> 1, a1, type), fft(n >> 1, a2, type);
	comp w(1, 0), wn(cos(PI2 / n), type * sin(PI2 / n));
	for(int i = 0; i < n >> 1; i ++, w *= wn)
		a[i] = a1[i] + w * a2[i], 
		a[i + (n >> 1)] = a1[i] - w * a2[i];
}

int main() {
	n = read(), m = read();
	for(int i = 0; i <= n; i ++) a[i] = read();
	for(int i = 0; i <= m; i ++) b[i] = read();
	
	int lim = 1;
	for(; lim <= n + m; lim <<= 1) ;
	
	fft(lim, a, 1), fft(lim, b, 1);
	for(int i = 0; i <= lim; i ++) a[i] *= b[i];
	fft(lim, a, -1);
	
	for(int i = 0; i <= n + m; i ++)
		printf("%d ", (int)(0.5 + a[i].real() / lim));
	return 0;
}

FFT 的优化

迭代优化

蝴蝶操作

待更。qwq

猜你喜欢

转载自blog.csdn.net/Binary_Heap/article/details/82918827