我的 “基2-快速傅立葉變換” 的樸素實現(xiàn)(代碼部分)

作為學習過程,我們盡量少用 庫函數(shù)。由于我們需要進行復數(shù)乘法梭纹、加法運算,下面先粗糙地定義一下復數(shù)運算:

template<typename T>
struct Complex {
    T r;
    T i;

    Complex(): r((T)0), i((T)0) { }

    Complex(T real, T image): r(real), i(image) { }

    Complex(const Complex<T>& other) {
        r = other.r;
        i = other.i;
    }
    
    Complex<T>& operator = (const Complex<T>& c) {
        if (&c == this) return *this;

        r = c.r;
        i = c.i;
        return *this;
    }
};
    
template<typename T>
Complex<T> operator + (const Complex<T>& c1, const Complex<T>& c2) {
    Complex<T> x;
    x.r = c1.r + c2.r;
    x.i = c1.i + c2.i;
    return x;
}
    
template<typename T>
Complex<T> operator - (const Complex<T>& c1, const Complex<T>& c2) {
    Complex<T> x;
    x.r = c1.r - c2.r;
    x.i = c1.i - c2.i;
    return x;
}
    
template<typename T>
Complex<T> operator * (const Complex<T>& c1, const Complex<T>& c2) {
    Complex<T> x;
    x.r = c1.r * c2.r - c1.i * c2.i;
    x.i = c1.r * c2.i + c1.i * c2.r;
    return x;
}

我們考慮使用靜態(tài)大小的 FFT 樣本點數(shù) N致份,也就是在編譯期变抽,我們已經知道 FFT 運算的樣本點數(shù)了。因此我們將該參數(shù)直接作為 C++ 模板參數(shù)進行傳遞氮块。

template <size_t N, typename T>
class naivefft {
public:
    typedef Complex<T> cpx_t;

public:
    naivefft() ;
    void cdft(cpx_t* a) ;

private:
    int order_of_2(int n);

    void bit_reverse(cpx_t* a);

    void butterfly_op(cpx_t* a, int l, int r, cpx_t tw) ;

private:
    cpx_t _twiddle_factors[N/2+1];
    int _stages;
    int _N;
};

我們先來實現(xiàn)一下 蝶形運算绍载,為了減少復數(shù)乘法,我們用一個中間變量保存乘法結果:

template<size_t N, typename T>
void naivefft<N, T>::butterfly_op(cpx_t* a, int l, int r, cpx_t tw) {
    // we get two FFT coefficients with only one multiplication and two additions.
    cpx_t x = a[r] * tw;
    cpx_t y = a[l] + x;
    a[r] = a[l] - x;
    a[l] = y;
}

進行 FFT 主體循環(huán)時滔蝉,最外層的循環(huán)是遍歷層數(shù)击儡,我們稱為 階段 stages;為此蝠引,我們需要一個計算 2 的整數(shù)次冪 的函數(shù):

template<size_t N, typename T>
int naivefft<N, T>::order_of_2(int n) {
    int ord = 0;
    // counting bits followed by the 1
    while (n > 1) {
        ord += 1;
        n >>= 1;
    }
    return ord;
}

不過在我們計算 order_of_2 時阳谍,我們要先確認參數(shù) n 是 2 的整數(shù)次冪,為此我們使用一個 編譯期間進行檢查 的元函數(shù):

template <size_t N>
struct is_order_2 {
    enum { yes = (N > 0) && ((N & (N - 1)) == 0) };
};

在構造函數(shù)中螃概,我們初始化 旋轉因子 需要一個函數(shù)矫夯,即 復數(shù)版 exp 函數(shù),這是我們唯一調用 庫函數(shù) 的地方:

template<typename T>
Complex<T> exp(Complex<T> c) {
    return Complex<T>(std::exp(c.r) * std::cos(c.i), std::exp(c.r) * std::sin(c.i));
}

現(xiàn)在完成我們的構造函數(shù)吊洼,初始化必要的 旋轉因子训貌;

template<size_t N, typename T>
naivefft<N,T>::naivefft() {
    // checking the validity of FFT length at compile stage.
    static_assert(is_order_2<N>::yes, "N must be power of 2.");

    _N = N;
    // the primary root of _Nth order of 1.
    T phi = -2 * std::acos((T)-1) / _N;
    // we only calculate the necessary roots of 1.
    for (size_t i = 0; i < _N / 2 + 1; ++i)
        _twiddle_factors[i] = exp(cpx_t(0, i * phi));

    // calculate the order of 2 of N, and this indicates our fft stages.
    _stages = order_of_2(_N);
}

