zoukankan      html  css  js  c++  java
  • [快速傅立叶变换&快速傅里叶变换]【旧 手写笔记】

    $FFT$好美啊


     参考资料:

    1.算法导论 2.Miskcoo 3.Menci 4.虚数的意义-阮一峰


    简单说一下,具体在下面的图片

    实现:

    可以用$complex$也可以手写 和计算几何差不多 注意$complex*complex$

    $omega[k]=w(n,k)$  $omegaInv[k]=w(n,-k)$是共轭复数 先预处理 递推可能有精度问题

    $transform$

    • 先把位置弄好了,方法是直接求二进制逆序,单向交换
    • 然后枚举$l$为当前合并后的长度,$m=l>>1$就是当前要合并的两段的长度,$p$枚举位置,蝴蝶变化,指针就是喵啊
    • $[p,p+m)$保存了$A_0$,$[p+m,p+l)$保存了$A_1$,然后就是利用了$A(x)=A_0(x^2)+x*A_1(x^2)$

    $FFT$要先加倍次数界

    const double PI=acos(-1);
    struct Vector{
        double x,y;
        Vector(double a=0,double b=0):x(a),y(b){}
    };
    typedef Vector CD;
    Vector operator +(Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
    Vector operator -(Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
    Vector operator *(Vector a,Vector b){return Vector(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
    Vector conj(Vector a){return Vector(a.x,-a.y);}
    
    struct FastFourierTransform{
        int n,rev[N];
        CD omega[N],omegaInv[N];
        void ini(int m){
            n=1;
            while(n<m) n<<=1;
            for(int k=0;k<n;k++) 
                omega[k]=CD(cos(2*PI/n*k),sin(2*PI/n*k)),
                omegaInv[k]=conj(omega[k]);
    
            int k=0;
            while((1<<k)<n) k++;
            for(int i=0;i<n;i++){
                int t=0;
                for(int j=0;j<k;j++) if(i&(1<<j)) t|=(1<<(k-j-1));
                rev[i]=t;
            }
        }
        void transform(CD *a,CD *omega){
            for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
            for(int l=2;l<=n;l<<=1){
                int m=l>>1;
                for(CD *p=a;p!=a+n;p+=l)
                    for(int k=0;k<m;k++){
                        CD t=omega[n/l*k]*p[k+m];
                        p[k+m]=p[k]-t;
                        p[k]=p[k]+t;
                    }
            }
        }
        void DFT(CD *a,int flag){
            if(flag==1) transform(a,omega);
            else{
                transform(a,omegaInv);
                for(int i=0;i<n;i++) a[i].x/=(double)n;
            }
        }
    }fft;
    FFT模板

    每次递推$w$会更快

    长度枚举到$l$时 $w_n=e^{frac{2pi}{i}}$

    const double PI=acos(-1);
    struct Vector{
        double x,y;
        Vector(double a=0,double b=0):x(a),y(b){}
    };
    typedef Vector CD;
    Vector operator +(Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
    Vector operator -(Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
    Vector operator *(Vector a,Vector b){return Vector(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
    
    struct FastFourierTransform{
        int n,rev[N];
        void ini(int m){
            n=1;
            while(n<m) n<<=1;
    
            int k=0;
            while((1<<k)<n) k++;
            for(int i=0;i<n;i++){
                int t=0;
                for(int j=0;j<k;j++) if(i&(1<<j)) t|=(1<<(k-j-1));
                rev[i]=t;
            }
        }
        void DFT(CD *a,int flag){
            for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
            for(int l=2;l<=n;l<<=1){
                int m=l>>1;
                CD wn(cos(2*PI/l),flag*sin(2*PI/l));
                for(CD *p=a;p!=a+n;p+=l){
                    CD w(1,0);
                    for(int k=0;k<m;k++){
                        CD t=w*p[k+m];
                        p[k+m]=p[k]-t;
                        p[k]=p[k]+t;
                        w=w*wn;
                    }
                }
            }
            if(flag==-1) for(int i=0;i<n;i++) a[i].x/=n;
        }
    }fft;
    FFT模板2

    卷积 $(f imes g)(n)=sumlimits_{i=0}^{n}{f(i)*g(n-i)}$

    多项式乘法就是一个系数向量的卷积

    可以用$FFT$快速计算卷积

    遇到和不是定值的情况可以反转一个向量


     


    fft1


    fft2


    fft3


    fft4


    本来是另一篇博客,搬到这里来了

    参考资料

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

    https://oi.men.ci/fft-to-ntt/

    http://blog.csdn.net/acdreamers/article/details/8883285


    目的:

    1.只有整数参与时防止浮点误差(我做题少,还没遇到误差......)

    2.题目要求模意义下


    :设,使得成立的最小,称为对模的阶,记为

    原根:是正整数,是整数,若的阶等于,则称为模的一个原根。

    假设一个数对于模(p is prime)来说是原根,那么结果互不相同,那么是模的一个原根;

    因为当且仅当指数为的时候成立,所以不可能相同啊。

    有原根的充要条件:

    ,其中是奇素数。

    求模素数原根的方法:

    枚举g,对素因子分解,即的标准分解式,若恒有成立,则就是的原根。(对于合数求原根,只需把换成即可)

    实现:

    PrimitiveRoot

     当然了,在NNT中为了简单起见不要筛素数了,直接枚举p-1的所有约数就行了

    ll powMod(ll a,ll b,ll MOD){
        ll ans=1;
        for(;b;b>>=1,a=a*a%MOD)
            if(b&1) ans=ans*a%MOD;
        return ans;
    }
    int PrimitiveRoot(int p){
        if(p==2) return 1;
        for(int g=2;g<p;g++){
            int flag=1,m=sqrt(p);
            for(int i=2;i<=m;i++) if((p-1)%i==0)
                if(powMod(g,(p-1)/i,p)==1) {flag=0;break;}
            if(flag) return g;
        }
        return 0;
    }

    NNT ---Fast Number-Theoretic Transform

    质数p=q*n+1 (n=2m)  原根g  则gqn Ξ 1 (mod p)

    看成是的等价

    令gΞ gq (mod p)     即wn的等价

    • gn0,1,...,n-1  (mod p) 互不相同  
    • gn^n Ξ 1 (mod p)   则 gn^n/2 Ξ -1 (mod p)  ,因为互不相同所以不能是1
    • 其他wn的性质也满足

    所以可以用原根代替单位根

    这里的n(用N表示吧)可以比原来那个的n(乘法结果的长度扩展到2的幂次后的n)大,只要把q*N/n看做q就行了

     

    枚举到l长度时wn就是g(p-1)/l

    通常P和g是固定的,提前处理出来就行了  一个很好的选择是 1004535809=479221+1

    ll P=1004535809,MOD=P;
    ll Pow(ll a,ll b,ll MOD){
        ll ans=1;
        for(;b;b>>=1,a=a*a%MOD)
            if(b&1) ans=ans*a%MOD;
        return ans;
    }
    struct NumberTheoreticTransform{
        int n,rev[N];
        ll g;
        void ini(int m){
            n=1;
            while(n<m) n<<=1;
            
            int k=0;
            while((1<<k)<n) k++;
            for(int i=0;i<n;i++){
                int t=0;
                for(int j=0;j<k;j++) if(i&(1<<j)) t|=(1<<(k-j-1));
                rev[i]=t;
            }
    
            g=3;
        }
        void DFT(ll *a,int flag){
            for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
            for(int l=2;l<=n;l<<=1){
                int m=l>>1;
                ll wn=Pow(g,flag==1?(P-1)/l:P-1-(P-1)/l,P);
                for(ll *p=a;p!=a+n;p+=l){
                    ll w=1;
                    for(int k=0;k<m;k++){
                        ll t=w*p[k+m]%P;
                        p[k+m]=(p[k]-t+P)%P;
                        p[k]=(p[k]+t)%P;
                        w=w*wn%P;
                    }
                }
            }
            if(flag==-1){
                ll inv=Pow(n,P-2,P);;
                for(int i=0;i<n;i++) a[i]=a[i]*inv%P;
            }
        }
        void MUL(ll *A,ll *B){
            DFT(A,1);DFT(B,1);
            for(int i=0;i<n;i++) A[i]=A[i]*B[i]%MOD;
            DFT(A,-1);
        }
    }fft;

  • 相关阅读:
    什么是接口测试?怎样做接口测试?
    python下批量执行多条py文件的方法
    Jmeter运行报错software caused connection abort:recv failed
    性能测试一般容易出现瓶颈点
    性能测试流程规范(较好文档)
    Jmeter代理录制获取登录参数_移动端设置代理
    Http请求与WebSocket请求区别(WebSocket协议简析)
    JSONObject方法提取响应数据中的值
    Jmeter学习资料、控件下载地址大全
    图解IntelliJ IDEA 13版本对Android SQLite数据库的支持
  • 原文地址:https://www.cnblogs.com/candy99/p/6387915.html
Copyright © 2011-2022 走看看