1
大学数学基礎解説
文献あり

離散フーリエ変換と数論変換 (1) 離散フーリエ変換 (DFT)

1446
0

この連載について

離散フーリエ変換 (DFT) および数論変換 (NTT) の原理、そしてそれらのプログラミングにおける実装方法について記述します。
各用語の定義の違いを明確にするため、それぞれについての回を分割します。

  1. 離散フーリエ変換 (DFT) (本稿)
  2. 数列の畳み込み
  3. 高速フーリエ変換 (FFT)
  4. ビット反転置換
  5. 数論変換 (NTT)
  6. NTT の高速化
  7. 中国剰余定理による値の復元

本文

今回は、離散フーリエ変換 (discrete Fourier transform, DFT) の基本的な性質と、プログラミングにおける実装方法について記述します。畳み込みのような応用や、計算の高速化には触れず、まずは原理の記述に焦点を当てます。

この連載では虚数単位をiで表し、インデックス (添字) をk,jなどで表します。
また、eiθ=cosθ+isinθexpiθ で表すことがあります。

(離散でない) フーリエ変換

(離散でない) フーリエ変換 (Fourier transform, FT) というものがあり、その一部として次の結果があります。

フーリエ和

関数f:[0,1)Cが次のような三角多項式の形式で表せるとする。
  f(x)=k=nnckexp(2πikx)  (ckC)

このとき、
  f^(k)=01f(x)exp(2πikx)dx
とすると、係数ck
  ck=f^(k)
により定まる。

ここで、f:[0,1)Cは半開区間[0,1)から複素数全体への写像を表しています。

フーリエ変換では、このように積分を利用します。フーリエ和をさらにフーリエ級数に発展させると、それが収束すれば周期関数を三角関数の和 (正弦波の重ね合わせ) で表すことが可能になります。
それに対して、積分の代わりに和で扱うものが離散フーリエ変換と言えます。

離散フーリエ変換と逆離散フーリエ変換

離散フーリエ変換の定義および定理を記述します。
本稿では、三角多項式を用いる代わりに多項式を用いる定義を扱います。

多項式

nを正の整数とする。
n1次多項式f:CCは、次の形式で表せる写像とする。
  f(x)=0k<nckxk  (ckC)
すなわち、n1次多項式はn個の係数ckのみによって特徴づけられる。
また、必要であれば、knにおける係数をck=0とする。

この連載ではcn10という制約を設けないため、cn1=0である多項式も便宜上n1次と見なすことができます。文献 [1] では「次数上界がnの多項式 (polynomial of degree-bound n)」と表現されています。

離散フーリエ変換

nを正の整数とする。
複素数体Cにおける1の原始n乗根を、ωn=e2πin=exp2πinで表す。

n1次多項式f:CCに対し、n1次多項式f^:CCを次のように定義する。
  f^(x):=0k<nf(ωn k)xk

ff^を対応させる写像を、離散フーリエ変換 (discrete Fourier transform, DFT) と呼び、Fnで表す。

DFT がnに依存していることを表現するため、記号Fnを導入します。
ここでは、n2の冪である必要はありません。
後述する高速フーリエ変換 (FFT) では、n2の冪とし、その性質を利用することになります。

文献によっては、
  f^(t):=0k<nf(k)exp(i2πknt)=0k<nf(k)ωnkt
のように三角多項式として定義されていることもありますが、この場合はx=ωntとすれば本質的には同じものです。

次の定理の証明の核となる補題を用意します。

