快速傅里叶变换(FFT)笔记&模板

先贴三个写的很棒的博客
https://blog.csdn.net/enjoy_pascal/article/details/81478582
https://www.cnblogs.com/RabbitHu/p/FFT.html
https://zhuanlan.zhihu.com/p/31584464?from_voters_page=true

然后整理一下fft的板子,避免以后忘记了怎么敲的在看不懂dalao们的板子
根据定义直接写的朴素版

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <unordered_map>
using namespace std;

typedef long long ll;

const int inf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1);
const int mod=1000000009;
/****************************Link Start****************************/

struct Complex {
    double a,b;
    Complex operator+(const Complex &x) const {
        return Complex{a+x.a,b+x.b};
    }
    Complex operator-(const Complex &x) const {
        return Complex{a-x.a,b-x.b};
    }
    Complex operator*(const Complex &x) const {
        return Complex{a*x.a-b*x.b,a*x.b+b*x.a};
    }
};
Complex fx[100010],gx[100010];
Complex hx[100010];
Complex tmp[100010];

void fft(Complex arr[],int len,int cal) {
    if(len==1) return ;
    int mid=len/2;
    for(int i=0;i<mid;i++) tmp[i]=arr[i*2],tmp[i+mid]=arr[i*2+1];
    for(int i=0;i<len;i++) arr[i]=tmp[i];
    fft(arr,mid,cal); fft(arr+mid,mid,cal);
    for(int i=0;i<mid;i++) {
        Complex omg={cos(2*pi*i/len),cal*sin(2*pi*i/len)};
        tmp[i]=arr[i]+(omg*arr[i+mid]);
        tmp[i+mid]=arr[i]-(omg*arr[i+mid]);
    }
    for(int i=0;i<len;i++) arr[i]=tmp[i];
}

int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0,x;i<n;i++) {
        scanf("%d",&x); fx[i].a=1.0*x;
    }
    for(int i=0,x;i<m;i++) {
        scanf("%d",&x); gx[i].a=1.0*x;
    }
    int len=1;
    while(len<n+m-1) len*=2;
    fft(fx,len,1); fft(gx,len,1);
    for(int i=0;i<len;i++) hx[i]=fx[i]*gx[i];
    fft(hx,len,-1);
    for(int i=0;i<n+m-1;i++) {
        if(i==0) printf("%d",(int)(hx[i].a/len));
        else printf(" %d",(int)(hx[i].a/len));
    }
    return 0;
}

优化后的板子

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <unordered_map>
using namespace std;

typedef long long ll;

const int inf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1);
const int mod=1000000009;
/****************************Link Start****************************/

struct Complex {
    double a,b;
    Complex operator+(const Complex &x) const {
        return Complex{a+x.a,b+x.b};
    }
    Complex operator-(const Complex &x) const {
        return Complex{a-x.a,b-x.b};
    }
    Complex operator*(const Complex &x) const {
        return Complex{a*x.a-b*x.b,a*x.b+b*x.a};
    }
};
Complex fx[100010],gx[100010];
Complex hx[100010];
int pos[100010];

void fft(Complex arr[],int len,int cal) {
    int bit=0;
    while((1<<(bit+1))<len) bit++;
    pos[0]=0;
    for(int i=0;i<len;i++) {
        pos[i]=(pos[i>>1]>>1)|((i&1)<<bit);
        if(i<pos[i]) swap(arr[i],arr[pos[i]]);
    }
    for(int mid=1;mid<len;mid*=2) {
        for(int i=0;i<len;i+=2*mid) for(int j=0;j<mid;j++) {
            Complex omg={cos(pi*j/mid),cal*sin(pi*j/mid)};
            Complex x=arr[i+j],y=arr[i+j+mid];
            arr[i+j]=x+omg*y,arr[i+j+mid]=x-omg*y;
        }
    }
}

int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0,x;i<n;i++) {
        scanf("%d",&x); fx[i].a=1.0*x;
    }
    for(int i=0,x;i<m;i++) {
        scanf("%d",&x); gx[i].a=1.0*x;
    }
    int len=1;
    while(len<n+m-1) len*=2;
    fft(fx,len,1); fft(gx,len,1);
    for(int i=0;i<len;i++) hx[i]=fx[i]*gx[i];
    fft(hx,len,-1);
    for(int i=0;i<n+m-1;i++) {
        if(i==0) printf("%d",(int)(hx[i].a/len));
        else printf(" %d",(int)(hx[i].a/len));
    }
    return 0;
}

最后是感觉需要记录下来的一些东西
1、fft本质是以nlogn的复杂度将多项式的系数表示转换成点值表示,同样ifft(快速傅里叶逆变换)可以nlogn的复杂度把多项式的点值表示转成系数表示
点值表示的两个多项式可以直接o(n)对应相乘
2、跑fft的多项式的项数需要是2的整数次幂,长度不够的要系数补0进行补齐,举个实际的栗子:
设h(x)=f(x)*g(x),在一般系数表达式中f(x)的项数为n,g(x)的项数为m,则h(x)的项数应该为n+m-1
那么我们拿f(x)和g(x)来跑fft时构造的多项式的项数应该是n+m-1补齐之后的2的整数次幂,而不是n或m补齐的
3、优化版板子没有用递归写法,而是直接用递推式求出各系数分治之后的位置,从下往上一层一层的计算

最最后,啥是蝴蝶操作啊

猜你喜欢

转载自www.cnblogs.com/JDZJBD/p/12897550.html