有限体の複数の元の逆数を一度に求める高速な方法
初めに
有限体の要素の逆数(逆元)操作は重たい処理です。
ここでは複数(
2個の場合
ここで、
演算コストは1回の乗算を
BLS12-381の曲線では
3個の場合
4個以上の場合
計算量を減らすために共通部分を探すと、
-
,abc \times 1/abcd=1/d -
,ab \times (d \times 1/abcd)=1/c -
,a \times (cd \times 1/abcd)=1/b bcd \times 1/abcd = 1/a
とします。これなら1個あたり3回の乗算ですみます。
一時バッファを用意する
より一般的に
0 | 1 | 2 | ... | |||
---|---|---|---|---|---|---|
... |
このテーブル作成のコストは
そして
以下同様に
演算コストは大体
0を含むときの注意点
また
C++による実装例
mclで使われているC++でのコードを抜粋します。const Tin& x
を入力として、それらの逆数を Tout& y
に出力します。引数の T *t
はテーブル作成のためのワーク領域です。
T::mul(T& z, const T& x, const T& y)
は
前半で x[i]
が0でも1でもないときだけ掛け算しながらテーブル T
に配置し、後半で t[pos-1]
の逆数を求めて、後ろから掛け算しながら逆数を求めています。
template<class Tout, class Tin, class T>
size_t invVecWork(Tout& y, Tin& x, size_t n, T *t)
{
size_t pos = 0;
for (size_t i = 0; i < n; i++) {
if (!(x[i].isZero() || x[i].isOne())) {
if (pos == 0) {
t[pos] = x[i];
} else {
T::mul(t[pos], t[pos - 1], x[i]);
}
pos++;
}
}
const size_t retNum = pos;
T inv;
if (pos > 0) {
T::inv(inv, t[pos - 1]);
pos--;
}
for (size_t i = 0; i < n; i++) {
const size_t idx = n - 1 - i;
if (x[idx].isZero() || x[idx].isOne()) {
y[idx] = x[idx];
} else {
if (pos > 0) {
T::mul(y[idx], inv, t[pos - 1]);
inv *= x[idx];
pos--;
} else {
y[idx] = inv;
}
}
}
return retNum;
}
楕円曲線の複数の点のアフィン座標化
楕円曲線の点を射影座標やヤコビ座標で表現しているとき、処理の最後でアフィン座標に変換する場合があります。
アフィン座標に変換するときは、
浮動小数点数への適用の可否
今回紹介した手法を有限体ではなく浮動小数点数に適用するのは慎重にした方がよいでしょう。
なぜなら有限体では常に厳密に計算できるので1個ずつ逆数を求めるのと結果は完全に一致します。
しかし、浮動小数点数では
また、SIMD命令には近似逆数命令を使った逆数計算手法が利用できることも多く、今回の方法を使わなくても高速に計算できる可能性が高いからです。
Discussion