pub trait ModTetration {
    // Required method
    fn mod_tetration(self, b: Self, n: Self) -> Self;
}
Expand description

tetration。

${}^b a\bmod n$ を求める。${}^\bullet \bullet$ は次のように定義される。 $$ {}^b a = \begin{cases} 1, & \text{if } b = 0; \\ a^{\left({}^{(b-1)} a\right)}, & \text{if } b \ge 1. \end{cases} $$ 直感的に書けば、$\underbrace{a^{(a^{(\cdots ^a)})}}_{b\text{ many}} \bmod n$ である。

Idea

大変大きくなりうる $z$ に対して $a^z\bmod n$ を求めることを考える。 このとき、dlogIdea と同じ議論から、ある $\mu$, $\lambda$ が存在して $z\lt\mu$ または $z=\mu+q\cdot\lambda+r$ とでき、後者のとき $a^z\equiv a^{\mu+r}\pmod{n}$ が成り立つ。

ここで、$\mu\le\log_2(n)$, $\lambda\sqsubseteq\varphi(n)$ である。 さらに、任意の $n$ に対して $\log_2(n)\le\varphi(n)$ なので、$z\ge\varphi(n)$ ならば $z\ge\mu$ とわかる。よって、以下のようにできる。 $$ \begin{aligned} a^z \equiv \begin{cases} a^z, & \text{if }z\lt \varphi(n); \\ a^{(z\bmod\varphi(n))+\varphi(n)}, & \text{otherwise}. \end{cases} \end{aligned} $$

直感的には、指数部が $\varphi(n)$ 以上であればすでに周期の中に入っており、入った後は $\varphi(n)$ を法として合同かつ $\varphi(n)$ 以上の値さえ得られれば十分ということである。

When $b$ is large

前述のように、${}^b a\bmod{n}$ を求める際に ${}^{b-1} a$ を法 ${\varphi(n)}$ で考える。 その次は $\varphi(\varphi(n)), \varphi(\varphi(\varphi(n))), \dots$ と続く。 $\varphi^\star(n)$ 段では考えるべき法が $1$ となり、それより上の段のことは無視できる。

そこで、$\varphi^\star(n)$ を考える。奇素数 $p$ に対して $\varphi(p^e)=p^{e-1}\cdot(p-1)$ が偶数であることと、$\varphi(2^e)=2^{e-1}$ であることから、$\varphi(\varphi(n))\lt n/2$ が成り立つ。すなわち、$\varphi^\star(n)\le 2\log(n)$ である1

よって、$b\ge 2\log(n)$ であれば ${}^{b+1} a\equiv {}^b a\pmod{n}$ となる。

Common bugs

繰り返し二乗法で $\varphi(n)$ 以上か判断しつつ $a^z\bmod\varphi(n)$ を求める際、 $a^{2^\bullet}$ が $\varphi(n)$ 以上になった時点で $a^z\ge\varphi(n)$ と判断してしまうと、誤検出してしまう場合がある。

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 が返ってしまう。 このバグや、あるいはそもそも $z\ge\varphi(n)$ を仮定していることなどにより、 ${}^3 2\bmod 32 = 0$ としてしまうコードがたくさん提出されている。 $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$ $\bullet$

Complexity

$O(\sqrt{n})$ time.

律速は、$\varphi(n), \varphi(\varphi(n)), \dots$ を求めるパートであり、 $O(\sum_{i=0}^{\varphi^\star(n)} \sqrt{n/2^i}) = O(\sqrt{n})$ である。

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

${}^b a$ は $a\uparrow\uparrow b$ (Knuth’s up-arrow notation) や $a\to b\to 2$ (Conway chained arrow notation) などとも表記される。


  1. ゆるゆるな bound である。実際にはもっと速く減りそう。 

Required Methods§

source

fn mod_tetration(self, b: Self, n: Self) -> Self

Implementations on Foreign Types§

source§

impl ModTetration for u8

source§

fn mod_tetration(self, b: Self, n: Self) -> Self

source§

impl ModTetration for u128

source§

fn mod_tetration(self, b: Self, n: Self) -> Self

source§

impl ModTetration for u64

source§

fn mod_tetration(self, b: Self, n: Self) -> Self

source§

impl ModTetration for u32

source§

fn mod_tetration(self, b: Self, n: Self) -> Self

source§

impl ModTetration for u16

source§

fn mod_tetration(self, b: Self, n: Self) -> Self

source§

impl ModTetration for usize

source§

fn mod_tetration(self, b: Self, n: Self) -> Self

Implementors§