Skip to content Skip to sidebar Skip to footer

Last Digit Of Power List

Outline of problem: Please note I will abuse the life out of ^ and use it as a power symbol, despite the caret symbol being the bitwise XOR operator in JS. Take a list of positive

Solution 1:

Finally! after doing some digging about I realized I can modify the initial idea and get the answer I wanted:

letLD = (as) =>
  as.reduceRight((acc, val) =>Math.pow(val < 20 ? val : (val % 20 + 20), acc < 4 ? acc : (acc % 4 + 4)) 
  , 1) % 10

Tests:

letLD = (as) =>
  as.reduceRight((acc, val) =>Math.pow(val < 20 ? val : (val % 20 + 20), acc < 4 ? acc : (acc % 4 + 4)) 
  , 1) % 10letLDTest = (array, expected) => 
  console.log((LD(array) === expected ? "Success" : "Failed"), 
    "for", array, "got", LD(array), "expected", expected);

LDTest([ 2, 2, 2 ],    6)
LDTest([ 2, 2, 2, 2 ], 6)
LDTest([ 3, 4, 5 ],    1)
LDTest([ 6, 8, 10 ],   6)
LDTest([ 2, 2, 0 ],    2)
LDTest([ 12, 30, 21 ], 6) 
LDTest([ 0, 0 ],       1) // x^0 === 1 LDTest([ 0 ],          0)  

So why modulo 20? Because a modulus of 10 loses precision in the cases such as [ 2,2,2,2 ], when we hit the last step as from the question example:

Fourth iteration, where the issue becomes apparent. a = 6, v = 2:

p[2 % 10][(6-1) % p[2 % 10].length]

= [ 2,4,8,6 ][5 % 4]

= 4

Easily disproved by 2 ^ 16 = 65536.

By simply allowing a multiple up to 20, we have the LCM (lowest common multiple) for each count of the array, as well as for the length of p, which is 10. (LCM([ 1,2,4,10 ]) === 20).

However, as the exponential is now never higher than 40^8 (approximately 6 trillion), and as it is taken to the modulo of 4 in the next iteration, we can simply do the exponential and return the answer each time.

Of course to get the digit in the final case, we need to take modulo of 10 to just return that last digit.


There's still some stuff I'm not understanding here.

We allow any value under the modulo value to retain its value with the ternary operators. E.g., for the exponent, prev < 4 ? prev : (prev % 4 + 4). However I initially believed this to be prev === 0 ? 0 : (prev % 4 + 4).

This is because th exponent of zero does not have the same ending digit as the other multiples of the modulo, it always equals 1 (x ^ 0 === 1). So, by adding 4, we get a value that does have the same last digit, and by leaving zero alone we still get 1 for zero exponents.

Why is it that prev < 4 ? prev : (prev % 4 + 4) was required to make this answer correct? Why is e.g., prev = 3, need to be 3 instead of adding 4 like the others?

Solution 2:

I believe your top-down approach is somewhat flawed, because LD(b) is not the only determining factor in LD(a^b), unlike with a - i.e. all digits of b influence the value of LD(a^b). Therefore, with a top-down algorithm, it only makes sense to compute the LDat the end, which kind of defeats the point.


That's not to say the formula you derived is useless - quite the opposite.

Looking at the inner index, we see that (b - 1) % p[a % 10].length could potentially be computed recursively bottom-up. So what are the possible values of L = p[a % 10].length? 1, 2, and 4. The corresponding possible values for I = (b - 1) % L are:

  • For L = 1, I = 0 trivially.
  • For L = 2, I = 0 if b is odd. Let b = c ^ d, and note that b is odd if either c is odd or d = 0, otherwise even. Thus this case is also trivial if we peek at the next number in the array.
  • For L = 4, I is simply the last digit of b - 1 in base 4 (call it LD4). We can compute an analogous digit table, and process the rest of the array as before:

    // base 4 tableconst q = [
       [0],  // 0^1=0 ...
       [1],  // 1^1=1 ...
       [2, 0, 0, 0, ..... ], // infinitely recurring
       [3, 1], // 3^1=3, 3^2=1, 3^3=3, 3^4=1 ...
    ];
    

Ah. Well, since there are so few cases, a few if-clauses won't hurt:

LD2(a) | LD2(a^b)
----------------------------
0      | 1ifb=0, else01      | 1 always

LD4(a) | LD4(a^b)
----------------------------------------
0      | 1ifb=0, else01      | 1 always
2      | 1ifb=0, 2ifb=1, else03      | 3if b odd, else1

With the assumption that 0^0 = 1. Also, LD4(b - 1) = LD4(b + 3).


Code:

function isEnd(a, i)
{
   return (i > a.length - 1);
}

