1.什么事FFT?(恼
2.为什么要用FFT?
-
在计算两个多项式相乘的时候,我们要将两个多项式的项数一一对应相乘。
-
所以计算 f ( x ) ∗ g ( x ) f(x)*g(x) f(x)∗g(x) 时,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
-
但是我们可以将函数用另一种表示方法。
-
总所周知,用 n + 1 n+1 n+1 个点可以确定一个 n n n 次的函数(传送门),所以我们不妨写成(以 f ( x ) f(x) f(x)为例)
f ( x ) = { ( x 0 , f ( x 0 ) ) , ( x 1 , f ( x 1 ) ) , . . . , ( x n , f ( x n ) ) } f(x)= \{(x0,f(x0)),(x1,f(x1)),...,(xn,f(xn))\} f(x)={(x0,f(x0)),(x1,f(x1)),...,(xn,f(xn))}
即用坐标表示一个多项式函数。
-
但有什么用呢?
假设我们要求 f ( x ) ∗ g ( x ) f(x)*g(x) f(x)∗g(x)(设 f ( x ) , g ( x ) f(x),g(x) f(x),g(x) 的次数为 n n n)的结果(自然也是一个多项式,设为 h ( x ) h(x) h(x) 罢)
若我们用坐标来求解,那么
h ( x ) = { ( x 0 , f ( x 0 ) ∗ g ( x 0 ) ) , ( x 1 , f ( x 1 ) ∗ g ( x 1 ) ) , . . . , ( x n , f ( x n ) ∗ g ( x n ) ) } h(x)= \{ (x0,f(x0)*g(x0)),(x1,f(x1)*g(x1)),...,(xn,f(xn)*g(xn))\} h(x)={(x0,f(x0)∗g(x0)),(x1,f(x1)∗g(x1)),...,(xn,f(xn)∗g(xn))}
事 O ( n ) O(n) O(n) 的。
-
但是很可惜,多项式表示变换为坐标的时间复杂度事 O ( n 2 ) O(n^2) O(n2) 的(恼
因为你不仅要枚举 n + 1 n+1 n+1 个不同的 x x x ,还要将每一个 x x x 带入。
所以这时候就要用到 F F T FFT FFT
3.FFT代码
已经有人讲的很详细了,再详细也没有用了罢(悲
所以不再赘述了,链接直接贴出来(这事会员制博客,作者受到的惩罚是。。。(悲):
LeeCongWei
【学习笔记】极其美妙的算法——FFT(快速傅里叶变换)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<complex>
#define co complex<double>
#define pi acos(-1.0)
using namespace std;
int n,m;
long long limt=1;
co a[4000200],b[4000200];
int rev[4000200];
void fft(co *c,int inv)
{
for(int i=0;i<limt;i++)
if(i<rev[i]) swap(c[rev[i]],c[i]);
for(int mid=1;mid<limt;mid<<=1)
{
int len=mid*2;
co w(cos(pi/mid),inv*sin(pi/mid));
for(int i=0;i<limt;i+=len)
{
co omega(1,0);
for(int j=0;j<mid;j++)
{
co p1=c[i+j],p2=omega*c[i+j+mid];
c[i+j]=p1+p2;
c[i+j+mid]=p1-p2;
omega=w*omega;
}
}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<=n;i++)
cin>>a[i];
for(int i=0;i<=m;i++)
cin>>b[i];
while(limt<n+m+1)
limt*=2;
for(int i=0;i<limt;i++)
if(i&1)
rev[i]=((rev[i>>1]>>1)+limt/2);
else
rev[i]=(rev[i>>1]>>1);
fft(a,1),fft(b,1);
for(int i=0;i<limt;i++)
a[i]*=b[i];
fft(a,-1);
for(int i=0;i<=n+m;i++)
printf("%d ",(int)(a[i].real()/limt+0.5));
}
作者只是个背板子的食雪汉(悲