快速傅里叶算法(灌水)

来源:互联网 发布:历史时间轴软件 编辑:程序博客网 时间:2024/05/29 06:37

定义和声明

多项式表示方法

  1. 系数表示法: A(x)=n1j=0ajxj
  2. 点值表示法: (x0,y0),(x1,y1),...,(xn1,yn1)
    • 可证这n个不同点唯一确定一个次数界为n的多项式
    • 也可以用公式A(x)=n1k=0ykjk(xxj)/jk(xkxj)O(n2)的复杂度下求解系数表达式

单位复数根

  1. n次单位复根,满足条件wn=1,可得w=e2πik/n
  2. n次单位复数根满足一些基本性质:
    • 消去性质:wdkdn=wkn,由此可得nwn/2n=w2=1
    • 折半性质: wk+n/2n=wkn
    • 求和性质:n1j=0(wkn)j=0

对于多项式A(x),计算它在n个n次单位复数根处的值,即可求出其离散傅里叶变换(DFT)。所以我们求其在n个n次单位复数根处的点值表达式,也相当于对其进行一次离散傅里叶变换。而使用其离散傅里叶变换得到的点值表达式能很方便地在O(n)的时间内计算得到多项式乘法结果。
所以整个算法的过程即: FFT->点值乘法->逆FFT

算法描述

对于多项式A(x),定义

A[0](x)=a0+a2x+a4x2+...+an2xn/21

A[1](x)=a1+a3x+a5x2+...+an1xn/21

那么
A(x)=A[0](x2)+xA[1](x2)

所以,求A(x)在w0n,w1n,...,wn1n的值的问题被转化为求次数界为n/2的A[1](x)A[0](x)在点(w0n)2,(w1n)2,...(wn1n)2,这里实际上仅有n/2个不同值,此时问题规模被缩小了一半。

而计算逆FFT时,只需根据

aj=1/nk=0n1ykwkjn
即可
这里使用一段引自别人博客的代码(#

const double eps(1e-8);typedef long long lint;const double PI = acos(-1.0);struct Complex{    double real, image;    Complex(double _real, double _image)    {        real = _real;        image = _image;    }    Complex(){}};Complex operator + (const Complex &c1, const Complex &c2){    return Complex(c1.real + c2.real, c1.image + c2.image);}Complex operator - (const Complex &c1, const Complex &c2){    return Complex(c1.real - c2.real, c1.image - c2.image);}Complex operator * (const Complex &c1, const Complex &c2){    return Complex(c1.real*c2.real - c1.image*c2.image, c1.real*c2.image + c1.image*c2.real);}int rev(int id, int len){    int ret = 0;    for(int i = 0; (1 << i) < len; i++)    {        ret <<= 1;        if(id & (1 << i)) ret |= 1;    }    return ret;}//当DFT= 1时是DFT, DFT = -1则是逆DFTComplex* IterativeFFT(Complex* a, int len, int DFT)//对长度为len(2的幂)的数组进行DFT变换{    Complex* A = new Complex[len];//用A数组存储数组a分组之后新的顺序    for(int i = 0; i < len; i++)        A[rev(i, len)] = a[i];    for(int s = 1; (1 << s) <= len; s++)    {        int m = (1 << s);        Complex wm = Complex(cos(DFT*2*PI/m), sin(DFT*2*PI/m));        for(int k = 0; k < len; k += m)//这一层结点的包含数组元素个数都是(1 << s)        {            Complex w = Complex(1, 0);            for(int j = 0; j < (m >> 1); j++)//折半引理, 根据两个子节点计算父亲节点            {                Complex t = w*A[k + j + (m >> 1)];                Complex u = A[k + j];                A[k + j] = u + t;                A[k + j + (m >> 1)] = u - t;                w = w*wm;            }        }    }    if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;    return A;}
原创粉丝点击