fft总结
引入
对于两个多项式,我们可以在 O(n2) 的时间复杂度内求出他们的乘积(即卷积)
但如果 n≤106 呢?
那就用FFT吧~
FFT就是 O(nlogn) 求两个多项式乘积的东西
系数与点值表示
系数表示
对于一个多项式 f(x)=a0+a1x+a2x2+...anxn,我们可以把它的每一项系数提出来,变为
(a0,a1,a2...,an)
这就是多项式的系数表示
点值表示
众所周知,n+1 个点可以确定一个 n 次函数
那么就可以用 n+1 个点表示一个 n 次函数
这就是多项式的点值表示
那既然系数乘法要 O(n2) 的复杂度,点值的乘法又是 O(n) 的
则我们可考虑系数表示化为点值表示
这样我们就只需要一个能快速将多项式转换为点值表示和快速将点值表示转换为多项式的算法了
单位根
定义
对于方程 xn=1,它的解就相当于复平面上单位圆上的 n 个 n 等分点,如当 n=3 时

图中 A1,A2,A3 三点即为 x3=1 的解
设幅角为正且最小的复数为 ωn,称为 n 次单位根,即
ωn=cos(n2π)+isin(n2π)
即为图中 A2 点的坐标
由欧拉公式
ωnk=cos(n2kπ)+isin(n2kπ)
性质
ωn=cos(n2π)−isin(n2π)
ωanak=cos(n2akπ)+isin(n2akπ)=cos(n2kπ)+isin(n2kπ)=ωnk
FFT
FFT就是在 O(nlogn) 的时间复杂度内算出多项式的点值表示
对于多项式 f(x)=a0+a1x+a2x2+...+an−1xn−1
我们要将其转化为点值表示,可以设 n=2k(即在高次项处补0),那么我们可以按照次数的奇偶性分为:
f(x)=(a0+a2x2+...+an−2xn−2)+(a1x+a3x3+...+an−1xn−1)
设
f1(x)=a0+a2x+a4x2+...+an−2x2n
f2(x)=a1+a3x+a5x2+...+an−1x2n
则
f(x)=f1(x2)+xf2(x2)
f(−x)=f1(x2)−xf2(x2)
对于 f1(x) 和 f2(x),我们也可以如法炮制
那 x 我们应该带入什么值呢?
既然都讲了 ωn 那就带 ωn 吧(其实是因为 ωn 好算,且 ωanak=ωnk)
所以
f(ωn)=f1(ωn2)+ωnf2(ωn2)=f1(ω2n)+ωnf2(ω2n)
f(−ωn)=f1(ωn2)−ωnf2(ωn2)=f1(ω2n)−ωnf2(ω2n)
最后就可以分治来算了,时间复杂度 O(nlogn)
代码
struct complex
{
double r,i;
complex operator +(complex a)
{
a.r+=r;
a.i+=i;
return a;
}
complex operator -(complex a)
{
a.r-=r;
a.i-=i;
a.r=-a.r;
a.i=-a.i;
return a;
}
complex operator *(complex a)
{
double s=a.r,x=a.i;
a.r=r*s-i*x;
a.i=r*x+i*s;
return a;
}
complex(){}
complex(double a,double b){r=a,i=b;}
void om(long long n)
{
r=cos(2*pi/n);
i=sin(2*pi/n);
}
};
void fft(complex s[],long long n)
{
if(n==1) return;
complex j[n/2+1],o[n/2+1];
for(int i=0;i<n;i++)
{
if(i%2==1) j[i/2]=s[i];
else o[i/2]=s[i];
}
fft(j,n/2);
fft(o,n/2);
complex w;
w.om(n);
complex c(1,0);
for(int i=0;i<=n/2;i++,c=c*w)
{
s[i]=o[i]+c*j[i];
s[n-i]=o[i]-c*j[i];
}
}
IFFT
IFFT就是在 O(nlogn) 的时间复杂度内算出多项式的系数表示
那怎么做呢?就把原来变换中的 ωn 变为它的共轭复数,即 ωn
至于为什么嘛,我也不知道为什么qwq
struct complex
{
double r,i;
complex operator +(complex a)
{
a.r+=r;
a.i+=i;
return a;
}
complex operator -(complex a)
{
a.r-=r;
a.i-=i;
a.r=-a.r;
a.i=-a.i;
return a;
}
complex operator *(complex a)
{
double s=a.r,x=a.i;
a.r=r*s-i*x;
a.i=r*x+i*s;
return a;
}
complex(){}
complex(double a,double b){r=a,i=b;}
void om(long long n)
{
r=cos(2*pi/n);
i=sin(2*pi/n);
}
void uom(long long n)
{
r=cos(2*pi/n);
i=-sin(2*pi/n);
}
};
void fft(complex s[],long long n,bool ifft)
{
if(n==1) return;
complex j[n/2+1],o[n/2+1];
for(int i=0;i<n;i++)
{
if(i%2==1) j[i/2]=s[i];
else o[i/2]=s[i];
}
fft(j,n/2,ifft);
fft(o,n/2,ifft);
complex w;
if(ifft) w.uom(n);
else w.om(n);
complex c(1,0);
for(int i=0;i<=n/2;i++,c=c*w)
{
s[i]=o[i]+c*j[i];
s[n-i]=o[i]-c*j[i];
}
}
优化
emm,(q_q)我不会