素数 m を法とした逆元のテーブル。
以下、i−1modj を i(j)−1 と書く。
次で定められる a=(a0,a1,…,an) を返す。
ai={i(m)−1,0,if i(m)−1 exists;otherwise.
Note: i(m)−1=0。
次のことが成り立つ:
- 0(m)−1 は存在しない。
- 1(m)−1=1 である。
(i+m)(m)−1=i(m)−1 なので、i<m について考える。
各 j (1≤j<i) については j(m)−1
が得られているとする。ただし、m が素数なので、これらは常に存在する。
m=q⋅i+r (0≤r<i) とおき、i(m)−1 を辺々掛けると
m⋅i(m)−10=q⋅i⋅i(m)−1+r⋅i(m)−1≡q+r⋅i(m)−1(modm).
よって、
i(m)−1≡−q⋅r(m)−1(modm).
q>0 と r(m)−1>0 であることから、
i(m)−1=m−(q⋅r(m)−1modm)
となる。
r<i より r(m)−1
は既に得られているため、i(m)−1 が順次 O(1) time で求まる。
一般のケースでは gcd(r,m)=gcd(i,m) とは限らないので注意。
たとえば、3(8)−1 を求める際、8=3⋅2+2 なので
2(8)−1 が必要になるが、これは存在しない。
一般のケースで必要になる場合は、線形篩を用いる方法がある。See:
LinearSieve::recips
O(n) time。
use nekolib::math::mod_recip_table_prime;
assert_eq!(mod_recip_table_prime(2, 3), [0, 1, 2]);
assert_eq!(mod_recip_table_prime(4, 5), [0, 1, 3, 2, 4]);
assert_eq!(mod_recip_table_prime(9, 5), [0, 1, 3, 2, 4].repeat(2));