function isZero(a, i)
{
   if (!isEnd(a, i))
      if (a[i] == 0)
         return !isZero(a, i+1);
   returnfalse;
}

function isUnity(a, i)
{
   if (isEnd(a, i) || a[i] == 1)
      returntrue;
   return isZero(a, i+1);
}

function isOdd(a, i)
{
   if (isEnd(a, i) || a[i] % 2 == 1)
      returntrue;
   return isZero(a, i+1);
}

function LD2(a, i)
{
   if (isEnd(a, i) || a[i] % 2 == 1)
      return1;
   return isZero(a, i+1) ? 1 : 0;
}

function LD4(a, i)
{
   if (isEnd(a, i)) return1;
   switch (a[i] % 4) {
      case 0: return isZero(a, i+1) ? 1 : 0;
      case 1: return1;
      case 2: 
         if (isZero(a, i+1)) return1;
         if (isUnity(a, i+1)) return2;
         return0;
      case 3: return isOdd(a, i+1) ? 3 : 1;
      default: return -1; // exception?
   }
}

const p = [
   [ 0 ],      // 0^1=0, 0^2=0 ...
   [ 1 ],      // 1^1=1, 1^2=1 ...
   [ 2,4,8,6 ], // 2^1=2, 2^2=4 ...
   [ 3,9,7,1 ], // 3^1=3, 3^2=9 ...
   [ 4,6 ],     
   [ 5 ],      
   [ 6 ],      
   [ 7,9,3,1 ],
   [ 8,4,2,6 ],
   [ 9,1 ]
];

function LD10(a)
{
   let LDa = a[0] % 10;
   if (isZero(a, 1)) return1; // corner case not present in tablesconst row = p[LDa];
   switch (row.length) {
      case 1: return row[0];
      case 2: return row[1 - LD2(a, 1)];
      case 4: return row[(LD4(a, 1) + 3) % 4];
      default: return -1; // exception?
   }
}

The above code now passes all test cases.

Solution 3:

lets keep in mind that (a^b)%c = (a%c)^b % c.

I want to know lastDigit. lastDigit = x^y^z % 10 = (x%10 ^ y ^ z) % 10.

p[x % 10] has the pattern of repetition of powers of x. A pattern can have a length of 1, 2 or 4.

If the pattern had a length of 4, then lastDigit = p[x][(y^z % 4)] = p[x][(y%4)^z %4]

If our problem was not for x,y,z but for x0,x1,x2...xn:

lastDigit
 = p[x0][(x1 ^ x2 ... ^ xn-1 ^ xn) %4]
 = p[x0][((x1 ^ x2 ... ^ xn-1)%4 ^ xn) %4]

And I think teh recursive nature is apparent now:

I will use the number 4 for remainder because it is the smallest number that can divide 1, 2 and 4, for which patterns are also relatively easy to find.

const p = [
  [ 0 ],	  // 0^1=0, 0^2=0 ...
  [ 1 ],	  // 1^1=1, 1^2=1 ...
  [ 2,4,8,6 ], // 2^1=2, 2^2=4 ...
  [ 3,9,7,1 ], // 3^1=3, 3^2=9 ...
  [ 4,6 ],	 
  [ 5 ],	   
  [ 6 ],	   
  [ 7,9,3,1 ], 
  [ 8,4,2,6 ], 
  [ 9,1 ]	 
];

//returns x^y % 4functionpowMod4(x, y) {
	switch (x % 4) {
		case0:
			return0;
		case1:
			return1;
		case2:
			return y == 1 ? 2 : 0;
		case3:
			return (y % 2) == 0 ? 1 : 3;
	}
}

functionfoo(arr) {
	arr = arr.map((it, i, list) => {
		if (i + 1 < list.length && list[i + 1] === 0) {
			return1;
		} else {
			return it;
		}
	}).filter(x => x !== 0);

	let firstElem = arr[0] % 10;
	let l = p[firstElem].lengthlet resp = arr.slice(1).reduce((acc, v) => {
		if (acc === undefined) {
			return v
		}
		let rem = powMod4(acc, v);
		//console.log(v, acc, " => ", rem);return rem;

	}, undefined);


	return p[firstElem][(resp + l - 1) % l];
}

functiontest(arr, expected) {
  let resp = foo(arr);
	console.log(arr.join("^"), resp, resp == expected ? "ok" : "FAIL")
}

test([3, 4, 5], 1)
test([2, 2, 2, 2], 6)
test([4, 13, 16], 4)
test([3,5,7,10], 3)
test([41,42,43], 1)
test([7,6,21], 1)
test([2,2,2,0], 4)

So all I did is, I dropped the LD function. Instead, I am trying to find x1^x2...^xn % p[x0].length. In contrast, you were trying to find x1^x2...^xn % 10

Post a Comment for "Last Digit Of Power List"