12

I have to compute efficiently a^^b mod m for large values of a,b,m<2^32
where ^^ is the tetration operator: 2^^4=2^(2^(2^2))

m is not a prime number and not a power of ten.

Can you help?

JeanClaudeDaudin
  • 424
  • 6
  • 15

2 Answers2

17

To be clear, a^^b is not the same thing as a^b, it is the exponential tower a^(a^(a^...^a)) where there are b copies of a, also known as tetration. Let T(a,b) = a^^b so T(a,1) = a and T(a,b) = a^T(a,b-1).

To compute T(a,b) mod m = a^T(a,b-1) mod m, we want to compute a power of a mod m with an extremely large exponent. What you can use is that modular exponentiation is preperiodic with preperiod length at most the greatest power of a prime in the prime factorization of m, which is at most log_2 m, and the period length divides phi(m), where phi(m) is Euler's totient function. In fact, the period length divides Carmichael's function of m, lambda(m). So,

a^k mod m = a^(k+phi(m)) mod m as long as k>log_2 m.

Be careful that a is not necessarily relatively prime to m (or later, to phi(m), phi(phi(m)), etc.). If it were, you could say that a^k mod m = a^(k mod phi(m)) mod m. However, this is not always true when a and m are not relatively prime. For example, phi(100) = 40, and 2^1 mod 100 = 2, but 2^41 mod 100 = 52. You can reduce large exponents to congruent numbers mod phi(m) that are at least log_2 m, so you can say that 2^10001 mod 100 = 2^41 mod 100 but you can't reduce that to 2^1 mod 100. You could define a mod m [minimum x] or use min + mod(a-min,m) as long as a>min.

If T(a,b-1) > [log_2 m], then

a^T(a,b-1) mod m = a^(T(a,b-1) mod phi(m) [minimum [log_2 m]])

otherwise just calculate a^T(a,b-1) mod m.

Recursively calculate this. You can replace phi(m) with lambda(m).

It doesn't take very long to compute the prime factorization of a number under 2^32 since you can determine the prime factors in at most 2^16 = 65,536 trial divisions. Number-theoretic function like phi and lambda are easily expressed in terms of the prime factorization.

At each step, you will need to be able to calculate modular powers with small exponents.

You end up calculating powers mod phi(m), then powers mod phi(phi(m)), then powers mod phi(phi(phi(m))), etc. It doesn't take that many iterations before the iterated phi function is 1, which means you reduce everything to 0, and you no longer get any change by increasing the height of the tower.

Here is an example, of a type that is included in high school math competitions where the competitors are supposed to rediscover this and execute it by hand. What are the last two digits of 14^^2016?

