浅析FFT

       FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法。即为快速傅氏变换。它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。

       最近学习FFT,感觉GGN_2015的这篇博客写的非常好。大家可以去看一看,那我就负责讲一讲题目怎么做吧(一切源于这篇博客)。

       先贴一段FFT模板。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<complex>
using namespace std;
typedef complex<double> cd;//复数类的定义
const int maxl=2094153;//nlogn的最大长度(来自leo学长的博客)
const double PI=3.14159265358979;//圆周率,不解释
cd a[maxl],b[maxl];//用于储存变换的中间结果
int rev[maxl];//用于储存二进制反转的结果
void getrev(int bit){
    for(int i=0;i<(1<<bit);i++)//高位决定二进制数的大小
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));//能保证(x>>1)<x,满足递推性质
}
void fft(cd* a,int n,int dft)//变换主要过程
{
    for(int i=0;i<n;i++)//按照二进制反转
        if(i<rev[i])//保证只把前面的数和后面的数交换,(否则数组会被翻回来)
            swap(a[i],a[rev[i]]);
    for(int step=1;step<n;step<<=1)//枚举步长的一半
	{
        cd wn=exp(cd(0,dft*PI/step));//计算单位复根
        for(int j=0;j<n;j+=step<<1)//对于每一块
		{
            cd wnk(1,0);//!!每一块都是一个独立序列,都是以零次方位为起始的
            for(int k=j;k<j+step;k++)//蝴蝶操作处理这一块
			{
                cd x=a[k];
                cd y=wnk*a[k+step];
                a[k]=x+y;
                a[k+step]=x-y;
                wnk*=wn;//计算下一次的复根
            }
        }
    }
    if(dft==-1)//如果是反变换,则要将序列除以n
    	for(int i=0;i<n;i++)a[i]/=n;
}
int output[maxl];
char s1[maxl],s2[maxl];
int main()
{
    scanf("%s%s",s1,s2);//读入两个数
    int l1=strlen(s1),l2=strlen(s2);//就算"次数界"
    int bit=1,s=2;//s表示分割之前整块的长度
    for(bit=1;(1<<bit)<l1+l2-1;bit++)s<<=1;//找到第一个二的整数次幂使得其可以容纳这两个数的乘积
    for(int i=0;i<l1;i++)//第一个数装入a
        a[i]=(double)(s1[l1-i-1]-'0');
    for(int i=0;i<l2;i++)//第二个数装入b
        b[i]=(double)(s2[l2-i-1]-'0');
    getrev(bit);fft(a,s,1);fft(b,s,1);//dft
    for(int i=0;i<s;i++)a[i]*=b[i];//对应相乘
    fft(a,s,-1);//idft
    for(int i=0;i<s;i++)//还原成十进制数
	{
        output[i]+=(int)(a[i].real()+0.5);//注意精度误差
        output[i+1]+=output[i]/10;
        output[i]%=10;
    }
    int i;  
    for(i=l1+l2;!output[i]&&i>=0;i--);//去掉前导零
    if(i==-1)printf("0");//特判长度为0的情况
    for(;i>=0;i--)//输出这个十进制数
        printf("%d",output[i]);
    putchar('\n');
    return 0;
}

       让我们去掉注释再精简一下。

#include<bits/stdc++.h>
using namespace std;
typedef complex<double> cd;
const int maxl=2094153;
const double PI=3.14159265358979;
cd a[maxl],b[maxl];
int rev[maxl],output[maxl];
char s1[maxl],s2[maxl];
void getrev(int bit)
{
    for(int i=0;i<(1<<bit);i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void fft(cd* a,int n,int dft)
{
    for(int i=0;i<n;i++)
        if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int step=1;step<n;step<<=1)
	{
        cd wn=exp(cd(0,dft*PI/step));
        for(int j=0;j<n;j+=step<<1)
		{
            cd wnk(1,0);
            for(int k=j;k<j+step;k++)
			{
                cd x=a[k];
                cd y=wnk*a[k+step];
                a[k]=x+y;
                a[k+step]=x-y;
                wnk*=wn;
            }
        }
    }
    if(dft==-1)
    	for(int i=0;i<n;i++)a[i]/=n;
}
int main()
{
    scanf("%s%s",s1,s2);
    int l1=strlen(s1),l2=strlen(s2);
    int bit=1,s=2;
    for(bit=1;(1<<bit)<l1+l2-1;bit++)s<<=1;
    for(int i=0;i<l1;i++)
        a[i]=(double)(s1[l1-i-1]-'0');
    for(int i=0;i<l2;i++)
        b[i]=(double)(s2[l2-i-1]-'0');
    getrev(bit);fft(a,s,1);fft(b,s,1);
    for(int i=0;i<s;i++)a[i]*=b[i];
    fft(a,s,-1);
    for(int i=0;i<s;i++)
	{
        output[i]+=(int)(a[i].real()+0.5);
        output[i+1]+=output[i]/10;
        output[i]%=10;
    }
    int i;
    for(i=l1+l2;!output[i]&&i>=0;i--);
    if(i==-1)printf("0");
    for(;i>=0;i--)printf("%d",output[i]);
    putchar('\n');
    return 0;
}

       没啥好讲的,只是为了以后容易整理。我也懒得写博客了。

猜你喜欢

转载自blog.csdn.net/qq_34531807/article/details/79603912
FFT