快速傅里葉變換(FFT)

快速傅里葉變換大學的時候就學了,可現在想起來都還回去了已經。前兩天做到一道大數乘法題:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1028

需要用到傅里葉變換,就又從新拿出算法導論看了一遍,記下來加深一下記憶!!


傅里葉變換多用於信號處理,在頻率域和時間域之間進行變換,我只知道這麼多了!!

對於大數乘法,對每個大數,可以表示成一個多項式,如果直接進行多項式相乘,那麼時間複雜度是O(n^2)的。那爲什麼用了傅里葉變換就可以做到O(n*lg(n))了呢?聽我慢慢道來


一個n次多項式可以有兩種表示方法:

1.係數表示法:即用係數向量來表示一個n次多項式(a0, a1, a2,......an-1)

2.點值法:用n個點值對來表示,{(x0, y0), (x1, y1),......(xn-1, yn-1)};要求這n個x不能相同,爲什麼呢?其實說白了,給你n個點值對,而且你知道多項式是一個n次多項式,那麼你可以列出n個方程,解這個方程組,你就可以得出這個n次多項式的係數,之所以要求這n個x不能相同,就是爲了保證你能夠得到n個有效的方程,這裏面會有一個範德蒙德矩陣,線性代數裏學過,也忘記了,就記得這個矩陣的行列式可以直接套公式~~~


閒言少敘,點值法和係數法其實是一一對應的,如果給你兩個點值法表示的多項式[兩個多項式用的是同一組(x0, x1, ...xn)],讓你求他們點值法表示的乘積:很明顯,直接對應相乘就好了,時間複雜度爲O(n);FFT之所以能將時間複雜度從O(n^2)降到O(n*lgn),就是因爲它找到了將點值法表示的多項式在O(n*lgn)時間內轉化成對應的係數表示法!


整個過程是這樣的:

1.給兩個係數表示法表示的n次多項式 ===》》 變換成兩個點值表示法表示的多項式:這一步涉及到了擴充,從n次擴充到了2n次,稱之爲求值

2.給兩個點值表示法表示的多項式對應項相乘得到對應結果的點值法表示

3.將結果的點值法表示轉化成係數法表示


那用什麼方法可以在O(n*lgn)時間內完成點值法和係數法之間的轉換呢?

這就是FFT

其中的思想主要是二分,具體方法我也沒有弄明白,遞歸實現的FFT還可以看懂,迭代實現的FFT真心沒有弄明白,僅僅那個位置換操作的寫法我就看不懂了~~~~

先說從係數法到點值法的轉換:

遞歸實現的FFT裏是利用了單位復根的特殊性質:n個n次單位復根,把這n個複數平方,你會得到n/2個不同的數,每個出現兩次;所以原問題就是這樣被分成了更小的子問題~~~~T(n) = 2T(n/2) + O(n),其實最後一項不是O,而是西塔,你懂的,不好打出來的字符!所以整個時間複雜度就成了O(n*lgn)

具體是這樣的:給你一個n次多項式的係數表示A(x) = a0 + a1*x^1 + a2*x^2......an-1*x^(n-1);

讓你求他的點值表示,你需要求n個點值對,那麼這時候你選擇求這n個點對應的值,怎麼求呢?

你把這個n次多項式的係數表示拆成兩組:

一組是偶數項係數:A[0](x) = a0 + a2*x + a4*x^2 + .......a(n-2) * x^(n/2 - 1)

一組是奇數項係數:A[1](x) = a1 + a3*x + a5*x^2 + .......a(n-1) * x^(n/2 - 1)

這樣原來的A(x) = A[0](x^2) + x * A[1](x^2)

所以原來你需要求A(x)在 這n個點的值,現在你只要求這n個點中的一半就可以了,因爲這n個點的平方結果中只有n/2個不同的值.也就是說你只要求出A[0](x)和A[1](x)在點這n/2點處的值就可以了,具體爲什麼就不說了,一算就明白了;最後貼一個FFT的迭代實現,具體我也不是很明白,希望以後能弄懂~~~~~


// Copyright (c) 2013
// Author: Muye ([email protected])

#include <iostream>
#include <complex>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;

const double Pi = acos(-1);
const int LEN = 262145;
int ans[LEN] = {0};
complex<double> A[LEN], B[LEN], C[LEN];

int Init(string& sa, string& sb) {
    int la = sa.length();
    int lb = sb.length();
    int lc = 1;
    int l = max(la, lb) * 2 - 1;
    while(lc < l) lc <<= 1;
    for(int i = 0; i < la; ++i) A[i] = sa[la - i - 1] - '0';
    for(int i = la; i < lc; ++i) A[i] = 0;
    for(int i = 0; i < lb; ++i) B[i] = sb[lb - i - 1] - '0';
    for(int i = lb; i < lc; ++i) B[i] = 0;
    return lc;
}

void Bit_Reverse_Copy(complex<double> *p, int n) {
    for(int i = 1, j = n >> 1, k; i < n - 1; ++i) {
        if(i < j) swap(p[i], p[j]);
        k = n >> 1;
        while(j >= k) {
            j -= k;
            k >>= 1;
        }
        if(j < k) j += k;
    }
}

void Iterative_FFT(complex<double> *p, int n, int inverse = 1) {
    Bit_Reverse_Copy(p, n);
    for(int m = 2; m <= n; m <<= 1) {
        complex<double> wn(cos(inverse * 2 * Pi / m), sin(inverse * 2 * Pi / m));
        for(int k = 0; k < n; k += m) {
            complex<double> w(1, 0);
            for(int j = 0; j < m >> 1; ++j) {
                complex<double> t = w * p[k + j + m / 2];
                complex<double> u = p[k + j];
                p[k + j] = u + t;
                p[k + j + m / 2] = u - t;
                w = w * wn;
            }

        }
    }
    if(inverse == -1) {
        for(int i = 0; i < n; ++i) p[i] /= n;
    }
}

int main() {
    string sa, sb;
    cin >> sa >> sb;
    int n = Init(sa, sb);
    Iterative_FFT(A, n);
    Iterative_FFT(B, n);
    for(int i = 0; i < n; ++i) C[i] = A[i] * B[i];
    Iterative_FFT(C, n, -1);
    for(int i = 0; i < n; ++i) ans[i] = C[i].real() + 0.5;
    for(int i = 0; i < n; ++i) {
        ans[i+1] += ans[i] / 10;
        ans[i] %= 10;
    }
    while(!ans[--n] && n);
    while(n >= 0) cout << ans[n--];
    cout << endl;
    return 0;
}



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