快速傅里葉變換

【注】參考自算法導論慕爬。

1. 簡介

快速傅里葉變換(FFT)是實現(xiàn)離散傅里葉變換(DFT)和離散傅里葉逆變換(IDFT)的快速算法,其時間復雜度為 O(n \log n)隶糕。DFT 在實際生活中有很多應用坝疼,比如通過離散傅里葉變換翠拣,可以將系數(shù)表示的多項式轉(zhuǎn)為點值表示的多項式共耍,從而使得多項式的乘法的復雜度由 O(n^2) 降為 O(n) 虑灰。

2. 實現(xiàn)

FFT 涉及到離散傅里葉變換的知識,F(xiàn)FT 能夠?qū)崿F(xiàn)的基礎在于折半定理痹兜,折半定理提供了求大整數(shù)的 DFT 的一種思路:即通過將大整數(shù)不斷劃分為兩部分穆咐,然后分別求出兩部分的 DFT,最后再合并,本質(zhì)是一種分治的思想对湃。

3. 代碼

3.1 快速傅里葉變換模板

#include <bits/stdc++.h>
using namespace std;

// 復數(shù)
struct Complex{
    double re, im;
    Complex(): re(0), im(0) {}
    Complex(double _re, double _im): re(_re), im(_im) {}
    Complex operator + (const Complex cp) const {
        return Complex(re+cp.re, im+cp.im);
    }
    Complex operator - (const Complex cp) const {
        return Complex(re-cp.re, im-cp.im);
    }
    Complex operator * (const Complex cp) const {
        return Complex(re*cp.re - im*cp.im, re*cp.im + im*cp.re);
    }
    Complex operator / (const double div) const {
        return Complex(re/div, im/div);
    }
    void operator = (const Complex cp) {
        re = cp.re;
        im = cp.im;
    }
};
// 快速傅里葉變換
struct FFT{
    #ifndef _FFT_
    #define _FFT_
    #define ll int
    #define MAXN 3000005
    #endif
    // 待計算
    ll cnt;         // 位數(shù)
    ll len;         // 數(shù)組大醒陆小(len = 2^cnt)
    ll r[MAXN];     // 位逆序置換數(shù)組
    const double Pi = acos(-1.0);
    FFT(): cnt(0), len(0) {}
    // 構(gòu)建 2 的冪次的系數(shù)數(shù)組
    void build(Complex a[], ll curlen) {
        // 計算 len 所占位數(shù)
        cnt = 1;
        len = curlen;
        while((len >>= 1))   ++cnt;
        // 找到最小的大于等于 curlen 的 2 的冪值
        len = (ll)1 << cnt;    
        if(len-curlen == curlen) {
            --cnt;
            len = curlen;
        }
        // 超出 curlen 部分的系數(shù)補 0 
        for(ll i = curlen; i < len; ++i)    a[i] = Complex(0,0);
    }
    // 計算位逆序置換
    void bitReverse() {
        for(ll i = 0; i < len; ++i) r[i] = i;
        // 本質(zhì)是動態(tài)規(guī)劃
        // 利用 i>>1 的位逆序推導出 i 的位逆序
        for(ll i = 0; i < len; ++i) r[i] = (r[i>>1]>>1) | ((i&1)<<(cnt-1));
    }
    // 快速傅里葉變換
    void DFT(Complex y[]) {
        // 位逆序置換
        for(ll i = 0; i < len; ++i) {
            if(i < r[i])    swap(y[i], y[r[i]]);
        }
        // 合并 DFT
        // 枚舉合并長度
        for(ll i = 1; i < len; i<<=1) {
            // 主單位復根
            Complex wn(cos(Pi/i), sin(Pi/i));
            // 枚舉合并組首下標
            for(ll j = 0; j < len; j += (i<<1)) {
                Complex w(1,0);
                // 合并組 DFT
                for(ll k = j; k < j+i; ++k, w=w*wn) {
                    Complex u = y[k];
                    Complex v = w*y[k+i];
                    y[k] = u + v;
                    y[k+i] = u - v;
                }
            }
        }
    }
    // 快速傅里葉逆變換
    void IDFT(Complex a[]) {
        // 位逆序置換
        for(ll i = 0; i < len; ++i) {
            if(i < r[i])    swap(a[i], a[r[i]]);
        }
        // 合并 DFT
        // 枚舉合并長度
        for(ll i = 1; i < len; i<<=1) {
            // 主單位復根
            Complex wn(cos(Pi/i), -sin(Pi/i));
            // 枚舉合并組首下標
            for(ll j = 0; j < len; j += (i<<1)) {
                Complex w(1,0);
                // 合并組 DFT
                for(ll k = j; k < j+i; ++k, w=w*wn) {
                    Complex u = a[k];
                    Complex v = w*a[k+i];
                    a[k] = u + v;
                    a[k+i] = u - v;
                }
            }
        }
        for(ll i = 0; i < len; ++i) {
            a[i] = a[i]/len;
        }
    }
};

