YU2TA7KA's BLOG ~take one step at a time~

派生開発、組み込み開発周りのこと。

高速フーリエ変換の概要とアルゴリズム

はじめに

鈍行ではありますが、高速フーリエ変換の修得を進めています。先日、ChatGPTを利用しつつFFTするRustの実装ができました。これは、2021年9月号Interface 星野秋人さんの記事と「道具としてのフーリエ解析」を参考にしています。改めて、ここまでの理解で高速フーリエ変換について記述します。

高速フーリエ変換とは

高速フーリエ変換(fast Fourier transform, FFT)は、離散フーリエ変換(O(N^2))を効率的に計算するアルゴリズム(O(NlogN))です。この高速フーリエ変換により、コンピュータに現実的な処理速度で実装可能になります。ただし、N(入力データ数)が2の累乗である必要があります。これは高速フーリエ変換が処理対象データを再帰的に1/2個ずつに分割するアルゴリズムであることに由来します。
離散フーリエ変換については、こちらで説明しています。

高速フーリエ変換のアルゴリズム

4Stepで記述したアルゴリズムが以下になります。

  • Step1 バタフライ演算を行う
  • Step2 データ列(N個)をN/2個に分割する
  • Step3 データ数が2個になるまで、Step1とStep2を繰り返す
  • Step4 ビットリバースを行う

なお、バタフライ演算とビットリバースは高速フーリエ変換の中核をなす制御です*1

Step1 バタフライ演算を行う

まず、バタフライ演算とは、2つのデータに2種類の演算をし、2つのデータを求める演算です。例えば、(a,b)という入力に対して、(a+b)と(a-b)という2値出力を得るなどです。この演算過程の可視化としてシグナルフロー図というものがあります。「シグナルフロー図 FFT」で検索するといろいろ出てきます。

FFTのバタフライ演算は、2つのデータ(a,b)から「加算したデータ(a+b)」と「減算し、回転子Wのk乗をかけたデータ(W^k * (a-b))」の2値を求める演算を行います。2つのデータの選び方は、データ列を前半と後半に二分割して、それぞれの先頭から組にしていきます。kは組のインデックスになります。また、回転子Wの詳細は、高速フーリエ変換の前段である離散フーリエ変換を理解するを参照ください。なお、基底(データが2個だけの場合)は、「加算したデータ(a+b)」と「減算したデータ(a-b)」の2値を求める演算となります。回転子Wが1または-1になるためです。

演算結果は、「加算したデータ(a+b)」を前半分に、「減算し、回転子のk乗をかけたデータ(W^k * (a-b))」を後半分に、格納していきます。

Step2 データ列(N個)をN/2個に分割する

Step1の演算により、データ列の前半が加算データ群、後半が回転子*減算データ群に分割されました。この前後半に二分割したデータ列を用意します。

Step3 データ数が2個になるまで、Step1とStep2を繰り返す

データ数が2個になるまで、Step1とStep2を再帰処理します。Step2により、データ数が半分になっていくので、いつかはStep2でのデータ列が2個になります。この二分割再帰処理でフーリエ変換演算を完了させるため、離散フーリエ変換に比べて計算量を減らすことができます。

Step4 ビットリバースを行う

ビットリバースにより出力データを並び替えて0,1,2,...,N-1の順番に戻します。ビットリバースとは、数を2進数表示しそれを逆順にしたときの値、です。例えば、3bitのビットリバースは以下になります。

ビットリバース 2進数 逆転
0 0 000 000
1 4 001 100
2 2 010 010
3 6 011 110
4 1 100 001
5 5 101 101
6 3 110 011
7 7 111 111

FFTでは、バタフライ演算により入力データの各要素ごとにDFTを行った結果が得られます。しかし、その結果の順番が0,1,2,3,...,(N-1)とはなっておらず、入れ替わりが発生しています。そのため、バタフライ演算の結果順番を0,1,2,3,...,(N-1)に並び替える必要があります。入れ替わりが発生していますが、規則性があり、ビットリバース順に並べ替えることで期待順番0,1,2,3,...,(N-1)に戻せます。

以上で高速フーリエ変換完了です。

高速フーリエ変換の実装

github.com
にてRustで実装しています。

なぜ高速化できるのか?

ここまでで、実装に必要なアルゴリズム説明をしました。しかし、離散フーリエ変換から高速フーリエ変換が実現できる理由を説明していません。私は良い感じに説明をできるほどの理解はできておりません。ざっくり私の理解だと、離散フーリエ変換のDFT行列の要素は回転子Wのk乗で構成され、同じ計算をしている箇所があり、それらを"まとめる"ことで、高速化を実現している、"まとめる"方法はバタフライ演算に対応する、という感じです。詳細は参考文献を参照ください。

おわりに

高速フーリエ変換の概要とアルゴリズムをまとめました。高速フーリエ変換アルゴリズムの中身にバタフライ演算とビットリバースというものがあり、さらに再帰処理をしている、ということを今回初めて知った気がします。ようやくここまで知ることができました。今後は、波形データへのFFT、マイコンでFFTあたりをしたいなと思います。あとAtCoderの高速フーリエ変換も理解したいです。まだまだ高速フーリエ変換と戯れる感じになりそうです。

*1:これらについて「道具としてのフーリエ解析」に詳述されており、長い時間をかけて理解に至れました。