Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

高速フーリエ変換

高速フーリエ変換は離散フーリエ変換の高速なアルゴリズム

1の原始NN乗根による表現

ωN\omega_Nを用いると、離散フーリエ変換の式

fl=∑k=0N−1Fkei2πkl/N,Fk=1N∑l=0N−1fle−i2πkl/Nf_l=\sum_{k=0}^{N-1} F_k e^{i 2 \pi k l / N}, \quad F_k=\frac{1}{N} \sum_{l=0}^{N-1} f_l e^{-i 2 \pi k l / N}

は次のように書くことができる

fl=∑k=0N−1FkωNkl,Fk=1N∑l=0N−1flωN−klf_l=\sum_{k=0}^{N-1} F_k \omega_N^{k l}, \quad F_k=\frac{1}{N} \sum_{l=0}^{N-1} f_l \omega_N^{-k l}

ベクトルと行列の形で書くと以下になる。

(f0f1f2⋮fN−1)=(111⋯11ωNωN2⋯ωNN−11ωN2ωN4⋯ωN2(N−1)⋮⋮⋮⋮1ωNN−1ωN2(N−1)⋯ωN(N−1)(N−1))(F0F1F2⋮FN−1)\left(\begin{array}{c} f_0 \\ f_1 \\ f_2 \\ \vdots \\ f_{N-1} \end{array}\right)=\left(\begin{array}{ccccc} 1 & 1 & 1 & \cdots & 1 \\ 1 & \omega_N & \omega_N^2 & \cdots & \omega_N^{N-1} \\ 1 & \omega_N^2 & \omega_N^4 & \cdots & \omega_N^{2(N-1)} \\ \vdots & \vdots & \vdots & & \vdots \\ 1 & \omega_N^{N-1} & \omega_N^{2(N-1)} & \cdots & \omega_N^{(N-1)(N-1)} \end{array}\right)\left(\begin{array}{c} F_0 \\ F_1 \\ F_2 \\ \vdots \\ F_{N-1} \end{array}\right)
(F0F1F2⋮FN−1)=1N(111⋯11ωN−1ωN−2⋯ωN−(N−1)1ωN−2ωN−4⋯ωN−2(N−1)⋮⋮⋮⋮1ωN−(N−1)ωN−2(N−1)⋯ωN−(N−1)(N−1))(f0f1f2⋮fN−1)\left(\begin{array}{c} F_0 \\ F_1 \\ F_2 \\ \vdots \\ F_{N-1} \end{array}\right)=\frac{1}{N}\left(\begin{array}{ccccc} 1 & 1 & 1 & \cdots & 1 \\ 1 & \omega_N^{-1} & \omega_N^{-2} & \cdots & \omega_N^{-(N-1)} \\ 1 & \omega_N^{-2} & \omega_N^{-4} & \cdots & \omega_N^{-2(N-1)} \\ \vdots & \vdots & \vdots & & \vdots \\ 1 & \omega_N^{-(N-1)} & \omega_N^{-2(N-1)} & \cdots & \omega_N^{-(N-1)(N-1)} \end{array}\right)\left(\begin{array}{c} f_0 \\ f_1 \\ f_2 \\ \vdots \\ f_{N-1} \end{array}\right)

離散フーリエ変換を計算するには、長さNNの任意の数列a0,a1,a2,…,aN−1a_0, a_1, a_2, \ldots, a_{N-1}に対して

bk=∑l=0N−1alωNkl,k=0,1,2,…,N−1b_k=\sum_{l=0}^{N-1} a_l \omega_N^{k l}, \quad k=0,1,2, \ldots, N-1

となる数列b0,b1,b2,…,bN−1b_0, b_1, b_2, \ldots, b_{N-1}が計算できればよい

{Fk}\{ F_k \} から {fl}\{f_l\}を計算するには、bk=flb_k = f_l, ak=Fka_k = F_kとおけば

fl=∑k=0N−1FkωNklf_l =\sum_{k=0}^{N-1} F_k \omega_N^{k l}

が成り立つ。

{fl}\{f_l\} から {Fk}\{ F_k \} を計算するには、ωN‾=ωN−1\overline{\omega_N}=\omega_N^{-1} であるから

Fk=1N∑l=0N−1fl‾ωNkl‾F_k=\frac{1}{N} \overline{\sum_{l=0}^{N-1} \overline{f_l} \omega_N^{k l}}

高速フーリエ変換

NNが2の冪乗であれば、離散フーリエ変換の計算が効率化される(具体的にはO(N)O(N) から O(Nlog⁡N)O(N\log N)になる)これを使うのが高速フーリエ変換。

単位円での表現

1の原始NN乗根ωN\omega_Nを次々と冪乗すると、複素平面の単位円周上を回転する

Source
<Figure size 400x300 with 1 Axes>
定理

NNが偶数のとき、次の関係が成り立つ

ωNN/2=−1,ωNN/2+1=−ωN,ωNN/2+2=−ωN2,…,ωNN−1=−ωNN/2−1\omega_N^{N / 2}=-1, \quad \omega_N^{N / 2+1}=-\omega_N, \quad \omega_N^{N / 2+2}=-\omega_N^2, \quad \ldots, \quad \omega_N^{N-1}=-\omega_N^{N / 2-1}
証明

