Ackermann 関数。
与えられた (a,b,n) に対して A(a,b)modn を返す。
自然数 a, b に対して Péter–Ackermann 関数 A(a,b) は次のように定義される。
- A(0,b)=b+1,
- A(a+1,0)=A(a,1),
- A(a+1,b+1)=A(a,A(a+1,b)).
$\gdef{\hyper}{\operatorname{hyper}}$
以下の性質が知られている。
$$ a\gt 0 \implies \hyper_a(2, b+3)-3. $$
ただし、$\hyper_a$ は a 番目のハイパー演算子である。
a≤4 までは mod_pow
や mod_tetration
を用いて簡単に求められる。
mod_tetration
と同様に、指数部が十分大きくなると値が一定になるので、それを利用する。
$A(5, b) = \hyper_5(2, b+3)-3 = \underbrace{{}^{{}^{{}^2\cdots} 2} 2}_{b+3\text{ many}} - 3$
となるが、A(5,0) は現実的な値として求められる。
A(5,0)=222−3=42−3=2222−3=224−3=216−3=65533.
一方で、A(5,1) は次のようになる。
A(5,1)=2222−3=655362−3=65536 many22⋯2−3.
ここで mod_tetration
の議論を思い出すと、bamodn は b≥2log(n)
であれば一定値となる。引数の型が u64
である今、b として意味があるのは高々
2log(264)=128 程度であり、A(5,1) はそれを十分に上回る。
すなわち、A(5,1)modn=A(4,2log(n))modn として計算してしまってよい1。
さらに、A(6,0)=A(5,1) などから、a≥6 についても同様にでき、次のようにできる。
- A(0,b)≡b+1(modn),
- A(1,b)≡b+2(modn),
- A(2,b)≡2b+3(modn),
- A(3,b)≡2b+3−3(modn),
- A(4,b)≡b+32−3(modn),
- A(5,0)≡65533(modn),
- A(5,b)≡A(4,2log(n))(modn) for b≥1,
- A(a,b)≡A(4,2log(n))(modn) for a≥6.
入力 | 時間計算量 |
a≤2 or (a,b)=(5,0) | O(1) |
a=3 | O(log(b)) |
otherwise | O(n) |
use nekolib::math::ModAckermann;
let n = 10_u64.pow(9);
assert_eq!(2_u64.mod_ackermann(5, n), 13);
assert_eq!(3_u64.mod_ackermann(7, n), 1_021);
assert_eq!(4_u64.mod_ackermann(2, n), 719_156_733);
assert_eq!(4_u64.mod_ackermann(3, n), 437_428_733);
assert_eq!(4_u64.mod_ackermann(8, n), 432_948_733);
assert_eq!(9_u64.mod_ackermann(9, n), 432_948_733);