まずは、普通のO(n^2)のDFTを行列表現で書いてみます。このとき、eのべき乗のところを式で書くとイメージが大変なので、極座標系の矢印で書いてあげると分かりやすいです。

さて、FFTですが、まず偶数番と奇数番の周波数成分に分けます。偶数番成分に注目すると、
と書けます。この時点で演算数が半分になりました。これを繰り返せば分割統治の容量でO(N^2)からO(N log N)まで落とせそうです。
同様に、奇数番のものは以下のように表すことができます。
まず、上半分は、偶数です。さらにその中の上半分の2個は、4で割れる数(下二桁のビットが00のもの)です。逆に下半分の2個は、ビットが10のものです。
これを再帰的に考えていけば、LSBとMSBの関係が逆転していることが分かります。それでビット逆転を使うと都合がいいわけです。
ほんで、C++のソース。最初pythonで書こうとしていましたが、C++のcomplexライブラリの方が使いやすかったので、C++で書きました。FFTとInverse FFTは、非常に似た処理を行うので、共通部分はgenFFT()にまとめています。ソースのコンパクトさよりも分かりやすさを追求したので、比較的読みやすい(はず?)です。
- typedef double Amp; // amplitude at each sampling time
- typedef complex<double> Freq; // constituent frequencies
- int bit_reverse(int x, int n) {
- int rev = 0;
- for (--n; n > 0; n >>= 1, x >>= 1)
- rev = (rev << 1) | (x & 1);
- return rev;
- }
- vector<Freq> genFFT(const vector<Freq> &x, int sgn) {
- int N = x.size();
- vector<Freq> X(N);
- // set init values with bit reversed
- for (int i = 0; i < N; i++) {
- int rev = bit_reverse(i, N);
- X[i] = x[rev];
- }
- // butterfly diagrams
- for (int b = 2; b <= N; b <<= 1) {
- for (int i = 0; i < N; i++) {
- if (i % b >= b/2)
- continue;
- int k = i + b/2;
- Freq Wi = exp(Freq(0, sgn*2*PI*(i%b)/b));
- Freq Wk = exp(Freq(0, sgn*2*PI*(k%b)/b));
- X[i] += Wi*X[k];
- X[k] = Wk*X[k] + (X[i] - Wi*X[k]);
- }
- }
- return X;
- }
- vector<Freq> FFT(const vector<Amp> &x) {
- int N = x.size();
- // convert real numbers to complex numbers
- vector<Freq> x_t(N);
- for (int i = 0; i < N; i++)
- x_t[i] = Freq(x[i], 0);
- return genFFT(x_t, -1);
- }
- vector<Amp> IFFT(const vector<Freq> &X) {
- int N = X.size();
- vector<Freq> x_t = genFFT(X, 1);
- // convert complex numbers to real numbers
- // also normalize the amplituds (devide by N)
- vector<Amp>x(N);
- for (int i = 0; i < N; i++)
- x[i] = x_t[i].real()/N;
- return x;
- }
FFTが出来たので、次回「多倍長整数の除算をFFTで高速化する」でFFTシリーズをシメたいと思います。
0 件のコメント:
コメントを投稿