ある特定の剰余環上での2の羃倍を計算する
多倍長演算のアルゴリズムに Schönhage-Strassen Algorithm というものがあります。 これは、n桁の数同士の積が畳み込み演算で表わされることから、畳み込み定理を用いて乗算を高速化するアルゴリズムです。 畳み込み定理を利用するために剰余環上で離散フーリエ変換と似た計算を行うらしいです。 その仮定でを法とする剰余環上である整数
を計算する必要が出てきました。 ただ計算するだけなら問題ないのですが、は非常に大きい場合があり愚直に計算するとが64bit整数の表現範囲を越える可能性があります。
つまり、愚直に計算をするためには多倍長演算を行うか、2を掛けて剰余をとるという計算を回繰り返す必要があります。 多倍長演算を高速化するのに、そんな効率の悪い方法でいいのかと調べてみたら、その解決策となる計算方法を見つけました(調べたのが昔すぎてどこのサイトか覚えてないですが...)。 それが
ならば
というものでした。 なんでこうなるのか、というのが当時全く理解できてなかったのですが、最近導出できたのでその前提条件や導出仮定をまとめます。 ついでに実装もしてみます。
前提
対象はある自然数によって法がと表わされるような整数環上の剰余環です。 以下、代表元をとする剰余環上の元をと書きます。
命題
ある以上以下の整数と以上未満の整数に対して、
を満たす以上未満の整数に対して
が成立する。
導出
まず、の範囲を以上未満に制限したのは 、であるためです。 であるため、となるので、任意のに対して考える必要がなくなります。
さらにの範囲を制限したことで、の範囲が以上未満に限定できます(実際にはもう少し範囲が限定できますが必要がないのでしません)。
したがって除法の定理から、あるが唯一つ存在して
となることが言えます。
ここでとすると、除法の定理よりある整数を用いて
と書くことができます()。 さらも、を以上未満の整数を用いて
として置きかえると
と書くことができます。 同様にしてを以上未満の整数を用いて
として置きかえると
と書くことができます。
除法の定理よりはそれぞれと一致するため、には
という関係が成立します。 この関係式からを削除すると
となります。 ここではをで割った余りだったため
が成立し、であるため
となり、命題は示されました。
実際の計算
実際の計算ではもう少し工夫をします。
という計算を行ないますが、が以上以下であるという制約からのどちらも非ゼロとなることはありません。 さらにはプログラム上ではビットシフトのみで計算できます。
以上をふまえてc++で簡単に実装したコードが以下のようになります。
std::uint64_t fmp(std::uint64_t k, std::uint64_t x, std::uint64_t p) { std::uint64_t m = (1 << k) + 1; // 法 std::uint64_t mask = (1 << k) - 1; std::uint64_t y; p = p % (2 * k); // p の範囲を制限 if (p < k) { // y_2 がゼロの場合 std::uint64_t y_0, y_1; y_0 = (x << p) & mask; // ビットシフトで y_0 を計算 y_1 = (x >> (k - p)) & mask; // ビットシフトで y_1 を計算 y = y_0 - y_1; return y < 0 ? y + m : y; // 負数になったときに範囲を調整 } else { // y_1 がゼロの場合 std::uint64_t y_2, y_1; y_2 = (x >> (2 * k - p)) & mask; // ビットシフトで y_2 を計算 y_1 = (x << (p - k)) & mask; // ビットシフトで y_1 を計算 y = y_2 - y_1; return y < 0 ? y + m : y; // 負数になったときに範囲を調整 } }
まとめ
この方法で特定の剰余環上での2の羃との積を計算することができます。
剰余環上では、直感的でない方法でいろいろな計算ができるので、ほかにも剰余環関連のアルゴリズム等を調べてみたいです。