FFT

https://www.luogu.com.cn/blog/command-block/fft-xue-xi-bi-ji

http://blog.miskcoo.com/2015/04/polynomial-multiplication-and-fast-fourier-transform

 

 

 

#include <cstdio>
#include <cmath>
#define Maxn 1350000
using namespace std;
const double Pi=acos(-1);
int n,m;
struct CP
{
  CP (double xx=0,double yy=0){x=xx,y=yy;}
  double x,y;
  CP operator + (CP const &B) const
  {return CP(x+B.x,y+B.y);}
  CP operator - (CP const &B) const
  {return CP(x-B.x,y-B.y);}
  CP operator * (CP const &B) const
  {return CP(x*B.x-y*B.y,x*B.y+y*B.x);}
  //除法没用
}f[Maxn<<1],sav[Maxn<<1];
void dft(CP *f,int len)
{
  if (len==1)return ;//边界
  //指针的使用比较巧妙 
  CP *fl=f,*fr=f+len/2;
  for (int k=0;k<len;k++)
       sav[k]=f[k];
  for (int k=0;k<len/2;k++)//分奇偶打乱
    {
	    fl[k]=sav[k<<1];
		fr[k]=sav[k<<1|1];
	}
  dft(fl,len/2);
  dft(fr,len/2);//处理子问题
  //由于每次使用的单位根次数不同(len次单位根),所以要重新求。
  CP tG(cos(2*Pi/len),sin(2*Pi/len)),buf(1,0);
  for (int k=0;k<len/2;k++)
  {
    //这里buf = (len次单位根的第k个) 
    sav[k]=fl[k]+buf*fr[k];//(1)
    sav[k+len/2]=fl[k]-buf*fr[k];//(2)
    //这两条语句具体见Tips的式子
    buf=buf*tG;//得到下一个单位根。
  }
  for (int k=0;k<len;k++)
       f[k]=sav[k];
}
int main()
{
  scanf("%d",&n);
  for (int i=0;i<n;i++)
      scanf("%lf",&f[i].x);
  //一开始都是实数,虚部为0
  for(m=1;m<n;m<<=1);
  //把长度补到2的幂,不必担心高次项的系数,因为默认为0
  dft(f,m);
  for(int i=0;i<m;++i)
    printf("(%.4f,%.4f)\n",f[i].x,f[i].y);
  return 0;
}

  

#include <cstdio>
#include <cmath>
#define Maxn 1350000
using namespace std;
const double Pi=acos(-1);
int n,m;
struct CP
{
  CP (double xx=0,double yy=0){x=xx,y=yy;}
  double x,y;
  CP operator + (CP const &B) const
  {return CP(x+B.x,y+B.y);}
  CP operator - (CP const &B) const
  {return CP(x-B.x,y-B.y);}
  CP operator * (CP const &B) const
  {return CP(x*B.x-y*B.y,x*B.y+y*B.x);}
  //除法没用
}f[Maxn<<1],p[Maxn<<1],sav[Maxn<<1];
void dft(CP *f,int len)
{
  if (len==1)return ;//边界
  //指针的使用比较巧妙 
  CP *fl=f,*fr=f+len/2;
  for (int k=0;k<len;k++)sav[k]=f[k];
  for (int k=0;k<len/2;k++)//分奇偶打乱
    {fl[k]=sav[k<<1];fr[k]=sav[k<<1|1];}
  dft(fl,len/2);
  dft(fr,len/2);//处理子问题
  //由于每次使用的单位根次数不同(len次单位根),所以要重新求。
  CP tG(cos(2*Pi/len),sin(2*Pi/len)),buf(1,0);
  for (int k=0;k<len/2;k++){
    //这里buf = (len次单位根的第k个) 
    sav[k]=fl[k]+buf*fr[k];//(1)
    sav[k+len/2]=fl[k]-buf*fr[k];//(2)
    //这两条语句具体见Tips的式子
    buf=buf*tG;//得到下一个单位根。
  }for (int k=0;k<len;k++)f[k]=sav[k];
}
void idft(CP *f,int len)
{
  if (len==1)return ;//边界
  //指针的使用比较巧妙 
  CP *fl=f,*fr=f+len/2;
  for (int k=0;k<len;k++)sav[k]=f[k];
  for (int k=0;k<len/2;k++)//分奇偶打乱
    {fl[k]=sav[k<<1];fr[k]=sav[k<<1|1];}
  idft(fl,len/2);
  idft(fr,len/2);//处理子问题
  CP tG(cos(2*Pi/len),  -  sin(2*Pi/len)),buf(1,0);
               //注意这 ↑ 个负号! 
  for (int k=0;k<len/2;k++)
  {
    //这里buf = (len次反单位根的第k个) 
    sav[k]=fl[k]+buf*fr[k];
    sav[k+len/2]=fl[k]-buf*fr[k];
    buf=buf*tG;//得到下一个反单位根。
  }
  for (int k=0;k<len;k++)
       f[k]=sav[k];
}
int main()
{
  scanf("%d%d",&n,&m);
  for (int i=0;i<=n;i++)
       scanf("%lf",&f[i].x);
  for (int i=0;i<=m;i++)
       scanf("%lf",&p[i].x);
  for(m+=n,n=1;n<=m;n<<=1);//把长度补到2的幂
  dft(f,n);
  dft(p,n);//DFT
  for(int i=0;i<n;++i)
      f[i]=f[i]*p[i];//点值直接乘
  idft(f,n);//IDFT
  for(int i=0;i<=m;++i)
      printf("%d ",(int)(f[i].x/n+0.49));
  //注意结果除以n
  return 0;
}

  

 

 