3.2 大整數(shù)乘法模板

#include <bits/stdc++.h>
using namespace std;

// 復數(shù)
struct Complex{
    double re, im;
    Complex(): re(0), im(0) {}
    Complex(double _re, double _im): re(_re), im(_im) {}
    Complex operator + (const Complex cp) const {
        return Complex(re+cp.re, im+cp.im);
    }
    Complex operator - (const Complex cp) const {
        return Complex(re-cp.re, im-cp.im);
    }
    Complex operator * (const Complex cp) const {
        return Complex(re*cp.re - im*cp.im, re*cp.im + im*cp.re);
    }
    Complex operator / (const double div) const {
        return Complex(re/div, im/div);
    }
    void operator = (const Complex cp) {
        re = cp.re;
        im = cp.im;
    }
};
// 大整數(shù)乘法(快速傅里葉變換實現(xiàn))
struct BigIntegersMultiply{
    #ifndef _BIGINTEGERSMULTIPLY_
    #define _BIGINTEGERSMULTIPLY_
    #define ll int
    #define MAXN 3000005
    #endif
    const double Pi = acos(-1.0);
    // 待計算
    ll cnt;         // 位數(shù)
    ll len;         // 最終乘積數(shù)組大小(len = 2^cnt)
    ll r[MAXN];     // 位逆序置換數(shù)組
    ll ans[MAXN];   // 最終答案(逆序存儲)
    // 待輸入
    ll len1, len2;  // 初始系數(shù)數(shù)組大小
    Complex x1[MAXN], x2[MAXN]; // 兩個大整數(shù)乘數(shù)(逆序儲存)

