快速傅里叶变换总结
基本概念
对于求和式
其中最高次项的次数为
用
引入
给定多项式
通过系数表达式直接乘时间复杂度
此时两者相乘的点值表达式可以在
即:
于是就有想法:将系数表达式
快速傅里叶变换就是完成中间过程的工具,可以在
前置知识
有以下重要知识
欧拉公式:
单位复数根:
快速傅里叶变换
将多项式
有:
代入两组值:
得到:
发现只要求出
并且这个问题跟原问题是相同的。
于是分治下来解决子问题,最后合并为原问题即可。
void FFT(comp f[],int n)
{
if(n==1) return;
comp f1[n/2],f2[n/2];
for(int i=0;i<n/2;++i)
{
f1[i]=f[i<<1],f2[i]=f[i<<1|1];
}
FFT(f1,n/2),FFT(f2,n/2);
comp w=polar(1.0,(2.0*pi/n)),wk=comp(1,0);
for(int i=0;i<n/2;++i)
{
f[i]=f1[i]+f2[i]*wk;
f[i+n/2]=f1[i]-f2[i]*wk;
wk*=w;
}
}
快速傅里叶逆变换
现在完成了系数表示法
有结论:
IFFT等价于 FFT 中代入得复数变为其倒数,再除以变换长度。
故结论成立。
容易证明,这也等价于将
优化
以上的代码实现都基于递归,常数较大。
基于以下观察,可以写出迭代写法。
原位置对应元素的下表二进制翻转后,成了最后当前元素的下标,于是这样处理之后直接自底向上合并即可。
代码
递归版本
#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
w=(w<<1)+(w<<3)+(ch^48);
ch=getchar();
}
return w*f;
}
inline void write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9) write(x/10);
putchar(x%10+'0');
}
#define comp complex<double>
const int maxn=2e6+10;
const double pi=3.1415926535;
void FFT(comp f[],int n)
{
if(n==1) return;
comp f1[n/2],f2[n/2];
for(int i=0;i<n/2;++i)
{
f1[i]=f[i<<1],f2[i]=f[i<<1|1];
}
FFT(f1,n/2),FFT(f2,n/2);
comp w=polar(1.0,(2.0*pi/n)),wk=comp(1,0);
// comp w=comp(cos(2.0*pi/n),type*sin(2.0*pi/n)),wk=comp(1,0);
for(int i=0;i<n/2;++i)
{
f[i]=f1[i]+f2[i]*wk;
f[i+n/2]=f1[i]-f2[i]*wk;
wk*=w;
}
}
void IFFT(comp f[],int n)
{
FFT(f,n);
reverse(f+1,f+n);
}
int n,m;
comp a[maxn<<1],b[maxn<<1];
int main()
{
#ifndef ONLINE_JUDGE
//freopen("in.txt","r",stdin);
#endif
n=read(),m=read();
for(int i=0;i<=n;++i) a[i]=comp(read(),0);
for(int i=0;i<=m;++i) b[i]=comp(read(),0);
int k=1;
while(k<=n+m) k<<=1;
// cout<<k<<endl;
FFT(a,k),FFT(b,k);
for(int i=0;i<=k;++i)
{
a[i]*=b[i];
}
IFFT(a,k);
for(int i=0;i<=n+m;++i)
{
cout<<(int)(a[i].real()/k+0.5)<<" ";
}
return 0;
}
迭代版本
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
w=(w<<1)+(w<<3)+(ch^48);
ch=getchar();
}
return w*f;
}
void write(int x)
{
if(x<0) x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
#define comp complex<double>
const double pi=acos(-1);
const int maxn=4e6+10;
int n,m,len,k;
int rev[maxn];
comp a[maxn],b[maxn];
int get(int x)
{
int res=0;
for(int i=0;i<len;++i)
{
res+=(x>>i&1)<<(len-i-1);
}
return res;
}
void fft(comp f[],int n)
{
for(int i=0;i<n;++i)
{
rev[i]=get(i);
if(i<rev[i]) swap(f[i],f[rev[i]]);
}
for(int h=2;h<=n;h<<=1)
{
comp w=polar(1.0,2.0*pi/h);
for(int i=0;i<n;i+=h)
{
comp wk=comp(1,0);
for(int j=0;j<h/2;++j)
{
comp x=f[i+j],y=f[i+j+h/2]*wk;
f[i+j]=x+y,f[i+j+h/2]=x-y;
wk*=w;
}
}
}
}
void ifft(comp f[],int n)
{
fft(f,n);
reverse(f+1,f+n);
}
int main()
{
n=read(),m=read();
for(int i=0;i<=n;++i) a[i]=read();
for(int i=0;i<=m;++i) b[i]=read();
k=1;
while(k<=n+m) k<<=1,++len;
fft(a,k),fft(b,k);
for(int i=0;i<=k;++i) a[i]*=b[i];
ifft(a,k);
for(int i=0;i<=n+m;++i) printf("%d ",(int)(a[i].real()/k+0.5));
return 0;
}
解释:蝶形变换的位置数组有
本文作者:vanueber
本文链接:https://www.cnblogs.com/vanueber/p/18677622
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步