三次变两次"优化
根据 (a+bi)*(c+di)=a(c+di)*bi(c+di)=ac-bd+adi+bci
假设我们需要求 F(x)*G(x)
设复多项式 P(x)=F(x)+G(x)i
也就是实部为 F(x),虚部为 G(x).
则 P(x)^2=(F(x)+G(x)i)^2=F(x)^2-G(x)^2+2F(x)G(x)i

其实部为F(x)^2-G(x)^2,
其虚部为2F(x)G(x)

也就是说求出 P(x)^2之后,把它的虚部除以2即可.

#include<algorithm>
#include<cstdio>
#include<cmath>
#define Maxn 1350000
using namespace std;
const double Pi=acos(-1);
inline int read()
{
  register char ch=0;
  while(ch<48||ch>57)ch=getchar();
  return ch-'0';
}
int n,m;
struct CP
{
  CP (double xx=0,double yy=0){x=xx,y=yy;}
  double x,y;
  CP operator + (CP const &B) const
  {return CP(x+B.x,y+B.y);}
  CP operator - (CP const &B) const
  {return CP(x-B.x,y-B.y);}
  CP operator * (CP const &B) const
  {return CP(x*B.x-y*B.y,x*B.y+y*B.x);}
}f[Maxn<<1];//只用了一个复数数组 
int tr[Maxn<<1];
void fft(CP *f,bool flag)
{
  for (int i=0;i<n;i++)
    if (i<tr[i])swap(f[i],f[tr[i]]);
  for(int p=2;p<=n;p<<=1){
    int len=p>>1;
    CP tG(cos(2*Pi/p),sin(2*Pi/p));
    if(!flag)tG.y*=-1;
    for(int k=0;k<n;k+=p){
      CP buf(1,0);
      for(int l=k;l<k+len;l++){
        CP tt=buf*f[len+l];
        f[len+l]=f[l]-tt;
        f[l]=f[l]+tt;
        buf=buf*tG;
      }
    }
  }
}
int main()
{
  scanf("%d%d",&n,&m);
  for (int i=0;i<=n;i++)
      f[i].x=read(); //实部  
  for (int i=0;i<=m;i++)
      f[i].y=read(); //虚部 
  for(m+=n,n=1;n<=m;n<<=1);
  for(int i=0;i<n;i++)
       tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
  fft(f,1); //求出离散的值 
  for(int i=0;i<n;++i) //进行平方 
     f[i]=f[i]*f[i];
  fft(f,0);  //反演出系数 
  for(int i=0;i<=m;++i)  //虚部 /2,即为所要求的系数 
    printf("%d ",(int)(f[i].y/n/2+0.49));
  return 0;
}

  

 

posted @ 2021-01-18 21:11  我微笑不代表我快乐  阅读(91)  评论(0编辑  收藏  举报