離散フーリエ変換 (DFT) および数論変換 (NTT) の原理、そしてそれらのプログラミングにおける実装方法について記述します。
各用語の定義の違いを明確にするため、それぞれについての回を分割します。
今回は、離散フーリエ変換 (discrete Fourier transform, DFT) の基本的な性質と、プログラミングにおける実装方法について記述します。畳み込みのような応用や、計算の高速化には触れず、まずは原理の記述に焦点を当てます。
この連載では虚数単位を
また、
(離散でない) フーリエ変換 (Fourier transform, FT) というものがあり、その一部として次の結果があります。
関数
このとき、
とすると、係数
により定まる。
ここで、
フーリエ変換では、このように積分を利用します。フーリエ和をさらにフーリエ級数に発展させると、それが収束すれば周期関数を三角関数の和 (正弦波の重ね合わせ) で表すことが可能になります。
それに対して、積分の代わりに和で扱うものが離散フーリエ変換と言えます。
離散フーリエ変換の定義および定理を記述します。
本稿では、三角多項式を用いる代わりに多項式を用いる定義を扱います。
すなわち、
また、必要であれば、
この連載では
複素数体
DFT が
ここでは、
後述する高速フーリエ変換 (FFT) では、
文献によっては、
のように三角多項式として定義されていることもありますが、この場合は
次の定理の証明の核となる補題を用意します。
左辺を
(i)
(ii)
が成り立つ。
これは
とすると、
補題より、
この結果を、冒頭のフーリエ変換の説明のように言い換えてみましょう。
このとき、
とすると、係数
により定まる。
もう一つの要点は、順変換と逆変換に次のような対称性が現れることです。
なお、
今後、第
多項式の定義において、「
したがって、
これは、線形代数学でよくある考え方です。
ここでは、前節で見た定義や定理をベクトルで表現してみましょう。
で定義する。
写像
が成り立つ。
写像
IDFT の入力と出力だけに注目すると、
多項式
多項式をベクトル (あるいは数列) として考えることは必須ではありませんが、表記や入出力の観点でだいぶ見通しがよくなります。
プログラミングで実用されるのは後述する高速フーリエ変換 (FFT) ですが、原理の理解のために、まずは高速化せずにナイーブな実装を書くのもよいでしょう。ナイーブな実装は、FFT のテストを保証するためにも使うことができます。
さてここでは、前節で整理した DFT および IDFT を実装していきます。
ともに
計算内容が非常に似ているため、実装する関数を共通化します。
C# では次のように実装できます。
using System;
using System.Numerics;
public static class DFT
{
// k 番目の 1 の n 乗根 (n-th root)
static Complex NthRoot(int n, int k)
{
var t = 2 * Math.PI * k / n;
return Complex.FromPolarCoordinates(1, t);
}
// f(ω_n^k) の値
static Complex f(int n, Complex[] c, int k)
{
Complex r = 0;
for (int j = 0; j < c.Length; ++j)
r += c[j] * NthRoot(n, k * j);
return r;
}
// f の係数が整数のとき、f^ の係数も整数になるとは限りません。
public static Complex[] Transform(Complex[] c, bool inverse)
{
if (c == null) throw new ArgumentNullException(nameof(c));
var n = c.Length;
var r = new Complex[n];
for (int k = 0; k < n; ++k)
r[k] = inverse ? f(n, c, -k) / n : f(n, c, k);
return r;
}
}
時間計算量は
ではこの関数を呼び出してみましょう。
using System;
using System.Numerics;
static class Program
{
static void Main()
{
var c1 = new Complex[] { 3, 4, 5, 6, 123 };
// DFT
var t = DFT.Transform(c1, false);
// IDFT
var c2 = DFT.Transform(t, true);
// c1 ≒ c2 (誤差あり)
}
}
c1
は、多項式c2
が入力c1
とほぼ同じ値になることを確認できます。IDFT, DFT の順に実行しても同様です。
ただし、浮動小数点数を利用しているため、誤差が生じることがあります。計算結果が整数とわかっている場合は丸めればよいです。
次回: (2) 数列の畳み込み