離散フーリエ変換 (DFT) および数論変換 (NTT) の原理、そしてそれらのプログラミングにおける実装方法について記述します。
各用語の定義の違いを明確にするため、それぞれについての回を分割します。
今回は、離散フーリエ変換における数列の畳み込み (convolution) と、そのプログラミングについて記述します。計算の高速化については次回とし、原理の記述に焦点を当てます。
前回に引き続き、多項式をベクトルとして考えることにします。
すなわち、
また、ベクトル
畳み込み
には多くの種類があります。これは、畳み込みという用語が特定の式を指すのではなく、同じ性質を持つものすべてに対して定義できるためです。
この連載では多項式を取り扱っている文脈から、数列の畳み込み (この用語は文献 [2] による。単に畳み込みでもよいはず) を定義します。
で定義する。
この
これでは畳み込みの定義になっていないように見えるかもしれませんが、
であるとし、多項式の乗算をしてみると、分配法則により、
となります。
この式に現れた
により定まる数列あるいはベクトル
こちらのほうが畳み込みらしい形をしており原義通りですが、どちらも同じものです。
このように、数列の畳み込みは2つの多項式の積そのものとして定義できます。
すなわち、
が成り立つ。
DFT の定義および畳み込みの定義から、
制約
この定理の中で、
この結果を少し言い換えて、次の系が得られます。
多項式の積
具体的な手順としては、
この
多項式
したがって、上記の変換手順で
また、やはりこの時点においても、
FFT では
今回は数列の畳み込みを前節で整理した手順に従って実装します。
DFT および IDFT は前回に実装したものを使います。
前回に引き続き、プログラミング言語は C# です。
配列 a, b
の長さはそれぞれ
DFT を実行する前に、配列の長さを
using System;
using System.Numerics;
public static class DFT
{
// 前回の Transform メソッドは省略。
// public static Complex[] Transform(Complex[] c, bool inverse)
public static Complex[] Convolution(Complex[] a, Complex[] b)
{
if (a == null) throw new ArgumentNullException(nameof(a));
if (b == null) throw new ArgumentNullException(nameof(b));
var n = a.Length + b.Length - 1;
Array.Resize(ref a, n);
Array.Resize(ref b, n);
var fa = Transform(a, false);
var fb = Transform(b, false);
for (int k = 0; k < n; ++k)
{
fa[k] *= fb[k];
}
return Transform(fa, true);
}
}
前回実装した DFT および IDFT を実行するため、時間計算量は
ではこの関数を呼び出してみましょう。
using System;
using System.Numerics;
static class Program
{
static void Main()
{
var a = new Complex[] { 2, 1, 1 };
var b = new Complex[] { -1, -1, 1 };
// { -2, -3, 0, 0, 1 }
var c = DFT.Convolution(a, b);
}
}
この例は
今回も浮動小数点数を利用しているため、誤差が生じることがあります。計算結果が整数とわかっている場合は丸めればよいです。
次回は、これまでに出てきた処理を高速化します。
前回:
(1) 離散フーリエ変換 (DFT)
次回:
(3) 高速フーリエ変換 (FFT)