到此,我們迎來了第一個重要的函數(shù):按位逆序 (bit reversing)冒窍。

template<size_t N, typename T>
void naivefft<N,T>::bit_reverse(cpx_t* a) {
    cpx_t x;
    // traverse each index, since we know the 
    // 1st and last indices keep unchanged,
    // we scan from 1 to _N-2.
    for (int i = 1; i < _N - 1; ++i) {
        int o = _stages;  // valid bits length
        int c = i;        // temp value for index i    
        int ri = 0;       // reversed index of i

        // traverse each bit of i and
        // construct the reversed index of i
        // bit by bit.
        while (o--) {
            ri = (ri << 1) | (c & 1);
            c = (c >> 1);
        }
        
        // swap the two numbers when ri < i
        // to prevent swapping back.
        if (ri < i) {
            x = a[ri];
            a[ri] = a[i];
            a[i] = x;
        }
    }
}

最后递沪,完成我們的 Radix2-DIT-FFT 的主循環(huán),具體過程可以對照著 我的 “基2-快速傅立葉變換” 的樸素實現(xiàn)(推導部分) 中的 圖 3)來看综液;事實上我寫代碼的時候畫了長度為 16 的流圖款慨。

template<size_t N, typename T>
void naivefft<N, T>::cdft(cpx_t* a) {
    // first, permutating the numbers with bit reversed ordering.
    bit_reverse(a);

    // at each stage, we have several independent blocks.
    int block_size = 2;
    // the butterfly stride within the blocks.
    int stride = 1;
    // temp val for calculating the stride 
    // scanning at the twiddle factors table.
    int tw_stride_temp = 1;
    // taversing each stage.
    for (int stage = 0; stage < _stages; stage++) {
        // at new stage, we need to shrinking the twiddle stride,
        // so we double the denominator.
        tw_stride_temp *= 2;
        int tw_stride = _N / tw_stride_temp;
        // traversing each block of current stage.
        for (int b = 0; b < _N; b += block_size) {
            // calculating all the butterfly operations within one block.
            for (int s = 0; s + stride < block_size; s++) {
                butterfly_op(a, b + s, b + s + stride, _twiddle_factors[s * tw_stride]);
            }
        }
        // according the flow graph, we need to double 
        // the butterfly stride and block size at the next stage.
        stride *= 2;
        block_size *= 2;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谬莹,隨后出現(xiàn)的幾起案子檩奠,更是在濱河造成了極大的恐慌约素,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笆凌,死亡現(xiàn)場離奇詭異,居然都是意外死亡士葫,警方通過查閱死者的電腦和手機乞而,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慢显,“玉大人爪模,你說我怎么就攤上這事〖栽澹” “怎么了屋灌?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長应狱。 經常有香客問我共郭,道長,這世上最難降的妖魔是什么疾呻? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任除嘹,我火速辦了婚禮,結果婚禮上岸蜗,老公的妹妹穿的比我還像新娘尉咕。我一直安慰自己,他們只是感情好璃岳,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布年缎。 她就那樣靜靜地躺著,像睡著了一般铃慷。 火紅的嫁衣襯著肌膚如雪单芜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天枚冗,我揣著相機與錄音缓溅,去河邊找鬼。 笑死赁温,一個胖子當著我的面吹牛坛怪,可吹牛的內容都是我干的。 我是一名探鬼主播股囊,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼袜匿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了稚疹?” 一聲冷哼從身側響起居灯,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤祭务,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后怪嫌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體义锥,經...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年岩灭,在試婚紗的時候發(fā)現(xiàn)自己被綠了拌倍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡噪径,死狀恐怖柱恤,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情找爱,我是刑警寧澤梗顺,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站车摄,受9級特大地震影響寺谤,放射性物質發(fā)生泄漏。R本人自食惡果不足惜练般,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一矗漾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧薄料,春花似錦敞贡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谷市,卻和暖如春蛔垢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迫悠。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工鹏漆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人创泄。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓艺玲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鞠抑。 傳聞我的和親對象是個殘疾皇子饭聚,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內容