定義 ωN:=ei2π/N\omega_N := e^{i 2 \pi / N} より、

ωNN/2=(ei2π/N)N/2=eiπ=−1\omega_N^{N / 2} = \left(e^{i 2 \pi / N}\right)^{N / 2} =e^{i \pi}=-1

である。ゆえに

ωNN/2+k=ωNN/2ωNk=−ωNk\omega_N^{N / 2+k} = \omega_N^{N / 2} \omega_N^k=-\omega_N^k

例えばN=8N=8のときは、ωNN/2=ωN4=−1=−ωN0\omega_{N}^{N/2} = \omega_{N}^{4} = -1 = -\omega_{N}^{0}であり、ωNN/2+1=ωN5=−ωN1=−ωN\omega_{N}^{N/2+1} = \omega_{N}^{5} = -\omega_{N}^{1} = -\omega_{N}である。以下同様。

単位円で描くとちょうど反対側に位置する。

そのため、 N/2N/2個のωN\omega_Nについてのみ考えればよい ことがわかる。

Source
<Figure size 400x300 with 1 Axes>
定理

NNが偶数のとき、ωN2\omega^2_Nは1の原始N/2N/2乗根として表せる。

ωN2=ωN/2\omega_N^2=\omega_{N / 2}
証明

ωN2=(ei2π/N)2=ei4π/N=ei2π/(N/2)=ωN/2\omega_N^2=\left(e^{i 2 \pi / N}\right)^2=e^{i 4 \pi / N}=e^{i 2 \pi /(N / 2)}=\omega_{N / 2} となる

前出の定理より、ωN2k=ωN/2k\omega_{N}^{2k} = \omega_{N / 2}^kとなっている。例えばωN4=ωN/22\omega_{N}^{4} = \omega_{N / 2}^2となっている

Source
<Figure size 400x300 with 1 Axes>

N−1N-1 次多項式

f(x)=a0+a1x+a2x2+⋯+aN−1xN−1=∑l=0N−1alxlf(x)=a_0+a_1 x+a_2 x^2+\cdots+a_{N-1} x^{N-1}=\sum_{l=0}^{N-1} a_l x^l

を定義すると

bk=∑l=0N−1alωNkl,k=0,1,2,…,N−1b_k=\sum_{l=0}^{N-1} a_l \omega_N^{k l}, \quad k=0,1,2, \ldots, N-1

は

bk=f(ωNk),k=0,1,2,…,N−1b_k=f\left(\omega_N^k\right), \quad k=0,1,2, \ldots, N-1

と書くことができる。

つまり、 複素平面上の単位円周のNN等分点でN−1N-1次多項式f(x)f(x)を計算すればb0,b1,…,bNb_0,b_1,\dots,b_Nが得られる。

この計算をFFT⁡N[f(x)]\operatorname{FFT}_N[f(x)]と表す。

FFT⁡N[f(x)]={f(1),f(ωN),f(ωN2),…,f(ωNN−1)}\operatorname{FFT}_N[f(x)]=\{f(1), f(\omega_N), f(\omega_N^2), \ldots, f(\omega_N^{N-1})\}

間引き

上記の多項式は次のように書き直せる

f(x)=a0+a1x+a2x2+⋯+aN−1xN−1=a0+a2x2+a4x4+⋯+aN−2xN−2+x(a1+a3x2+a5x4+⋯+aN−1xN−2)=p(x2)+xq(x2)\begin{aligned} f(x)= & a_0+a_1 x+a_2 x^2+\cdots+a_{N-1} x^{N-1}\\ = & a_0+a_2 x^2+a_4 x^4+\cdots+a_{N-2} x^{N-2} \\ & +x\left(a_1+a_3 x^2+a_5 x^4+\cdots+a_{N-1} x^{N-2}\right) \\ = & p(x^2)+x q(x^2) \end{aligned}

ただし、

