利用快速傅里叶计算多项式相乘

FFT多项式计算

傅里叶变换是一个很大的概念,里面包含太多太多东西,整篇在傅里叶变换中只是一个小应用而已。就我的理解,描述快速傅里叶变换优化多项式乘法的原理和具体实践。

傅里叶变换

傅里叶变换是一种线性积分变换,用于信号在时域(或空域)和频域之间的变换。

一般情况下,若傅里叶变换一词不加任何限定语,则指的是连续傅里叶变换。(连续)傅里叶变换定义如下:傅里叶变换将可积函数f:RCf: \mathbb{R} \rightarrow \mathbb{C}表示成复指数函数的积分或级数性质。
f^(ξ)=f(x)e2πixξdx \hat{f} \left( \xi \right) = \int_{-\infty}^{\infty} f \left( x \right) e^{-2\pi i x \xi } dx
其中ξ\xi为任意实数。自变量xx表示时间,变换变量ξ\xi表示频率。在多数情况下,傅里叶变换是可逆的,即可以通过f^\hat{f}得到其原函数ff,定义如下:
f(ξ)=f^(ξ)e2πiξxdξ f \left( \xi \right) = \int_{-\infty}^{\infty} \hat{f} \left( \xi \right) e^{2\pi i \xi x} d \xi

离散傅里叶变换

为了在科学计算和数字信号处理等领域使用计算机进行傅里叶变换,必须将函数xnx_{n}定义在离散点而非连续域内,且须满足有限性和周期性等提交。这种情况下,使用离散傅里叶变换(Discrete Fourier Transform,DFT),DFT是傅里叶变换在时域和频域上都呈离散的形式,函数定义如下:
x^n=k=0N1xkei2πNknn=0,,N1 \hat{x}_{n} = \sum_{k=0}^{N-1}x_{k} e^{-i \frac{2\pi}{N} k n} \qquad n = 0, \cdots, N-1
其逆变换为:
xk=1Nn=0N1x^nei2πNnkk=0,,N1 x_{k} =\frac{1}{N} \sum_{n=0}^{N-1} \hat{x}_{n} e^{-i \frac{2\pi}{N} n k} \qquad k = 0, \cdots, N-1

单位根

数学上,nn次单位根是nn次幂为1的负数。定义为ωn=1(n=1,2,3,)\omega^{n}=1 \quad \left( n=1,2,3,\cdots\right)ω\omegann次单位根。单位的nn次根有nn个:e2πki/n(k=0,1,2,,n1)e^{2\pi k i / n} \quad \left(k=0,1,2,\cdots, n-1 \right)。我们用ωn0,ωn1,,ωnn1\omega_{n}^{0}, \omega_{n}^{1}, \cdots, \omega_{n}^{n-1}表示这个nnnn次根,ωni=e2πki/n\omega_{n}^{i} = e^{2\pi k i / n}

结合欧拉公式,可以很容易得看出,ωnk=e2πkni=cos(2πkn)+isin(2πkn)=1\omega_{n}^{k} = e ^{2\pi \frac{k}{n} i } = cos\left(2\pi \frac{k}{n}\right) + i \cdot sin \left( 2\pi \frac{k}{n} \right) = 1

对于n0,k0,d0n \geq 0, k \geq 0, d \geq 0,有ωdndk=(e2πi/dn)dk=(e2πi/n)k=ωnk\omega_{dn}^{dk}=\left( e^{2\pi i /dn}\right)^{dk} = \left( e^{2\pi i /n}\right)^{k} = \omega_{n}^{k}

对于n\forall n满足n>0n > 02n2 | n,有ωnk+n2=e2πin(k+n2)=e2πik/neπi=ωnk(cosπ+isinπ)=ωnk\omega_{n}^{k + \frac{n}{2}} = e ^ {\frac{2 \pi i}{n} \left( k + \frac{n}{2}\right)} = e^{2 \pi i k / n} e^{\pi i} = \omega_{n}^{k} \left( cos \pi + i sin \pi \right) = -\omega_{n}^{k}

