关于快速傅里叶变换
__vector__ · · 个人记录
前言:
本博客学习于此篇文章
前置:
点值表示法:
快速傅里叶变换中的特殊性质:
设
也就是通过两个函数的卷积的进行FFT得到的结果
复数:
复数由实数和虚数两个部分组成,其中实数部分可以看作横轴上的数,虚数部分可以看作纵轴上的数,同时也用另一个单位
设一个复数的实数部分为a,虚数部分为b,那么它的模记为
复数的乘法(假设为
共轭复数:
对于一个复数
可以发现,这就是 (说废话呢)
单位根
设一个数
将一个半径为
看一下这张图:
从箭头开始的那一点开始,逆时针将
推式子:
设多项式
拆开,变成:
两边很相似。
设
显然,
设一个值
对于代入
可以发现,
所以可以通过求前半部分直接得到后半部分。
FFT实现:
递归版 FFT 跑得慢,所以不使用递归版。
每个数的最终位置就是当前位置二进制反转之后的位置,所以可以提前改变位置。
代码(P1919):
#include <bits/stdc++.h>
#include <complex>
using namespace std;
#define reg register
const double PI=acos(-1);
const int maxn=5e6+5;
typedef long long ll;
complex<double> a[maxn];
complex<double> b[maxn];
ll c[maxn];//存储结果
int rev[maxn];
char given_a[maxn];
char given_b[maxn];
void FFT(complex<double>* num,int n,int inv)
{
for(reg int i=0;i<n;i++)
{
if(i<rev[i])
{
swap(num[i],num[rev[i]]);
}
//提前更改为fft之后的序列
}
for(reg int mid=1;mid<n;mid<<=1)
{//要处理序列大小的一半
complex<double> dwg(cos(PI/mid),inv*sin(PI/mid));//单位根
for(reg int i=0;i<n;i+=(mid<<1))
{//处理到的地方
complex<double> omega(1,0);
//逐步求出omega(n,k)
for(reg int j=0;j<mid;j++,omega*=dwg)
{
complex<double> n1=num[i+j];
complex<double> n2=num[i+j+mid]*omega;
//根据左边求出右边
num[i+j]=n1+n2;
num[i+j+mid]=n1-n2;
}
}
}
}
int main()
{
scanf("%s",given_a);
scanf("%s",given_b);
int len1=strlen(given_a);
int len2=strlen(given_b);
for(reg int i=0;i<len1;i++)
{
a[i]=given_a[len1-i-1]^48;
}
for(reg int i=0;i<len2;i++)
{
b[i]=given_b[len2-i-1]^48;
}
//-------------------------
int newlen=1;//计算结果位数
int bit_cnt=0;//len1+len2二进制
while(newlen<=len1+len2)
{
newlen<<=1;
//可以看作将其设为2的整数次幂
//因为fft只能处理2的整数次幂
bit_cnt++;
}
for(reg int i=0;i<newlen;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit_cnt-1));
}
//---------------------------------
FFT(a,newlen,1);
FFT(b,newlen,1);
for(reg int i=0;i<=newlen;i++)
{
a[i]*=b[i];
}
FFT(a,newlen,-1);
for(reg int i=0;i<len1+len2;i++)
{
c[i]=a[i].real()/newlen+0.5;
}
for(reg int i=0;i<len1+len2;i++)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
newlen=len1+len2+1;//fft完成后,设置回原来的
while(c[newlen])//处理剩余的仅为
{
c[newlen+1]+=c[newlen]/10;
c[newlen]%=10;
newlen++;
}
while(!c[newlen])//处理剩余的仅为
{
newlen--;
}
for(reg int i=newlen;i>=0;i--)
{
printf("%d",c[i]);
}
#ifndef ONLINE_JUDGE
system("pause");
#endif
return 0;
}