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

離散フーリエ変換と数論変換 (2) 数列の畳み込み

838
0

この連載について

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

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

本文

今回は、離散フーリエ変換における数列の畳み込み (convolution) と、そのプログラミングについて記述します。計算の高速化については次回とし、原理の記述に焦点を当てます。

前回に引き続き、多項式をベクトルとして考えることにします。
すなわち、n1次多項式はC nに属します。
また、ベクトルfの第k成分をfkで表します (つまり多項式におけるxkの係数)。

数列の畳み込み

畳み込み には多くの種類があります。これは、畳み込みという用語が特定の式を指すのではなく、同じ性質を持つものすべてに対して定義できるためです。
この連載では多項式を取り扱っている文脈から、数列の畳み込み (この用語は文献 [2] による。単に畳み込みでもよいはず) を定義します。

数列の畳み込み・多項式の積

n,mを正の整数とする。
n1次多項式fC nおよびm1次多項式gC mに対し、n+m2次多項式fgC n+m1
  (fg)(x):=f(x)g(x)
で定義する。
このfgを、fgの畳み込み (convolution) と呼ぶ。

これでは畳み込みの定義になっていないように見えるかもしれませんが、
  f(x)=0k<n+m1akxk (akC)  g(x)=0k<n+m1bkxk (bkC)  ak=0 (nk<n+m1)  bk=0 (mk<n+m1)
であるとし、多項式の乗算をしてみると、分配法則により、
  f(x)g(x)=0k<n+m1(0jkajbkj)xk
となります。

この式に現れた
  ck:=0jkajbkj  (0k<n+m1)
により定まる数列あるいはベクトル(ck)C n+m1を畳み込みの定義としてもよいです。
こちらのほうが畳み込みらしい形をしており原義通りですが、どちらも同じものです。
このように、数列の畳み込みは2つの多項式の積そのものとして定義できます。

畳み込み定理

n,m1,m2を正の整数とし、m1+m21nとする。
m11次多項式fC m1およびm21次多項式gC m2に対し、
  Fn(fg)k=Fn(f)k Fn(g)k  (0k<n)
すなわち、
  Fn(fg)=(Fn(f)kFn(g)k)C n
が成り立つ。

DFT の定義および畳み込みの定義から、0k<nに対し、
  Fn(fg)k=(fg)(ωn k)=f(ωn k)g(ωn k)=Fn(f)kFn(g)k

制約m1+m21nは、多項式fgの積がn1次以下であることを保証するために設定しています。

この定理の中で、fg,f,gの3つに対する DFT が現れます。これらのFnnを一致させておかないと、標本点に使われる1n乗根ωnの値が変わってしまいます (m1m2としないように)。

この結果を少し言い換えて、次の系が得られます。

畳み込み定理

n,m1,m2を正の整数とし、m1+m21nとする。
m11次多項式fC m1およびm21次多項式gC m2が既知であるとき、
多項式の積fgC nを DFT および IDFT により求めることができる。

具体的な手順としては、Fn(fg)の第k成分をFn(f)kFn(g)kとして計算し、
このFn(fg)Fn1を作用させればよい。

多項式f,gからそれらの積fgを求めることは、単純な分配法則により時間計算量O(m1m2)で可能です。
したがって、上記の変換手順でO(n2)となるのでは意味がありませんが、後述する高速フーリエ変換 (FFT) によりO(nlogn)で実行できるようになります。

また、やはりこの時点においても、n2の冪である必要はありません。
FFT では2の冪の性質を利用することになります。

プログラミング

今回は数列の畳み込みを前節で整理した手順に従って実装します。
DFT および IDFT は前回に実装したものを使います。

前回に引き続き、プログラミング言語は C# です。
配列 a, b の長さはそれぞれm1,m2に相当します。
DFT を実行する前に、配列の長さをnに変更しています (拡張された部分の係数は0)。

      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 を実行するため、時間計算量はO(n2)です。FFT ではこれをO(nlogn)にすることができます。
ではこの関数を呼び出してみましょう。

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

この例は(x2+x+2)(x2x1)=x43x2を表しています。
m1=3,m2=3としているため、n=5となります。
今回も浮動小数点数を利用しているため、誤差が生じることがあります。計算結果が整数とわかっている場合は丸めればよいです。

作成したサンプル コード

次回は、これまでに出てきた処理を高速化します。

前回: (1) 離散フーリエ変換 (DFT)
次回: (3) 高速フーリエ変換 (FFT)

参考文献

[1]
Thomas H. Cormen ほか, アルゴリズムイントロダクション 第3版 総合版, pp. 746-759
[2]
高橋 陽一郎, 岩波講座 現代数学の基礎1 実関数とFourier解析1, pp. 69-74, pp. 109-112
投稿日:202195
OptHub AI Competition

この記事を高評価した人

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

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

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

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

投稿者

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

コメント

他の人のコメント

コメントはありません。
読み込み中...
読み込み中
  1. この連載について
  2. 本文
  3. 数列の畳み込み
  4. プログラミング
  5. 参考文献