zoukankan      html  css  js  c++  java
  • FFT【快速傅里叶变换】FWT【快速沃尔什变换】

    实在是 美丽的数学啊

    关于傅里叶变换的博客 讲的很细致 图片非常易于理解http://blog.jobbole.com/70549/

    大概能明白傅里叶变换是干吗的了

    但是还是不能明白为什么用傅里叶变换来算多项式求和

    在多项式中,DFT就是系数表式转换成点值表示的过程。

    我们熟知的是多项式的系数表示法,通过给定一组  来确定一个唯一的多项式:

    而多项式还可以有另一种表示法,称为点值表示法

    其中

    可以证明,对一组互不相同的该方法也可以唯一地表示一个多项式。

    为什么要引入点值表示法这个并不“直观”的形式呢?下表显示了它的好处:

    执行运算 系数表示 点值表示
    A(x)+B(x) O(n) O(n)
    A(x)∗B(x) O(n2) O(n)

    *当然,点值表示法下的运算均要求 A(x)和 B(x) 所取的点集 {x0,x1,...,xn−1}是相同的,且运算出的 C(x) 也为点值表示法。

    FFT只是快速的求DFT的方法罢了,不是一个新的概念。 在ACM-ICPC竞赛中, FFT算法常被用来为多项式乘法加速。

    FFT原理就是通过奇偶分开,把规模减半,递归分治 在O(nlogn)时间内完成DFT运算

    普通的计算多项式乘法的计算,时间复杂度O(n2)O(n2)。而FFT先将多项式点值表示(O(nlogn)),在O(n)下完成对点值的乘法,再以O(nlogn)O(nlog⁡n)完成IFFT,重新得到系数表示。

    利用FFT求卷积

    普通的计算多项式乘法的计算,时间复杂度O(n2)O(n2)。而FFT先将多项式点值表示(O(nlogn)),在O(n)下完成对点值的乘法,再以O(nlogn)完成IFFT,重新得到系数表示。

    步骤一(补0)

    在两个多项式前面补0,得到两个2n次多项式,设系数向量分别为v1v1和v2v2。

    步骤二(求值)

    使用FFT计算f1=DFT(v1)和f2=DFT(v2)。则f1f1与f2f2为两个多项式在2n2n次单位根处的取值(即点值表示)。

    步骤三(乘法)

    把f1f1与f2f2每一维对应相乘,得到ff,代表对应输入多项式乘积的点值表示。

    步骤四(插值)

    使用IFFT计算v=IDFT(f),其中vv就是乘积的系数向量。

    综上

    fft(x1, len, 1);
    fft(x2, len, 1);
    for (int i = 0;i < len;i++) {
        x[i] = x1[i] * x2[i];
    }
    fft(x, len, -1);

    FFT算法步骤:https://wenku.baidu.com/view/8bfb0bd476a20029bd642d85.html

    kuangbin模板:

    const double PI = acos(-1.0);
    //复数结构体
    struct complex
    {
        double r,i;
        complex(double _r = 0.0,double _i = 0.0)
        {
            r = _r; i = _i;
        }
        complex operator +(const complex &b)
        {
            return complex(r+b.r,i+b.i);
        }
        complex operator -(const complex &b)
        {
            return complex(r-b.r,i-b.i);
        }
        complex operator *(const complex &b)
        {
            return complex(r*b.r-i*b.i,r*b.i+i*b.r);
        }
    };
    /*
     * 进行FFT和IFFT前的反转变换。
     * 位置i和 (i二进制反转后位置)互换
     * len必须去2的幂
     */
    void change(complex y[],int len)
    {
        int i,j,k;
        for(i = 1, j = len/2;i < len-1; i++)
        {
            if(i < j)swap(y[i],y[j]);
            //交换互为小标反转的元素,i<j保证交换一次
            //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
            k = len/2;
            while( j >= k)
            {
                j -= k;
                k /= 2;
            }
            if(j < k) j += k;
        }
    }
    /*
     * 做FFT
     * len必须为2^k形式,
     * on==1时是DFT,on==-1时是IDFT
     */
    void fft(complex y[],int len,int on)
    {
        change(y,len);
        for(int h = 2; h <= len; h <<= 1)
        {
            complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
            for(int j = 0;j < len;j+=h)
            {
                complex w(1,0);
                for(int k = j;k < j+h/2;k++)
                {
                    complex u = y[k];
                    complex t = w*y[k+h/2];
                    y[k] = u+t;
                    y[k+h/2] = u-t;
                    w = w*wn;
                }
            }
        }
        if(on == -1)
            for(int i = 0;i < len;i++)
                y[i].r /= len;
    }

    沃尔什变换https://www.cnblogs.com/ACMLCZH/p/8022502.html

    主要功能是求:,其中为集合运算符。

      就像FFT一样,FWT是对数组的一种变换,我们称数组X的变换为FWT(X)。

      所以FWT的核心思想是:

        为了求得C=A★B,我们瞎搞搞出一个变换FWT(X),

        使得FWT(C)=FWT(A)  FWT(B),然后根据FWT(C)求得C。

        (其中★表示卷积运算,表示将数组对应下标的数相乘的运算)

        也就是说我们可以通过FWT(X)变换把复杂度O(n^2)的★运算变为O(n)的运算。

      跟FFT是完全相同的。所以我们考虑怎么搞出这个FWT(X)。

  • 相关阅读:
    atom 安装插件列表
    django学习
    windows 安装 python3
    python3 监控代码变化 自动重启 提高开发效率
    git无法pull仓库refusing to merge unrelated histories
    python 项目部署virtualenv
    python 多线程并发threading & 任务队列Queue
    python logging 日志使用
    jupyter 教程
    mysql 替换数据库字段内容
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9643385.html
Copyright © 2011-2022 走看看