FFT(快速傅里叶变换)
前置知识
\(1.复数\)
\(2.单位根\)
\(3.循环结构\)
\(4.C++\)
1.复数
- \(定义:形如a+bi的数,其中i^2=-1\)
- \(计算:1.(a+bi)+(c+di)=(a+c)+(b+d)i\)
\(\ \ \ \ \ \ \ \ \ \ \ 2.(a+bi)-(c+di)=(a-c)+(b-d)i\)
\(\ \ \ \ \ \ \ \ \ \ \ 3.(a+bi)(c+di)=(ac-bd)+(ad+bc)i\) \(向量表示法\)
\(用x表示实部(没有i的部分),用y表示虚部(i前的系数)\)\(性质1.复数乘法满足向量模角相加\)
\(证明:arctan(\frac{a}{b})+arctan(\frac{c}{d})=arctan(\frac{\frac{a}{b}+\frac{c}{d}}{1-\frac{ac}{bd}})=arctan(\frac{ad+bc}{bd-ac})\)
\(是不是巨简单!\)
2.单位根
\(定义:n次单位根是n次幂为1的复数。它们位于复平面的单位圆上,\)
\(构成正n边形的顶点,其中一个顶点是1。\)\(显然,\omega_n^0=\omega_n^n=1,\omega_n=(cos(\frac{2\pi}{n}),sin(\frac{2\pi}{n})),n个复数为\omega_n^1,\omega_n^2...\omega_n^n\)
\(性质:\omega_{n}^k=\omega_{2n}^{2k}\)
正题
\(令A(x)=a_0+a_1x^1...a_nx^n\)
\(则A(x)=(a_0+a_2x^2...a_{2\lfloor\frac{n}{2}\rfloor}x^{2\lfloor\frac{n}{2}\rfloor})+(a_1x+a_3x^3...a_{2\lfloor\frac{n}{2}\rfloor+1}x^{2\lfloor\frac{n}{2}\rfloor+1}),令左边的为A_1(x^2),右边的为xA_2(x^2),A(x)=A_1(x^2)+xA_2(x^2)\)
\(将\omega_n^k(k<\frac{n}{2})带入得\)
\(A(\omega_n^k)=A_1(\omega_n^{2k})+\omega_n^kA_2(\omega_n^{2k})\)
\(将\omega_n^{k+\frac{n}{2}}带入得\)
\(A(\omega_n^k)=A_1(\omega_n^{2k})-\omega_n^kA_2(\omega_n^{2k})\)
\(我们发现2式只有系数不同,所以可以在k\in[0,\frac{n}{2}-1]时顺便把k\in[\frac{n}{2},n-1]计算出来,就可以实现以下时间\)
\[ T(n)=\left\{ \begin{array}{rcl} 2T(\frac{n}{2})+n&& {1
\(时间复杂度为O(nlog_2n)\)
细节
为使序列长度为\(2^m\),把不足位补0
把下标奇偶分类后的位置其实就是2进制下的反转,所以可以通过迭代(非递归)实现,实际效率也是递归版的\(1.5\)~\(2\)倍
\(\mathfrak{Talk\ is\ cheap,show\ you\ the\ code.}\)
#include
#include
#include
#include
using namespace std;
# define read read1()
# define Type template
Type inline T read1(){
T n=0;
char k;
bool fl=0;
do (k=getchar())=='-'&&(fl=1);while('9'a;
public:
Array(){}
void push(int n){a.push_back(n);}
Array(int* l,int* r){while(l!=r)push(*l),++l;}
int size(){return a.size();}
int& operator [] (const int x){return a[x];}
};
void FFT(const int len,vector&a,const int Ty,int *r=NULL){
if(!r){
r=new int[len];
r[0]=0;int L=log2(len);
f(i,0,len-1){
r[i]=(r[i>>1]>>1)|((i&1)<_x(limit+1),_y(limit+1);
Array ans;
f(i,0,n)_x[i]=complex(x[i],0);
f(i,0,m)_y[i]=complex(y[i],0);
FFT(limit,_x,1);
FFT(limit,_y,1);
f(i,0,limit)_x[i]*=_y[i];
FFT(limit,_x,-1);
f(i,0,n+m)ans.push((int)(_x[i].x/limit+0.5));
return ans;
}
void into(int n,Array &x){
f(i,0,n)x.push(read);
}
Array x,y,ans;
int main(){
int n=read,m=read;
into(n,x),into(m,y);
ans=x*y;
f(i,0,n+m)printf("%d ",ans[i]);
return 0;
}