    BigIntegersMultiply(): cnt(0), len(0) {}
    // 構(gòu)建 2 的冪次的系數(shù)數(shù)組
    void build() {
        // 計算兩個乘數(shù)最小的公共 2 的冪次 len 及所占位數(shù) cnt
        cnt = 0, len = 1;
        while(len < (len1<<1) || len < (len2<<1))   ++cnt, len <<= 1;
        // 超出 curlen 部分的系數(shù)補 0 
        for(ll i = len1; i < len; ++i)  x1[i] = Complex(0,0);
        for(ll i = len2; i < len; ++i)  x2[i] = Complex(0,0);
    }
    // 計算位逆序置換
    void bitReverse() {
        for(ll i = 0; i < len; ++i) r[i] = i;
        // 本質(zhì)是動態(tài)規(guī)劃
        // 利用 i>>1 的位逆序推導出 i 的位逆序
        for(ll i = 0; i < len; ++i) r[i] = (r[i>>1]>>1) | ((i&1)<<(cnt-1));
    }
    // 快速傅里葉變換
    void DFT(Complex x[]) {
        // 位逆序置換
        for(ll i = 0; i < len; ++i) {
            if(i < r[i])    swap(x[i], x[r[i]]);
        }
        // 合并 DFT
        // 枚舉合并長度
        for(ll i = 1; i < len; i<<=1) {
            // 主單位復根
            Complex wn(cos(Pi/i), sin(Pi/i));
            // 枚舉合并組首下標
            for(ll j = 0; j < len; j += (i<<1)) {
                Complex w(1,0);
                // 合并組 DFT
                for(ll k = j; k < j+i; ++k, w=w*wn) {
                    Complex u = x[k];
                    Complex v = w*x[k+i];
                    x[k] = u + v;
                    x[k+i] = u - v;
                }
            }
        }
    }
    // 快速傅里葉逆變換
    void IDFT(Complex x[]) {
        // 位逆序置換
        for(ll i = 0; i < len; ++i) {
            if(i < r[i])    swap(x[i], x[r[i]]);
        }
        // 合并 DFT
        // 枚舉合并長度
        for(ll i = 1; i < len; i<<=1) {
            // 主單位復根
            Complex wn(cos(Pi/i), -sin(Pi/i));
            // 枚舉合并組首下標
            for(ll j = 0; j < len; j += (i<<1)) {
                Complex w(1,0);
                // 合并組 DFT
                for(ll k = j; k < j+i; ++k, w=w*wn) {
                    Complex u = x[k];
                    Complex v = w*x[k+i];
                    x[k] = u + v;
                    x[k+i] = u - v;
                }
            }
        }
        for(ll i = 0; i < len; ++i) {
            x[i] = x[i]/len;
        }
    }
    // 大整數(shù)乘法
    void bigMultiply() {
        build();
        bitReverse();
        DFT(x1), DFT(x2);
        for(ll i = 0; i < len; ++i) x1[i] = x1[i]*x2[i];
        IDFT(x1);
        for(ll i = 0; i < len; ++i) ans[i] = ll(x1[i].re + 0.5);
        for(ll i = 0; i < len; ++i) ans[i+1] += ans[i]/10, ans[i] %= 10;
        len = len1 + len2 - 1;
        while(len && !ans[len]) --len;
        ++len;
    }
};
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拍柒,一起剝皮案震驚了整個濱河市心傀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拆讯,老刑警劉巖脂男,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異往果,居然都是意外死亡疆液,警方通過查閱死者的電腦和手機一铅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門陕贮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人潘飘,你說我怎么就攤上這事肮之。” “怎么了卜录?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵戈擒,是天一觀的道長。 經(jīng)常有香客問我艰毒,道長筐高,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任丑瞧,我火速辦了婚禮柑土,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绊汹。我一直安慰自己稽屏,他們只是感情好,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布西乖。 她就那樣靜靜地躺著狐榔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪获雕。 梳的紋絲不亂的頭發(fā)上薄腻,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音届案,去河邊找鬼被廓。 笑死,一個胖子當著我的面吹牛萝玷,可吹牛的內(nèi)容都是我干的嫁乘。 我是一名探鬼主播昆婿,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蜓斧!你這毒婦竟也來了仓蛆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤挎春,失蹤者是張志新(化名)和其女友劉穎看疙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體直奋,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡能庆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脚线。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搁胆。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖邮绿,靈堂內(nèi)的尸體忽然破棺而出渠旁,到底是詐尸還是另有隱情,我是刑警寧澤船逮,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布顾腊,位于F島的核電站,受9級特大地震影響挖胃,放射性物質(zhì)發(fā)生泄漏杂靶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一酱鸭、第九天 我趴在偏房一處隱蔽的房頂上張望吗垮。 院中可真熱鬧,春花似錦凛辣、人聲如沸抱既。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闽烙,卻和暖如春蝗敢,著一層夾襖步出監(jiān)牢的瞬間捷泞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工寿谴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锁右,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像咏瑟,于是被迫代替她去往敵國和親拂到。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容