多项式与快速傅里叶变换

本来这里我想写仔细一点,但我想,傅里叶变换确实比较复杂,况且,我也无法在没有任何参照的情况下写出下面的代码,所以我也就贴上代码,然后写写我在实现过程中的一些想法。下面的代码基本上都是和《算法导论》上面的实现一样的。

多项式的两种表示方法

说实话,第一次看到多项式式的点值形式的时候,我是比较惊讶的,因为在我的第一感觉是,点值无法确定一个多项式,但书中用了一个很简单的说明就让我明白了我的直觉是错的。对于多项式点值表达式的乘法也让我印象深刻。其实比较简单,如果,那么对于点,必然存在,也就是对应两个点的直接相乘。

以前学《信号与系统》的时候,我就知道时域卷积对应于频域相乘,但是我还是不太清楚为什么,这里给了我一点直观的感觉。

算法的基本思路

多项式相乘,如果使用直接的方法,那么需要的时间,而点值形式的多项式乘法只需要,这时候我们很自然的想到,能否找到一种快速的方法,实现两种形式的转化。

先考虑从系数形式到点值形式的转化,根据霍纳法则,我们可以在的时间内求得一个点对应的值,但一共有n个点,也需要。这时候,我们想到了一些算法设计的技巧,比如分治法。我们可以把多项式按照奇偶划分成两部分,然后分别求解,然后合并。则看上去很不错,但写出递归式:

这里不好的地方在于,递归过程中,我们要求的n个点没有减少,如果我们能在递归过程中,减少对点的求解,也就是说,让根之间有关系,减少计算,如果能减少到一半,那么递归式变成了:

这样,这个算法就比直接求解要优秀。

这里,选择的特别的根是单位复数根,它有一个很重要的性质。
\begin{array}{l}{f\left(\omega_{n}^{i+n / 2}\right)=f\left(-\omega_{n}^{i}\right)=} \\ {a_{0}+a_{2} \omega_{n / 2}^{i}+a_{4}\left(\omega_{n / 2}^{i}\right)^{2} \ldots+a_{n-2}\left(\omega_{n / 2}^{i}\right)^{(n-2) / 2}} \\ {-\omega_{n}^{i}\left(a_{1}+a_{3} \omega_{n / 2}^{i}+a_{5}\left(\omega_{n / 2}^{i}\right)^{2} \ldots+a_{n-1}\left(\omega_{n / 2}^{i}\right)^{(n-2) / 2}\right)}\end{array}

利用这个性质,可以在计算的过程中,实现减少点的计算。

计算完了,我们还要转回来,根据复数根的性质,我们发现,这个矩阵的逆矩阵很好求,就是虚部取反,除以n,然后问题有回到了前面。

#include 
#include 
#include 
#include 
#include 
using namespace std;

const double PI = acos(-1);

void fft(vector > &a ,int n, int op){
    if(n == 1){
        return;
    }
    //divide
    vector > a0(n/2,0);
    vector > a1(n/2,0);
    for(int i=0;i w(1,0); //通过巧妙的选取根来减少计算
    complex wn(cos(2 * PI / n),sin(2 * PI * op /n));
    for(int i=0;i > &a ,int n, int op){
    //开始输入的时候位置为对应计数比特翻转之后的值
    int len = (int)log2(n) - 1;
    vector rev(n,0);
    for(int i=0;i>1]>>1) | ((i&0x01)< wn(cos( PI / i),sin(PI * op / i));
        for(int k=0;k w(1,0);
            for(int j=0;j t = w * a[k + j + i];
                complex u = a[k + j];
                a[k+j] = u + t;
                a[k+j+i] = u - t;
                w *= wn;
            }
        }
    }
    if(op == -1){
        for(int i=0;i>n>>m;
    int n1= 1;
    while(n1<=m+n){
        n1 <<= 1;
    }
    vector > v1(n1,0);
    vector> v2(n1,0);
    vector> res(n1,0);
    int tmp;
    for(int i=0;i<=n;i++){
        cin>>v1[i];
    }
    for(int i=0;i<=m;i++){
        cin>>v2[i];
    }
    //将系数形式转化为点值形式
    fft(v1,n1,1);
    fft(v2,n1,1);
    
    //利用点值形式计算多项式乘法
    for(int i=0;i

代码的注释写得挺多的,我个人只能说对这个算法有一个基本的理解,主要是为了记录学习的过程。如果有错误,请多多包涵。

你可能感兴趣的:(多项式与快速傅里叶变换)