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
- return: 28 mod 10 =
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
- return 64 mod 10 =
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.