zoukankan      html  css  js  c++  java
  • FFT简明指南

    简介

    FFT是用来计算多项式卷积的东西。

    多项式卷积: (C=Aast B) ,即 (c_k=sum_{i+j=k} a_i imes b_j) 。(假设下标范围 (0-n)

    直接按照定义做是 (O(n^2)) 的,但是FFT可以做到 (nlog(n))

    一些奇奇怪怪的东西(定义)

    考虑一个 (n) 次多项式,取 (n) 个不同的 (x) 代入会得到 (n)(y) ,然后发现由于我们可以解方程,因此只要不是无解、多解,我们可以说,一个 (n) 次多项式和一个(有 (n) 个点的集合一一对应)。

    对于多项式 (A=sum_{i=0}^{n-1}a_i imes x^i) ,定义其在 (x=x_0) 处的点值为 (A(x_0)=sum_{i=0}^{n-1}a_i imes x_0^i)

    多项式系数表示:对于 (A=sum_{i=0}^{n-1} a_i imes x^i) ,系数表示为 (A={a_0,a_1,...,a_{n-1}})

    多项式点值表示: (A={(x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1})}) 。如果 (x) 的取值确定的情况下,显然也可以写成 (A={y_0,y_1,...,y_{n-1}}) 。这样的话,多项式的系数、点值表示都可以写成一个向量vector的形式了。

    点值表示中,若A与B (x) 的取值相同,它们相乘只需要把 (x) 对应的 (y) 分别相乘即可。

    n次单位根(其实n次单位根只应该有1个,但是为了方便这里就当作有n个):方程 (x^n=1) 的n个复数根,分别称为 (w_n^0,w_n^1,w_n^2,...,w_n^{n-1}) ,其中 (w_n^0=1) 。简记为 (w_{n,0}) 。不引起歧义的情况下,我会写成 (w_0) 。同时,在这里,n应该是2的幂次(原因后文会讲)。

    主要用到的n次单位根的性质: (w_{n,1}^k=w_{n,k},w_{n,k}=w_{2 imes n,2 imes k},w_{n,k+frac{n}{2}}=-w_{n,k})

    DFT,就是把一个长度为 (n) 次的向量(多项式系数表示)代入 (n) 次单位根,从而转换为另一个向量(多项式点值表示)。而IDFT,是把以上点值表示转换为系数表示。它们的复杂度都是 (O(nlog n))

    做法

    数学

    好,假设我们现在需要把一个长为 (2^{len}=n) 的多项式 (A=sum_{i=0}^{n-1}a_i imes x^i) 转换为点值表示(系数表示和系数表示向量被认为是相同的)。

    我们先把它分成两部分:

    [A=sum_{i=0}^{frac{n}{2}-1}a_{2 imes i} imes x^{2 imes i}+sum_{i=0}^{frac{n}{2}-1}a_{2 imes i+1} imes x^{2 imes x+1}。 ]

    构造两个新的多项式,其系数表示的向量分别为:

    [A_1={a_0,a_2,...,a_{n-2}}\ A_2={a_1,a_3,...,a_{n-1}} ]

    把这两个多项式分别变成点值表示(相当于递归向下做):

    [A_1={(w_{frac{n}{2},0},a_0),(w_{frac{n}{2},1},a_2),...,(w_{frac{n}{2},frac{n}{2}-1},a_{n-2})}\ A_2={(w_{frac{n}{2},0},a_1),(w_{frac{n}{2},1},a_2),...,(w_{frac{n}{2},frac{n}{2}-1},a_{n-2})} ]

    然后考虑我们需要的是A的点值表示。枚举每一个 (n) 阶单位根,我们需要知道A代入 (w_n^i) 的值。然后再看一下第一个公式

    [A=sum_{i=0}^{frac{n}{2}-1}a_{2 imes i} imes x^{2 imes i}+sum_{i=0}^{frac{n}{2}-1}a_{2 imes i+1} imes x^{2 imes x+1}。 ]

    又因为有 ((w_n^1)^2=w_n^2=w_{frac{n}{2}}^1) ,所以代入 (w_n^i) 后,点值就是A1在 (w_{frac{n}{2}}^i) 处的点值加上 (w_n^i imes A_2[w_{frac{n}{2}}^i])

    然后是IDFT。有一个结论,是说只要把以上的 (n) 次单位根全都变成相反数就行。证明自行百度。

    代码

    inline void FFT(Complex *A,int len,int fl);
    

    DFT中,A开始存系数,后来存(A[w_i])

    基本代码:

    inline void FFT(Complex *A,int len,int fl){
        if (len == 1) return;
        Complex *A1, *A2;
        A1 = new Complex[len / 2], A2 = new Complex[len / 2];
        rep(i, 0, len / 2){
            A1[i] = A[i * 2];
            A2[i] = A[i * 2 + 1];
        }
        FFT(A1, len / 2, fl);
        FFT(A2, len / 2, fl);
        Complex w = init(len, fl), cur = (Complex)1;
        rep(i, 0, len){
            A[i] = A1[i % (len / 2)] + cur * A2[i % (len / 2)];
            cur = cur * w;
        }
    }
    

    如果把最终的数字转换的结果放出来,就是这样的:

    int tmp;
    inline void FFT(Complex *A,int len,int fl){
        // reverse
        int j = 0;
        rep(i, 0, len){
            if (j > i) Swap(A[i], A[j]);
            // reverse add j
        }
        // reverse
        Complex w, step, tmp;
        for (i = 1; i < len; i <<= 1){
            w = ...;
            for (int j = 0; j < len; j += i * 2){
                step = (Complex)1;
                for (k = j; k < j + i; ++k){
                    tmp = step * A[j + i + k];
                    A[j + i + k] = A[j + k] - tmp;
                    A[j + k] += tmp;
                    step *= w;
                }
            }
        }
    }
    

    (上面的加减可能需要理解)

    NTT

    如上,但是把所有的 (n) 次单位复根换成 (pm 3^{998244352/n}mod{998244353})

  • 相关阅读:
    商业软件太贵?找开源替代品
    Odoo9发行说明
    Odoo(OpenERP)配置文件openerp-server.conf详解
    MyBatis-Generator最佳实践
    elasticsearch 口水篇(1) 安装、插件
    log4j直接输出日志到flume
    Maven编译时跳过Test
    Flume1.5.0的安装、部署、简单应用(含伪分布式、与hadoop2.2.0、hbase0.96的案例)
    Flume 1.5.0简单部署试用
    一共81个,开源大数据处理工具汇总(下),包括日志收集系统/集群管理/RPC等
  • 原文地址:https://www.cnblogs.com/pupuvovovovo/p/11704961.html
Copyright © 2011-2022 走看看