标签:fft
??其实我不知道我是否真的理解了FFT,但是我会用FFT优化多项式乘法了QAQ。。
(以下大多摘自算导
前置知识
1. 多项式
??在一个代数域F上,关于变量x的多项式定义为形式和形式表示的函数
2. 多项式的次数界
??若多项式有非零系数的最高次项为
3. 多项式的表示
(1)系数表示法
??对于一个次数界为n的多项式A(x)来说,其系数表示法可以看做是一个列向量
??另外,加法运算的时间复杂度是
(2)点值表示法
?? 有n个点
??已知点值表示求系数表示称为插值,用拉格朗日插值法可以做到
??若次数界为n的多项式A(x),B(x)的n个点的
进入正题。。
1. 单位复根
??n次单位复根是满足
平面的原点为圆心的单位圆上,为
??因为有
重要性质
相消引理
?? 对任何整数
?? 推论:对任意偶数
折半引理
?? 如果
求和引理
??对于任意整数
证明:
?? 原式
2. DFT
??A(x)是一个次数界为n的多项式,不失一般性地假定n是2的幂,因为
次数界总是可以增大的。有列向量
3. 快速傅里叶变换(FFT)
??FFT是一种可以在
是分治。
??定义
所有偶数下标的系数,
所以用
?? 求出
是范德蒙特矩阵。
定理:
证明:
??那么有
??写的时候发现非递归要比递归快很多。。
递归:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<memory.h>
#define N 400010
using namespace std;
const double pi=acos(-1);
struct complex{
double x,i;
complex(){}
complex(double x,double i):x(x),i(i){}
complex operator+(complex a) {return complex(x+a.x,i+a.i);}
complex operator-(complex a) {return complex(x-a.x,i-a.i);}
complex operator*(complex a) {return complex(x*a.x-i*a.i,x*a.i+i*a.x);}
}a[N],b[N];
int n,m,i,nn;
void fft(complex *a,int n,int t)
{
if (n==1) return;
complex a0[n>>1],a1[n>>1];
for (int i=0;i<n;i+=2) a0[i>>1]=a[i],a1[i>>1]=a[i+1];
fft(a0,n>>1,t);fft(a1,n>>1,t);
complex wn(cos(2*pi/n),t*sin(2*pi/n)),w(1,0);
for (int i=0;i<(n>>1);i++,w=w*wn) a[i]=a0[i]+w*a1[i],a[i+(n>>1)]=a0[i]-w*a1[i];
}
int main()
{
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));memset(b,0,sizeof(b));
for (i=0;i<=n;i++) scanf("%lf",&a[i].x);
for (i=0;i<=m;i++) scanf("%lf",&b[i].x);
nn=1;while (nn<=n+m) nn<<=1;
fft(a,nn,1);fft(b,nn,1);
for (i=0;i<=nn;i++) a[i]=a[i]*b[i];
fft(a,nn,-1);
for (i=0;i<=n+m;i++) printf("%d ",(int)(a[i].x/nn+0.5));
}
非递归:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<memory.h>
#define N 400010
using namespace std;
const double pi=acos(-1);
struct complex{
double x,i;
complex(){}
complex(double x,double i):x(x),i(i){}
complex operator+(complex a) {return complex(x+a.x,i+a.i);}
complex operator-(complex a) {return complex(x-a.x,i-a.i);}
complex operator*(complex a) {return complex(x*a.x-i*a.i,x*a.i+i*a.x);}
}a[N],b[N];
int n,m,i,nn,len,rev[N];
void fft(complex *a,int n,int t)
{
for (int i=0;i<n;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
for (int j=1;j<n;j<<=1)
{
complex wn(cos(2*pi/(j<<1)),t*sin(2*pi/(j<<1)));
for (int i=0;i<n;i+=(j<<1))
{
complex w(1,0),t0,t1;
for (int k=0;k<j;k++,w=w*wn) t0=a[i+k],t1=w*a[i+j+k],a[i+k]=t0+t1,a[i+j+k]=t0-t1;
}
}
}
int main()
{
freopen("FFT.in","r",stdin);
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));memset(b,0,sizeof(b));
for (i=0;i<=n;i++) scanf("%lf",&a[i].x);
for (i=0;i<=m;i++) scanf("%lf",&b[i].x);
nn=1;len=0;while (nn<=n+m) nn<<=1,len++;
rev[0]=0;
for (i=1;i<nn;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
fft(a,nn,1);fft(b,nn,1);
for (i=0;i<=nn;i++) a[i]=a[i]*b[i];
fft(a,nn,-1);
for (i=0;i<=n+m;i++) printf("%d ",(int)(a[i].x/nn+0.5));
}
??非递归这里有一个翻转的函数,意在让所有数按合并时候的二叉树的叶子节点的顺序排列,不难发现翻转过来就是它的新位置。。
标签:fft
原文地址:http://blog.csdn.net/tag_king/article/details/46351821