nを正の整数とする。
1の原始n乗根ωnCについて、次が成り立つ。
  0k<nωn jk={n  (j=0)0  (0<j<n)

左辺をSとすると、S=0k<n(ωn j)k

(i) j=0のとき、ωn j=1だから、
  S=0k<n1=n
(ii) 0<j<nのとき、ωn j1だから、
  S=1(ωn j)n1ωn j=1(ωn n)j1ωn j=0

逆離散フーリエ変換

nを正の整数とする。
n1次多項式f:CCに対し、
  f(x)=1n0k<nf^(ωnk)xk
が成り立つ。

f^fを対応させる写像を、逆離散フーリエ変換 (inverse discrete Fourier transform, IDFT) と呼ぶ。
これはFnの逆元であるため、Fn1で表す。

  f(x)=0j<ncjxj  (cjC)
とすると、0m<nに対して、
  f^(ωnm)=0k<n(0j<ncjωn kj)ωnmk=0j<ncj(0k<nωn k(jm))
補題より、
  f^(ωnm)=ncm

この結果を、冒頭のフーリエ変換の説明のように言い換えてみましょう。

逆離散フーリエ変換

nを正の整数とする。
n1次多項式f:CCは、次の形式であるとする。
  f(x)=0k<nckxk  (ckC)
n個の係数ckは未知だが、n個の値f(ωn k)は既知であるとする。
このとき、
  f^(x):=0k<nf(ωn k)xk
とすると、係数ck
  ck=1nf^(ωnk)
により定まる。

n個の標本点における値をもとにn個の係数ckを決定しているということは、つまりn元連立1次方程式を解いているのです。ここで、標本点として1n乗根の集合 {ωn k} を採用すると、ckが美しい形で求められるということです。

fの実際の次数がn1より小さい場合でも、範囲外の係数を0としてn1次多項式と見なすことによりこれまでの議論が成り立ちます。逆に、fの次数がn1より大きい場合はこの連立方程式を解けません。

もう一つの要点は、順変換と逆変換に次のような対称性が現れることです。

  • f^(x)の係数は、fを入力としてf(ωn k)で定まる
  • f(x)の係数は、f^を入力として1nf^(ωnk)で定まる

なお、f^の定義で1n倍しておくと、逆変換も1n倍となって統一できるのですが、離散フーリエ変換の分野では、逆変換を1n倍とする定義が多いようです (おそらく実用上の都合)。

多項式をベクトルとして考える

今後、第k成分がvkであるベクトルを(vk)で表すことにします。

多項式の定義において、「n1次多項式はn個の係数ckのみによって特徴づけられる」と書きました。
したがって、n1次多項式f:CCを、n次元ベクトル (ck)C n と同一視できます。
これは、線形代数学でよくある考え方です。

ここでは、前節で見た定義や定理をベクトルで表現してみましょう。

離散フーリエ変換

nを正の整数とする。
n1次多項式fC nに対し、n1次多項式f^C n
  f^:=(f(ωn k))
で定義する。
写像Fn:C nff^C nを、離散フーリエ変換 (DFT) と呼ぶ。

逆離散フーリエ変換

nを正の整数とする。
n1次多項式fC nに対し、
  f=(1nf^(ωnk))
が成り立つ。
写像Fn1:C nf^fC nを、逆離散フーリエ変換 (IDFT) と呼ぶ。

IDFT の入力と出力だけに注目すると、f^は任意の入力として考えてよい (記号を変えてよい) ため、まとめると次のようになります。

  • 離散フーリエ変換 (DFT) Fn:f(f(ωn k))
  • 逆離散フーリエ変換 (IDFT) Fn1:f(1nf(ωnk))

多項式fに対して、DFT により「標本点におけるfの値の集合」に変換し、類似の変換である IDFT によりfを復元できることが明確になりました。
多項式をベクトル (あるいは数列) として考えることは必須ではありませんが、表記や入出力の観点でだいぶ見通しがよくなります。

プログラミング

プログラミングで実用されるのは後述する高速フーリエ変換 (FFT) ですが、原理の理解のために、まずは高速化せずにナイーブな実装を書くのもよいでしょう。ナイーブな実装は、FFT のテストを保証するためにも使うことができます。

さてここでは、前節で整理した DFT および IDFT を実装していきます。
ともにC nからC nへの写像であり、n1次多項式の係数(ck)C nを入力として受け取ります。
計算内容が非常に似ているため、実装する関数を共通化します。

C# では次のように実装できます。n次元ベクトルは、長さnの配列で表せばよいです。

      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;
    }
}
    

時間計算量はO(n2)です。FFT ではこれをO(nlogn)にすることができます。
ではこの関数を呼び出してみましょう。

      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は、多項式3+4x+5x2+6x3+123x4を表しています。DFT, IDFT の順に変換すると、出力c2が入力c1とほぼ同じ値になることを確認できます。IDFT, DFT の順に実行しても同様です。
ただし、浮動小数点数を利用しているため、誤差が生じることがあります。計算結果が整数とわかっている場合は丸めればよいです。

fの各成分が整数だとしても、f^の各成分は整数になるとは限りません (f^を丸めないように)。

作成したサンプル コード

次回: (2) 数列の畳み込み

改訂履歴

  • 2021.09.03 記号Fnを導入。
  • 2021.09.13 1の原始n乗根に関する補題を追加。

参考文献

投稿日:2021831
OptHub AI Competition

この記事を高評価した人

高評価したユーザはいません

この記事に送られたバッジ

バッジはありません。
バッチを贈って投稿者を応援しよう

バッチを贈ると投稿者に現金やAmazonのギフトカードが還元されます。

投稿者

Researcher and Developer for Algorithms, using C# (.NET). 数検1級金賞 (2002).

コメント

他の人のコメント

コメントはありません。
読み込み中...
読み込み中
  1. この連載について
  2. 本文
  3. (離散でない) フーリエ変換
  4. 離散フーリエ変換と逆離散フーリエ変換
  5. 多項式をベクトルとして考える
  6. プログラミング
  7. 改訂履歴
  8. 参考文献