Trait nekolib::math::ModTetration
source · pub trait ModTetration {
// Required method
fn mod_tetration(self, b: Self, n: Self) -> Self;
}
Expand description
tetration。
を求める。 は次のように定義される。 直感的に書けば、 である。
Idea
大変大きくなりうる に対して を求めることを考える。
このとき、dlog
の Idea と同じ議論から、ある , が存在して
または とでき、後者のとき
が成り立つ。
ここで、, である。 さらに、任意の に対して なので、 ならば とわかる。よって、以下のようにできる。
直感的には、指数部が 以上であればすでに周期の中に入っており、入った後は を法として合同かつ 以上の値さえ得られれば十分ということである。
When is large
前述のように、 を求める際に を法 で考える。 その次は と続く。 段では考えるべき法が となり、それより上の段のことは無視できる。
そこで、 を考える。奇素数 に対して が偶数であることと、 であることから、 が成り立つ。すなわち、 である1。
よって、 であれば となる。
Common bugs
繰り返し二乗法で 以上か判断しつつ を求める際、 が 以上になった時点で と判断してしまうと、誤検出してしまう場合がある。
ⓘ
fn mod_pow(mut a: u64, mut b: u64, n: u64) -> u64 {
let mut res = 1 % n;
let mut large = false;
while b > 0 {
if b & 1 == 1 {
res *= a;
if res >= n { res %= n; large = true; }
}
a *= a;
if a >= n { a %= n; large = true; } // !
b >>= 1;
}
if large { res + n } else { res }
}
最後のループで初めて a >= n
になると、res < n
なのに res + n
が返ってしまう。
このバグや、あるいはそもそも を仮定していることなどにより、
としてしまうコードがたくさん提出されている。
Complexity
time.
律速は、 を求めるパートであり、 である。
Examples
use nekolib::math::ModTetration;
let n = 10_u64.pow(9);
assert_eq!(0_u64.mod_tetration(0, n), 1);
assert_eq!(0_u64.mod_tetration(1, n), 0);
assert_eq!(0_u64.mod_tetration(2, n), 1);
assert_eq!(0_u64.mod_tetration(3, n), 0);
assert_eq!(1_u64.mod_tetration(0, n), 1);
assert_eq!(1_u64.mod_tetration(1, n), 1);
assert_eq!(2_u64.mod_tetration(0, n), 1);
assert_eq!(2_u64.mod_tetration(1, n), 2);
assert_eq!(2_u64.mod_tetration(2, n), 4);
assert_eq!(2_u64.mod_tetration(3, n), 16);
assert_eq!(2_u64.mod_tetration(4, n), 65536);
assert_eq!(3_u64.mod_tetration(9, n), 64_195_387);
assert_eq!(3_u64.mod_tetration(10, n), 464_195_387);
assert_eq!(3_u64.mod_tetration(11, n), 464_195_387);
assert_eq!(3_u64.mod_tetration(99, n), 464_195_387);
Notations
は (Knuth’s up-arrow notation) や (Conway chained arrow notation) などとも表記される。
ゆるゆるな bound である。実際にはもっと速く減りそう。 ↩