离散傅里叶变换(DFT)
首先引入DFT式:
V ( j ) = ∑ i = 0 n − 1 a i ω n i j V(j)=\sum_{i=0}^{n-1} a_i \omega_n^{ij} V(j)=∑i=0n−1aiωnij
其中, a i a_i ai 为一长度为 n n n 的多项式 A ( x ) A(x) A(x) 的各项系数,即 A ( x ) = ∑ i = 0 n − 1 a i x i A(x)=\sum_{i=0}^{n-1}a_i x^i A(x)=∑i=0n−1aixi, n n n 也是DFT的模长。 V ( x ) V(x) V(x) 为多项式经过DFT后的多项式。 ω n \omega_n ωn 为 n n n 次单位根,即 ω n = cos ( 2 π n ) + i sin ( 2 π n ) \omega_n = \cos(\frac{2 \pi}{n}) + i \sin(\frac{2 \pi}{n}) ωn=cos(n2π)+isin(n2π), i i i 为虚数单位,即 i 2 = − 1 i^2=-1 i2=−1。此过程可以看做一个向量与范德蒙矩阵相乘。
类似的还有IDFT式:
V ( j ) = 1 n ∑ i = 0 n − 1 a i ω n − i j V(j)=\frac{1}{n} \sum_{i=0}^{n-1} a_i \omega_n^{-ij} V(j)=n1∑i=0n−1aiωn−ij
根据定义,可以用时间复杂度为 O ( n 2 ) O(n^2) O(n2) 的算法解决。
当 log 2 n ∈ Z \log_2n \in \Z log2n∈Z 时,可以用FFT解决该问题。时间复杂度 O ( n log 2 n ) O(n \log_2n) O(nlog2n),空间复杂度 O ( n ) O(n) O(n)。
#include<stdio.h>
#include<cmath>
#define R register int
#define D double
#define I inline
#define N 131072
#define PIE 3.141592653589793
struct Complex{
D Real,Image;
I void Read(){
int x;
scanf("%d",&x);
Real=x;
}
};
I Complex Pair(D A,D B){
Complex res;
res.Real=A;
res.Image=B;
return res;
}
I Complex operator+(Complex A,Complex B){
return Pair(A.Real+B.Real,A.Image+B.Image);
}
I Complex operator-(Complex&A,Complex&B){
return Pair(A.Real-B.Real,A.Image-B.Image);
}
I Complex operator*(Complex A,Complex B){
return Pair(A.Real*B.Real-A.Image*B.Image,A.Image*B.Real+A.Real*B.Image);
}
I void Swap(Complex&A,Complex&B){
Complex Tem;
Tem=A;
A=B;
B=Tem;
}
I void FFT(Complex*A,const int len,const short type){
int tem=0;
for(R i=0;i!=len;i++){
if(i<tem){
Swap(A[i],A[tem]);
}
R j=len;
do{
j>>=1;
tem^=j;
}while(tem<j);
}
static Complex w[N];
w[0]=Pair(1,0);
for(R i=1;i!=len;i<<=1){
Complex omg;
omg=Pair(cos(PIE/i),sin(PIE/i)*type);
for(R j=i-2>>1;j!=-1;j--){
w[j<<1]=w[j];
w[j<<1|1]=w[j]*omg;
}
for(R j=0;j!=len;j+=i<<1){
for(R k=j;k!=i+j;k++){
Complex T1=A[k],T2=A[i+k]*w[k-j];
A[k]=T1+T2;
A[i+k]=T1-T2;
}
}
}
if(type==-1){
D t=1.0/len;
for(R i=0;i!=len;i++){
A[i].Real*=t;
A[i].Image*=t;
}
}
}
int main(){
return 0;
}
当 log 2 n ∉ Z \log_2n \notin \Z log2n∈/Z 时,FFT不再适用。以下有两种解决方法:
1. 卷积
根据计数原理,有 i j = ( i + j 2 ) − ( i 2 ) − ( j 2 ) ij=\binom{i+j}{2}-\binom{i}{2}-\binom{j}{2} ij=(2i+j)−(2i)−(2j),带回DFT式,则有:
ω n ( j 2 ) V ( j ) = ∑ i = 0 n − 1 a i ω n − ( i 2 ) ω n ( i + j 2 ) \omega_n^{\binom{j}{2}}V(j)=\sum_{i=0}^{n-1} a_i \omega_n^{-\binom{i}{2}} \omega_n^{\binom{i+j}{2}} ωn(2j)V(j)=∑i=0n−1aiωn−(2i)ωn(2i+j)
因此利用FFT减法卷积即可。求解IDFT时同理。
时间复杂度 O ( n log 2 n ) O(n \log_2n) O(nlog2n),空间复杂度 O ( n ) O(n) O(n)
若将 i j ij ij 拆为 − ( i − j ) 2 − i 2 − j 2 2 -\frac{(i-j)^2-i^2-j^2}{2} −2(i−j)2−i2−j2 也可以卷积,但存在指数为分数,非常麻烦,甚至有时不存在或无意义。
2. 分治
设 d d d 为 n n n 的最小非 1 1 1约数,设 m = n d , j = p d + q m=\frac{n}{d},j=pd+q m=dn,j=pd+q,其中 0 ⩽ p < m , 0 ⩽ q < d 0 \leqslant p<m,0 \leqslant q<d 0⩽p<m,0⩽q<d。带入DFT式,有:
V ( p d + q ) = ∑ i = 0 n − 1 a i ω n i ( p d + q ) V(pd+q)=\sum_{i=0}^{n-1}a_i \omega_n^{i(pd+q)} V(pd+q)=∑i=0n−1aiωni(pd+q)
将括号展开,由于 ω m d d = ω m \omega_{md}^d=\omega_m ωmdd=ωm,所以:
V ( p d + q ) = ∑ i = 0 n − 1 a i ω m i p ω n i q V(pd+q)=\sum_{i=0}^{n-1}a_i \omega_m^{ip} \omega_n^{iq} V(pd+q)=∑i=0n−1