zoukankan      html  css  js  c++  java
  • 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)
    ",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;
    }
    

      

  • 相关阅读:
    ThinkPHP5+jQuery+MySql实现投票功能
    JQ input输入框回车生成标签,可删除,并获取标签的值
    php 使用 CURL 获取数据
    new String创建了几个对象
    java高级开发面试总结
    使用 Sublime Text 将含下划线的字符串批量替换为驼峰命名法格式的字符串
    Synchronized方法锁、对象锁、类锁区别
    利用Redisson实现分布式锁及其底层原理解析
    MySQL索引
    JVM常见面试题
  • 原文地址:https://www.cnblogs.com/cutemush/p/14295234.html
Copyright © 2011-2022 走看看