14^^2016 mod 100 
= 14^T(14,2015) mod 100
= 14^(T(14,2015) mod lambda(100) [minimum 6]) mod 100
= 14^(T(14,2015 mod 20 [minimum 6]) mod 100

T(14,2015) mod 20 
= 14^T(14,2014) mod 20
= 14^(T(14,2014) mod 4 [minimum 4]) mod 20

T(14,2014) mod 4
= 14^T(14,2013) mod 4
= 14^(T(14,2013 mod 2 [minimum 2]) mod 4

T(14,2013) mod 2
= 14^T(14,2012) mod 2
= 14^(T(14,2012 mod 1 [minimum 1]) mod 2
= 14^(1) mod 2
= 14 mod 2
= 0

T(14,2014) mod 4 
= 14^(0 mod 2 [minimum 2]) mod 4
= 14^2 mod 4
= 0

T(14,2015) mod 20
= 14^(0 mod 4 [minimum 4]) mod 20 
= 14^4 mod 20
= 16

T(14,2016) mod 100
= 14^(16 mod 20 [minimum 6]) mod 100
= 14^16 mod 100
= 36

So, 14^14^14^...^14 ends in the digits ...36.

Douglas Zare
  • 3,296
  • 1
  • 14
  • 21
  • 1
    Can you give the recursive algorithm part in pseudo-code with all the stop conditions of the recursion and taking into account of the case when a and m are not relatively prime. – JeanClaudeDaudin Jun 11 '15 at 11:12
  • @bilbo: The reason I used x mod y [min k] instead of x mod y was that my answer handles the case that a is not relatively prime to m, or to phi(m). I never assumed that a was relatively prime to m. The lines "If T(a,b-1) > [log_2 m], then a^T(a,b-1) mod m = a^(T(a,b-1) mod phi(m) [minimum [log_2 m]]) otherwise just calculate a^T(a,b-1) mod m." describe the algorithm. – Douglas Zare Jun 12 '15 at 03:56
  • I don't see how to compute T(a,b-1) without overflow to test T(a,b-1) > [log_2 m]. The recursive function I compute is T1(a,b,totient(m),log_2(m)) but I don't see how to include the test T(a,b-1) > [log_2 m] – JeanClaudeDaudin Jun 12 '15 at 12:02
  • If b>=1, then T(a,b) >= a. If that doesn't let you compare T(a,b) with log_2 m, then take the log base a of both sides, so you compare T(a,b-1) with log_a(log_2 m). Apply this recursively. – Douglas Zare Jun 12 '15 at 17:57
  • @DouglasZare could you please explain this sentence "a mod m [minimum x] or use min + mod(a-min,m) as long as a>min". I tried to implement this algorithm using Eulers totient, and in most of my test cases it succed but not all and particularly your example fails as well, but as I don't have an implementation of carmichael lambda function it's hard to compare results. – Kaveh Hadjari Dec 12 '18 at 15:25
  • @Kaveh: What do you mean that my example fails? – Douglas Zare Dec 13 '18 at 12:04
  • @DouglasZare: Sorry if I'm unclear: I've put my implementation on repl.it in python code: https://repl.it/repls/UnusualRingedVirtualmachines , At the end it runs your example but the output is 16 not 36 as in your example so I must have missed something in your explanation, a hint would be very welcome. Thanks! – Kaveh Hadjari Dec 13 '18 at 14:08
  • 1
    Fixed it. What did not work for me was infact this step: a^(T(a,b-1) mod phi(m) [minimum [log_2 m]]). I changed it to this: a^T = a^(phi(m) + (T(a,b-1) mod phi(m)) (mod m) according to the solution given to this SO-question: https://stackoverflow.com/questions/21367824/how-to-evalute-an-exponential-tower-modulo-a-prime .. And that did it. – Kaveh Hadjari Dec 16 '18 at 21:06
1

I struggled a bit in understanding the accepted answer, & the other stack-overflow question linked by Kaveh helped.

The full solution uses a variety of ideas:

  1. Exponentiation by Squaring.
  2. Phi function computation from prime factors.
  3. The fact that exponentiation is (pre-)periodic with the (loose) guarantee that values greater than phi(m) will be a part of the cycle.

Adding a full simplified working rust implementation for anyone looking for more clarity:

fn phi(mut x: u64) -> u64 {
    let mut c = 2;
    let mut s = x;
    while x > 1 {
        if x % c == 0 {
            s -= s / c;
        }
        while x % c == 0 {
            x /= c;
        }
        c += 1;
    }
    s
}

fn mod_exp(b: u64, p: u64, m: u64) -> u64 {
    if p == 0 {
        1
    } else if p & 1 == 1 {
        (b * mod_exp(b, p - 1, m)) % m
    } else {
        mod_exp((b * b) % m, p >> 1, m)
    }
}

fn mod_tetr(b: u64, p: u64, m: u64) -> u64 {
    if b % m == 0 {
        0
    } else if p == 1 {
        b
    } else {
        mod_exp(b, phi(m) + mod_tetr(b, p - 1, phi(m)), m)
    }
}

Aditya
  • 80
  • 6
  • it seems there is a problem in your solution: mod_tetr(3,2,20) return 1 instead of 7 and mod_tetr(3,3,345) return 1 instead of 312 – JeanClaudeDaudin Feb 12 '23 at 00:13
  • Thank you for validating, I tested these cases & was unable to replicate the issue. PTAL. https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ae38ce7667da53584b54f36927ac3bd5 – Aditya Feb 20 '23 at 06:10
  • sorry your code is ok, it was my mistake when translating your code in c++. – JeanClaudeDaudin Feb 20 '23 at 19:14