4

I realise that there are several topics already covering this. But my question is not regarding how to build such an algorithm, rather in finding what mistake I have made in my implementation that's causing a single test out of dozens to fail.

The challenge: supplied with a std::list<int> of random numbers, determine the last digit of x1 ^ (x2 ^ (x3 ^ (... ^ xn))). These numbers are large enough, or the lists long enough, that the result is astronomical and cannot be handled by traditional means.

My solution: I chose to use a modular arithmetic approach. In short, the last digit of these huge powers will be the same as that of a reduced power consisting of the first digit of the base (mod 10), raised to the last two digits of the exponent (mod 100). The units in a sequence of powers repeat in patterns of 4 at most, so we can use mod 4 to reduce the exponent, offset by 4 to avoid remainders of 0. At least, this is my understanding of it so far based on the following resources: brilliant / quora.

#include <list>
#include <cmath>

int last_digit(std::list<int> arr)
{
  // Break conditions, extract last digit
  if (arr.size() == 1) return arr.back() % 10;
  if (arr.size() == 0) return 1;

  // Extract the last 2 items in the list
  std::list<int> power(std::prev(arr.end(), 2), arr.end());
  arr.pop_back(); arr.pop_back();

  // Treat them as the base and exponent for this recursion
  long base = power.front(), exponent = power.back(), next;

  // Calculate the current power
  switch (exponent)
  {
    case  0: next = 1; break;
    case  1: next = base % 100; break;
    default: next = (long)std::pow(base % 10, exponent % 4 + 4) % 100;
  }
  if (base != 0 && next == 0) next = 100;

  // Add it as the last item in the list
  arr.push_back(next);

  // Recursively deal with the next 2 items in the list
  return last_digit(arr);
}

Random example: 123,232 694,027 140,249 ≡ 8

  • First recrusion: { 123'232, 694'027, 140'249 }
    • base: 694,027 mod 10 = 7
    • exponent: 140,249 mod 4 + 4 = 5
    • next: 75 = 16,807 mod 100 = 7
  • Second recursion: { 123'232, 7 }
    • base: 123,232 mod 10 = 2
    • exponent: 7 mod 4 + 4 = 7
    • next: 27 = 128 mod 100 = 28
  • Third recursion: { 28 }
    • return: 28 mod 10 = 8

The problem: this works for dozens of test cases (like the one above), but fails for 2 2 101 2 ≡ 6.
By hand:

  • 1012 = 10,201
  • 210,201 mod 4 = 0, + 4 = 4
  • 24 = 16 // 6 -correct

Following the algorithm, however:

  • First recursion: { 2, 2, 101, 2 }
    • base: 101 mod 10 = 1
    • exponent: 2 mod 4 + 4 = 6
    • next: 16 = 1 mod 100 = 1
  • Second recursion: { 2, 2, 1 } (we can already see that the result is going to be 4)
    • exponent = 1, next = 2 mod 100 = 2
  • Third recursion: { 2, 2 }
    • base: 2 mod 10 = 2
    • exponent: 2 mod 4 + 4 = 6
    • next: 26 = 64 mod 100 = 64
  • Fourth recursion: { 64 }
    • return 64 mod 10 = 4 // -wrong

In a way, I see what's going on, but I'm not entirely sure why it's happening for this one specific case, and not for dozens of others. I admit I'm rather pushing the limits of my maths knowledge here, but I get the impression I'm just missing a tiny part of the puzzle.

I reckon this post is long and arduous enough as it is. If anyone has any insights into where I'm going wrong, I'd appreciate some pointers.

Chris
  • 65
  • 5
  • 2
    Right now you have `if (base != 0 && next = 0)`. I believe you meant `next == 0`? – TrebledJ Dec 13 '18 at 09:00
  • @TrebuchetMS indeed I did, thanks for pointing that out :) Unfortunately, it's just a typo – Chris Dec 13 '18 at 10:12
  • 1
    Read up on https://en.wikipedia.org/wiki/Fermat%27s_little_theorem. Apply the theorem mod 5 and you'll see where you're algorithm is failing. – eh9 Dec 19 '18 at 16:49
  • @eh9 thanks for the resource, I'll give it a go. – Chris Dec 19 '18 at 18:52

1 Answers1

2

There's a lot of problems regarding the modulo of a really big number and a lot of the sol'ns back there was basically based on basic number theory. Fermat's Little Theorem, Chinese Remainder Theorem, and the Euler's Phi Function can all help you solve such problems. I recommend you to read "A Computational Introduction to Number Theory and Algebra" by Victor Shoup. It'll help you a lot to better simplify and approach number theory-related questions better. Here's the link for the book in case you'll browse this answer.