{p(x)=a0+a2x+a4x2+⋯+aN−2xN/2−1q(x)=a1+a3x+a5x2+⋯+aN−1xN/2−1\left\{\begin{array}{l} p(x)=a_0+a_2 x+a_4 x^2+\cdots+a_{N-2} x^{N / 2-1} \\ q(x)=a_1+a_3 x+a_5 x^2+\cdots+a_{N-1} x^{N / 2-1} \end{array}\right.

とおいた。この係数の 間引き は高速フーリエ変換における重要な計算手順の一つ。

p(x2)p(x^2)はxxが複素平面上の単位円周のNN等分点を順にたどって1周するとき x2x^2はそれらを1つおきに進んで2周する 。そのため 前半のN/2N/2個のみを計算すればよい から、

FFT⁡N[p(x2)]={p(1),p(ωN2),p(ωN4),…,p(ωNN−2)}\operatorname{FFT}_{N}[p(x^2)]=\{p(1), p(\omega_N^2), p(\omega_N^4), \ldots, p(\omega_N^{N-2})\}

となる。

また「NNが偶数のときωN2=ωN/2\omega_N^2=\omega_{N / 2}」という定理より以下のように書き直せる。

FFT⁡N[p(x2)]={p(1),p(ωN/2),p(ωN/22),…,p(ωN/2N/2−1)}\operatorname{FFT}_{N}[p(x^2)]=\{p(1), p(\omega_{N/2}), p(\omega_{N/2}^2), \ldots, p(\omega_{N/2}^{N/2-1})\}

→ データ数N/2N/2個の離散フーリエ変換 を計算すればいい。

q(x2)q(x^2)についても同様に得られる。

FFT⁡N[q(x2)]={q(1),q(ωN/2),q(ωN/22),…,q(ωN/2N/2−1)}\operatorname{FFT}_{N}[q(x^2)]=\{q(1), q(\omega_{N/2}), q(\omega_{N/2}^2), \ldots, q(\omega_{N/2}^{N/2-1})\}

これらN/2N/2個の離散フーリエ変換で得られるものたちを以下のようにFFT⁡N/2\operatorname{FFT}_{N / 2}と表記する

FFT⁡N[p(x2)]=FFT⁡N/2[p(x)],FFT⁡N[q(x2)]=FFT⁡N/2[q(x)]\operatorname{FFT}_N[p(x^2)]=\operatorname{FFT}_{N / 2}[p(x)], \quad \operatorname{FFT}_N[q(x^2)]=\operatorname{FFT}_{N / 2}[q(x)]

バタフライ

f(x)f(x)は「NNが偶数のとき、ωNN/2=−1,ωNN/2+1=−ωN,ωNN/2+2=−ωN2,…,ωNN−1=−ωNN/2−1\omega_N^{N / 2}=-1, \quad \omega_N^{N / 2+1}=-\omega_N, \quad \omega_N^{N / 2+2}=-\omega_N^2, \quad \ldots, \quad \omega_N^{N-1}=-\omega_N^{N / 2-1}」という定理と f(x)=p(x2)+xq(x2)f(x) = p(x^2)+x q(x^2) により、以下のように計算できる。

{f(ωNk)=p(ωN/2k)+ωNkq(ωN/2k),k=0,1,…,N/2−1f(ωNN/2+k)=p(ωN/2k)−ωNkq(ωN/2k),k=0,1,…,N/2−1\begin{cases} f(\omega_N^k)=p(\omega_{N / 2}^k)+\omega_N^k q(\omega_{N / 2}^k), & k=0,1, \ldots, N / 2-1 \\ f(\omega_N^{N / 2+k})=p(\omega_{N / 2}^k)-\omega_N^k q(\omega_{N / 2}^k), & k=0,1, \ldots, N / 2-1 \end{cases}

この計算を バタフライ と呼び、式中のωNk\omega_N^kを 回転因子(ひねり因子) と呼ぶ。

考察:なぜ引数はωN/2k\omega_{N / 2}^kなのに回転因子はωNk\omega_N^kなのか?

f(x)=p(x2)+xq(x2)f(x)= p(x^2)+x q(x^2)なのと同様の形になっていると思われる。

考察:f(ωNN/2+k)=p(ωN/2k)−ωNkq(ωN/2k)f(\omega_N^{N / 2+k})=p(\omega_{N / 2}^k)-\omega_N^k q(\omega_{N / 2}^k)で−ωNk-\omega_N^kとマイナスがつくのはなぜ?

前述の定理より、ωNN/2+k=−ωNk\omega_N^{N / 2+k} = -\omega_N^{k}

高速フーリエ変換

FFT⁡N\operatorname{FFT}_Nは係数の間引き(Decimation-In-Time, DIT)DND_Nを行ってFFT⁡N/2\operatorname{FFT}_{N/2}を実行し、バタフライBNB_Nを施す。

間引きによってFFT⁡N/4,FFT⁡N/8,FFT⁡N/16,…\operatorname{FFT}_{N/4}, \operatorname{FFT}_{N/8}, \operatorname{FFT}_{N/16}, \dotsと分解していくと最終的にFFT⁡1\operatorname{FFT}_{1}の計算になり、0次式(定数)の計算になる。

その後バタフライによって統合していく。

そのためFFT⁡N\operatorname{FFT}_{N}は間引きの繰り返しとバタフライの繰り返しで分割統治法によって計算できる。

この方法を 高速フーリエ変換 (FFT)と呼ぶ。

Pythonの実装

by chatGPT

Source
FFTの結果:
X[0] = 28.000+0.000j
X[1] = -4.000+9.657j
X[2] = -4.000+4.000j
X[3] = -4.000+1.657j
X[4] = -4.000+0.000j
X[5] = -4.000-1.657j
X[6] = -4.000-4.000j
X[7] = -4.000-9.657j
NumPy FFT結果:
X[0] = 28.000+0.000j
X[1] = -4.000+9.657j
X[2] = -4.000+4.000j
X[3] = -4.000+1.657j
X[4] = -4.000+0.000j
X[5] = -4.000-1.657j
X[6] = -4.000-4.000j
X[7] = -4.000-9.657j
Source
<Figure size 800x400 with 1 Axes>