ここでの拡張 DFT は,時間方向または周波数方向の点をずらした 変換のことで,
N-1 (j+δ1)(k+δ2) A = Σ a W k j=0 j N
で定義します.この逆変換も拡張 DFT で
1 N-1 -(j+δ2)(k+δ1) a = --- Σ A W k N j=0 j N
となります.もし,δ1, δ2 が有理数ならば,拡張 DFT は通常の DFT を 適当な間隔で点をとばして計算したものに相当します.たとえば,δ1=1/2, δ2=0 の場合は長さ 2N の奇数点上での DFT になります.
この拡張 DFT は,通常の FFT の変換前のデータに W^δ2 j を掛けて, 変換後のデータに W^δ1 k を掛ければ計算できることになります. しかし,δ1 または δ2 のどちらかがゼロのこの変換は,通常の FFT の 三角関数データをずらすだけで容易に実行できます [参考文献].したがって,これらの乗算の 片方は簡単に省略することができます.
例えば,時間方向に δ1 シフトした拡張 DFT は周波数間引き FFT の 三角関数を δ1 だけずらすことで計算できます.具体的なルーチンは 次のようになります.
リスト2.2-1. 周波数間引きによる拡張 FFT |
---|
/* F[k]=Σ_j=0^n-1 a[j]*exp(±2*pi*i*(j+delta1)*k/n),0<=k<n を計算する. n はデータ数で 2 の整数乗, theta は ±2*PI/n, ar[0...n-1], ai[0...n-1], はデータの実部, 虚部. */ #include <math.h> void gfft1(int n, double theta, double delta1, double ar[], double ai[]) { int m, mh, i, j, k; double wr, wi, xr, xi; for (m = n; (mh = m >> 1) >= 1; m = mh) { for (i = 0; i < mh; i++) { wr = cos(theta * (i + delta1)); wi = sin(theta * (i + delta1)); for (j = i; j < n; j += m) { k = j + mh; xr = ar[j] - ar[k]; xi = ai[j] - ai[k]; ar[j] += ar[k]; ai[j] += ai[k]; ar[k] = wr * xr - wi * xi; ai[k] = wr * xi + wi * xr; } } theta *= 2; } /* ---- unscrambler ---- */ i = 0; for (j = 1; j < n - 1; j++) { for (k = n >> 1; k > (i ^= k); k >>= 1); if (j < i) { xr = ar[j]; xi = ai[j]; ar[j] = ar[i]; ai[j] = ai[i]; ar[i] = xr; ai[i] = xi; } } } |
同様に,周波数方向に δ2 だけシフトした拡張 DFT は時間間引き FFT の 三角関数を δ2 だけずらすことで計算できます.これらの拡張 DFT に対する FFT を,拡張 FFT と呼ぶことにします.
次に実数データに対する拡張 FFT の計算について 考えます.これは, 実数専用の FFT ルーチンを用いる方法の第二の方法 を用いて計算できます.例えば,実数データの時間 1/2 シフト拡張 FFT は 三角関数データを 1/2 だけずらすことで計算できます.このルーチンは 次のようになります.
リスト2.2-2. 実数データの時間 1/2 シフト拡張 FFT |
---|
/* 実数データの時間 1/2 シフト拡張 DFT : F[k]=Σ_j=0^n-1 a[j]*exp(-2*pi*i*(j+1/2)*k/n),0<=k<n を計算する. 出力 a[0...n/2-1], a[n/2...n-1] はそれぞれ F[0...n/2-1] の実部, F[n/2...n-1] の符号を付けかえた虚部に相当する. */ #include <math.h> void grfft1h(int n, double a[]) { int m, mh, mq, i, j, k, irev, jr, ji, kr, ki; double theta, wr, wi, xr, xi; /* ---- scrambler ---- */ i = 0; for (j = 1; j < n - 1; j++) { for (k = n >> 1; k > (i ^= k); k >>= 1); if (j < i) { xr = a[j]; a[j] = a[i]; a[i] = xr; } } theta = -8 * atan(1.0) / n; for (mh = 1; (m = mh << 1) <= n; mh = m) { /* ---- real to real butterflies ---- */ for (jr = 0; jr < n; jr += m) { kr = jr + mh; xr = a[jr] - a[kr]; a[jr] += a[kr]; a[kr] = xr; } if (mh == 1) continue; /* ---- real to complex butterflies ---- */ mq = mh >> 1; irev = mq; for (ji = mq; ji < n; ji += m) { wr = cos(0.5 * theta * irev); wi = sin(0.5 * theta * irev); for (k = n >> 2; k > (irev ^= k); k >>= 1); ki = ji + mh; xr = a[ji]; xi = a[ki]; a[ji] = wr * xr + wi * xi; a[ki] = wr * xi - wi * xr; } if (mh == 2) continue; /* ---- complex to complex butterflies ---- */ irev = mq; for (i = 0; i < n; i += m) { wr = cos(theta * irev); wi = sin(theta * irev); for (k = n >> 2; k > (irev ^= k); k >>= 1); for (j = 1; j < mq; j++) { jr = i + j; kr = jr + mh; ki = i + mh - j; ji = ki + mh; xr = a[jr] - a[kr]; xi = a[ji] - a[ki]; a[jr] += a[kr]; a[ji] += a[ki]; a[ki] = wr * xr + wi * xi; a[kr] = wr * xi - wi * xr; } } } } |
この実数データの時間 1/2 シフト拡張 FFT は,離散コサイン変換 DCT-II と 離散サイン変換 DST-II を同時に計算していることになります.したがって, このルーチンを二つに分離すれば,DCT-II と DST-II のルーチンが 作成できることになります.これは 次の 2.3.3で示します.