多项式

形如a0+a1x++anxna_{0} + a_{1} x + \cdots + a_{n} x^{n}的多项式被称为多项式,记为A(x)=i=0n1aixiA\left( x \right) = \sum_{i=0}^{n-1} a_{i} x^{i},其中ai,i=0,1,,na_{i}, i=0,1,\cdots,n为系数,用系数表示多项式为a=(a0,a1,,an)\vec{a}=\left(a_{0}, a_{1}, \cdots, a_{n} \right)a\vec{a}就为多项式的系数表示法

我们知道通过n+1n+1个不同的点{(x0,y0),,(xn,yn)}\left\{\left(x_{0},y_{0}\right),\cdots, \left(x_{n},y_{n}\right) \right\}就可以唯一确定一个nn次多项式,{(x0,y0),,(xn,yn)}\left\{\left(x_{0},y_{0}\right),\cdots, \left(x_{n},y_{n}\right) \right\}为多项式的点值表示法

快速傅里叶变换

快速傅里叶变换(Fast Fourier Transform,FFT),是计算离散傅里叶变换及其逆变换的快算算法。按照DFT的定义,计算一个长为nn的序列需要的时间复杂度为O(n2)O\left( n^{2} \right),而FFT通过把DFT矩阵分解为稀疏因子之积来快速计算变换,从而将时间复杂度降到O(nlogn)O\left( nlog n \right)

库里-图基快速傅里叶变换算法是最常见的FFT算法,基于分治策略,可将时间复杂度降为O(nlogn)O\left( nlog n \right)

假设一个多项式表示为A(x)=i=0n1aixiA\left( x \right) = \sum_{i=0}^{n-1} a_{i} x^{i},用系数表示法表示就是向量a=(a0,a1,,an)\vec{a}=\left(a_{0}, a_{1}, \cdots, a_{n} \right)。用点值表示,将nnnn次单位根ωn0,ωn1,,ωnn1\omega_{n}^{0}, \omega_{n}^{1}, \cdots, \omega_{n}^{n-1}带入到多项式为A(ωnk)=i=0n1aiωnki,k=0,1,,n1A\left( \omega_{n}^{k} \right) = \sum_{i=0}^{n-1} a_{i} \omega_{n}^{ki}, k = 0,1,\cdots, n-1。现在我们和DFT定义的公式比对,A(ωni)A\left(\omega_{n}^{i}\right)就是是aia_{i}的DFT,那么aia_{i}就是A(ωni)A\left(\omega_{n}^{i}\right)就是的IDFT。记为A(ωni)=DFT(ai)A\left(\omega_{n}^{i}\right) = DFT\left( a_{i} \right)

