FFT總結

FFT,快速傅里葉變換,其實也沒那麼神祕,就是一種變換方式罷了。在音頻視頻傳送中有很多應用,此處不贅述,只談談其在算法競賽中的用途。
FFT,一般用來快速乘,當然還有些其他應用。
比如給你a,b,兩個數,他們很大,超過了10w位,n^2的乘法就顯得太慢。
而FFT就是能nlogn時間解決此類問題的算法。

對於一個多項式A(x) = a0+a1*x+a2*x^2+...+an-1*x^(n-1)
這種是這個多項式的一個表示方法,用係數和權來表示,這樣乘起來是n^2的,我們需要轉換下表示法,及用點值表示法。什麼是點值表示法呢?感覺上來說,你可以把A(x)當成一條函數曲線在直角座標系中畫出來,上面選取n個不同的點(n次就選n個)這n個點就能確定這條直線了,那麼就用這n個點的集合來表示A(x),這叫點值表示法。
 點選取哪些呢?
 選取n次單位復根作爲來求點值是比較巧妙的做法。
 n次單位復根是指 w^n = 1,w是複數,這樣的根恰好有n個,爲e^(2*PI*i*k/n) k(0,1,...,n-1);
 可以用歐拉公式e^(i*u) = cos(u) + i*sin(u)
 將其展開,
 然後多項式的卷積,通過變換到點值表達式,就成了複數的對應積在求和,複雜度爲O(n)
 如何快速的從係數表示法轉化到點值表示法呢?
 那就是FFT了。
 至於FFT和n次單位復根詳解,看這篇博客吧,我懶得寫公式了http://blog.csdn.net/acdreamers/article/details/39005227
 所以多項式乘法就是,先轉化爲點值表達式,然後求積,再反轉爲係數表達式,然後隨你處理。
 核心代碼如下:
struct Virt {
    double r, i;
    Virt(double _r = 0, double _i = 0) : r(_r), i(_i) {}
    Virt operator+ (Virt& rhs) {
        return Virt(r + rhs.r, i + rhs.i);
    }
    Virt operator- (Virt& rhs) {
        return Virt(r - rhs.r, i - rhs.i);
    }
    Virt operator* (Virt& rhs) {
        return Virt(r * rhs.r - i * rhs.i, r * rhs.i + i * rhs.r);
    }
};
void Rader(Virt F[], int len) {
    int j = len >> 1;
    for(int i = 1; i < len - 1; i++) {
        if(i < j) swap(F[i], F[j]);
        int k = len >> 1;
        while(j >= k) {
            j -= k;
            k >>= 1;
        }
        if(j < k) j += k;
    }
}
void FFT(Virt F[], int len, int on) {
    Rader(F, len);
    for(int h = 2; h <= len; h <<= 1) {
        Virt wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
        for(int j = 0; j < len; j += h) {
            Virt w(1, 0);
            for(int k = j; k < j + h / 2; k++) {
                Virt u = F[k];
                Virt t = w * F[k + h / 2];
                F[k] = u + t;
                F[k + h / 2] = u - t;
                w = w * wn;
            }
        }
    }
    if(on == -1)
        for(int i = 0; i < len; i++)
            F[i].r /= len;
}
void Conv(Virt a[], Virt b[], int len) {
    FFT(a, len, 1);
    FFT(b, len, 1);
    for(int i = 0; i < len; i++)
        a[i] = a[i] * b[i];
    FFT(a, len, -1);
}
Virt va[maxn], vb[maxn];
int len;

最後結果存在result裏面

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