下面我们就依据上面陈列的基础基础,对A(ωnk)A\left(\omega_{n}^{k}\right)进行公式推导:
A(ωnk)=i=0n1aiωnki=i=0n21a2iωn2ki+ωnki=0n21a2i+1ωn2ki=i=0n21a2iωn2ki+ωnki=0n21a2i+1ωn2ki       \begin{matrix} A\left(\omega_{n}^{k}\right) &= \sum_{i=0}^{n-1} a_{i}\omega_{n}^{ki} \qquad \qquad \qquad \qquad \qquad \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{n}^{2ki} + \omega_{n}^{k} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{n}^{2ki} \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{\frac{n}{2}}^{ki} + \omega_{n}^{k} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{\frac{n}{2}}^{ki} \; \; \; \end{matrix}
又由于
A(ωnk+n2)=i=0n1aiωn(k+n2)i=i=0n21a2iωn2ki+ωnk+n2i=0n21a2i+1ωn2ki=i=0n21a2iωn2kiωnki=0n21a2i+1ωn2ki \begin{matrix} A\left(\omega_{n}^{k + \frac{n}{2}}\right) &= \sum_{i=0}^{n-1} a_{i}\omega_{n}^{\left(k+ \frac{n}{2}\right)i}\qquad \qquad \qquad \qquad \quad \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{n}^{2ki} + \omega_{n}^{k + \frac{n}{2}} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{n}^{2ki} \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{\frac{n}{2}}^{ki} - \omega_{n}^{k} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{\frac{n}{2}}^{ki} \qquad \end{matrix}
可以看出,对于k<n2k < \frac{n}{2}时,只要代入ωn2,ωn22,,ωn2n21\omega_{\frac{n}{2}}, \omega_{\frac{n}{2}}^{2}, \cdots, \omega_{\frac{n}{2}}^{\frac{n}{2} - 1},就可以求出A(ωnk)A\left(\omega_{n}^{k}\right)A(ωnk+n2)A\left(\omega_{n}^{k + \frac{n}{2}}\right)
A(ωnk)=A1(ωn2k)+A2(ωn2k)A(ωnk+n2)=A1(ωn2k)A2(ωn2k)(1) \begin{matrix} A\left(\omega_{n}^{k}\right) \quad =& A_{1}\left(\omega_{\frac{n}{2}}^{k}\right) + A_{2}\left(\omega_{\frac{n}{2}}^{k}\right) \\ \\ A\left(\omega_{n}^{k + \frac{n}{2}}\right) = & A_{1}\left(\omega_{\frac{n}{2}}^{k}\right) - A_{2}\left(\omega_{\frac{n}{2}}^{k}\right) \end{matrix} (公式1)
不递归的伪代码如下所示:

void get_rev(int bit)
{
    for(int i=0; i<(1<<bit); i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
/**
n为2的幂次方
op==1,DFT
op==-1,IDFT
*/
void fft(cd *a,int n,int op)
{
    for(int i=0; i<n; i++)
    {
        if(i<rev[i])
        {
            cd tmp = a[i];
            a[i] = a[rev[i]];
            a[rev[i]] = tmp;
        }
    }
    for(int step=1; step<n; step<<=1)
    {
        cd wn=exp(cd(0,op*PI/step));
        for(int j=0; j<n; j+=step<<1)
        {
            cd wnk(1,0);
            for(int k=j; k<j+step; k++)
            {
                cd x=a[k];
                cd y=wnk*a[k+step];
                a[k]=x+y;
                a[k+step]=x-y;
                wnk*=wn;
            }
        }
    }
    if(op==-1)
    {
        for(int i=0; i<n; i++)
            a[i]/=n;
    }
}

费了好大经历才把代码和原理结合在一起,在网上搜了好久都没有理解为何代码要这样写,后来在离散傅里叶变换和快速傅里叶变换课件中搜到一张图,瞬间明白,其中的子操作被称为蝴蝶操作。如下图所示,展示了一个8个点的DFT是怎样分解成4个2个点的DFT。
在这里插入图片描述
蝴蝶操作见下图,类似于蝴蝶形状,是不是和上述公式1的计算相符合。
在这里插入图片描述

FFT实现多项式乘法

当现在有多项式A(x)A\left(x\right)和多项式B(x)B\left(x\right),令C(x)=A(x)B(x)C\left(x\right) = A\left(x\right) \cdot B\left(x\right),那么如何求CC的系数cjc_{j}呢?

先引入一个定理,函数卷积的傅里叶变换是函数傅里叶变换的乘积。$C\left(x\right) 中次数为j的项由A \left(x\right) 中次数为k的项和B\left(x\right) 中次数j-k为的项相乘得到。那么我们先通过C\left(\omega_{n}\right) = A\left(\omega_{n}\right) B\left(\omega_{n},\right)IDFT,然后在通过IDFT计算c_{j}$。

    fft(a,n,1);
    fft(b,n,1);
    for(int i=0; i<s; i++) a[i]*=b[i];
    fft(a,n,-1);

备注

欧拉公式eix=cosx+isinxe^{ix} = cos x + i \cdot sin x

参考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章