1

Question

Find the last k digits of the number nn. It is guaranteed that the length of number nn is not less than k.

Example
For n = 5, k = 3, the result should be "125"

5^5 = 3125, last 3 digits is "125"

Input / Output
[input] integer n

1 ≤ N ≤ 10^9

[input] integer k

1 ≤ k ≤ 9

[output] a string

string of length k ---> last k digits of n^n

My Code

function n2n(n, k) {
  
  let a = Math.pow(n, n);
  let b = Array.from(a.toString()).map(Number);
  return b.slice((b.length-k),).join('');
}

console.log(n2n(5, 25));
console.log(n2n(5, 3));
console.log(n2n(1, 1));
console.log(n2n(5,4));
console.log(n2n(43, 7));
console.log(n2n(999, 9));

This code seems to work for the smaller numbers, but then fails with big numbers.

My theory is that it is because when the number gets really big it is no longer a typical number but becomes 5345354+e9325 or something like that.

Do you agree that my code works? Is there a way to prevent certain numbers bing processes as NaN.

The console logs in my code provides:

3125
125
1
3125
268NaNNaN70
NaN
Peter B
  • 22,460
  • 5
  • 32
  • 69
AndrewNeedsHelp
  • 385
  • 4
  • 15
  • The goal is integer, you are using float functions (admittedly this is an assumption, that the `pow()` is like in C; let me know if I assume wrongly here). Hence this seems relevant: https://stackoverflow.com/questions/588004/is-floating-point-math-broken?r=SearchResults&s=1|1412.7540 – RubberBee Jun 26 '20 at 09:06
  • 2
    The result of your calculations just don't fit into the 64 bits provided by the JavaScript `Number` type. – Robby Cornelissen Jun 26 '20 at 09:07
  • Thank you Robby, is there a way to resolve it? It is a JS algorithm on code wars, so surely there must be a way to resolve - do i have to make it a string at some point perhaps? – AndrewNeedsHelp Jun 26 '20 at 09:08
  • @RubberBee The standard JavaScript `Number` type is a floating point type. – Robby Cornelissen Jun 26 '20 at 09:08
  • 1
    @RobbyCornelissen And that is the datatype returned by `pow()`? I read that as agreeing with my comment. Or do I misunderstand your point? Your range issue definitly also is relevant, by the way; I am not contradicting your either. – RubberBee Jun 26 '20 at 09:10

5 Answers5

1

You should convert the number to BigInt to prevent the result is too big that causes precision loss:

function n2n(n, k) {
  let a = BigInt(n) ** BigInt(n);
  let b = Array.from(a.toString()).map(Number);
  return b.slice((b.length-k),).join('');
}

console.log(n2n(5, 25));
console.log(n2n(5, 3));
console.log(n2n(1, 1));
console.log(n2n(5, 4));
console.log(n2n(43, 7));
console.log(n2n(999, 9));
Hao Wu
  • 17,573
  • 6
  • 28
  • 60
  • 1
    To elaborate on this: 43^43 is 17,343,773,367,030,267,519,903,781,288,812,032,158,308,062,539,012,091,953,077,767,198,995,507--the last 7 digits should be 8,995,507, but the actual floating point number that is stored upon that calculation is 17,343,773,367,030,267,785,865,676,415,583,446,101,982,589,718,723,227,046,627,401,890,004,992, which isn't even close--not only are the last 7 digits wrong, but the last *57* all are. – Putnam Jun 26 '20 at 09:18
  • Would you be able to explain how Big Int works? I appreciate the link, but i found it quite confusing – AndrewNeedsHelp Jun 26 '20 at 09:20
  • 1
    @AndrewNeedsHelp I don't know how `BigInt` works in JavaScript in detail, but this article from V8 engine dev documents might help: https://v8.dev/features/bigint – Hao Wu Jun 26 '20 at 09:23
  • @HaoWu when I run the code above I get Cannot convert a BigInt value to a number – AndrewNeedsHelp Jun 26 '20 at 09:29
  • You'll want to convert it to a big int, rather than a number; it's questionable whether you need to do the conversion at all, since the result is the same either way. – Putnam Jun 26 '20 at 09:31
  • Basically `BitInt` is not using the typical floating-point calculation. While a number has too much precision that a 32bit memory can't store every detail of that number. That causes precision loss. BigInt probably uses dynamic memory alloc to store that number. It will try to keep the number as precise as possible. – Hao Wu Jun 26 '20 at 09:32
  • `BigInt` will keep the number precise (not "precise as possible"), by allocating as many bytes as necessary to represent it with standard integer representation (not floating point). – trincot Jun 26 '20 at 11:39
  • This solution does not work for large n: Try n = 10**9...... – trincot Jun 26 '20 at 11:50
1

Others suggested bruteforce solutions with BigInt yet its not suitable for this problem, on largest n = 10^9 the number n^n is simply too large to fit in memory.

Instead this problem can be solved with modular arithmetics:

  • Notice that getting some number modulo 10^k gives k last digits of that number

  • So now we need to find n^n mod 10^k which is a very well known problem

  • Instead of using Math.pow() we can implement our own pow that uses modular arithmetic

  • Something like that: (this algorithm is known as binary exponentiation if it's unclear whats happening you can look it up online)

function powMod(n, power, mod) {
  if( power == 0) return 1 % mod;
  if( power %2 == 1) return BigInt(n) * BigInt(powMod(n, power-1, mod)) % mod;
  return powMod(BigInt(n)*BigInt(n) % mod, power/2, mod);
}
  • Now the problem can be solved just by outputting powMod(n, n, 10 ** k)
Photon
  • 2,717
  • 1
  • 18
  • 22
0

By using BigInt, you could take a subset of digits for getting a wanted reuslt with number types.

This approach takes for every multiplication only the last (significant) digits for the next multiplication. The result works as long as a multiplication is possible with the wanted value.

function n2n(n, k) {
  const bigN = BigInt(n);

  let i = n,
      p = '1';

  while (i--) p = (BigInt(p) * bigN).toString().slice(-k);
  return p;
}

console.log(n2n(5, 25));
console.log(n2n(5, 3));
console.log(n2n(1, 1));
console.log(n2n(5, 4));
console.log(n2n(43, 7));
console.log(n2n(999, 9));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

You need to use BigInt, which can be done by calling BigInt(n).

Furthermore, you can use .substr( <negative value X> ) to get the last X characters from a string.

Here's how:

function n2n(n, k) {  
  let a = BigInt(n) ** BigInt(n);
  return a.toString().substr(-k);
}

console.log(n2n(5, 25));
console.log(n2n(5, 3));
console.log(n2n(1, 1));
console.log(n2n(5, 4));
console.log(n2n(43, 7));
console.log(n2n(999, 9));
console.log(n2n(999, 19));
console.log(n2n(999, 29));
console.log(n2n(999, 39));
console.log(n2n(999, 99999999));

Also -> I previously created an answer related to all this that could provide some more background: https://stackoverflow.com/a/53518106/1220550

Peter B
  • 22,460
  • 5
  • 32
  • 69
  • I get the same problem with the free code camp browser: "Cannot convert a BigInt value to a number, is it just a compatibility issue? – AndrewNeedsHelp Jun 26 '20 at 09:35
  • 1
    Sounds like it only knows **`**`** for plain numbers, and not for BigInt. In that case you have to write a loop or a helper function to calculate the power yourself. – Peter B Jun 26 '20 at 09:38
0

In the function, you typed Math.pow(n, n) instead of Math.pow(n, k).

Here's the old code:

function n2n(n, k) {
  
  let a = Math.pow(n, n);
  let b = Array.from(a.toString()).map(Number);
  return b.slice((b.length-k),).join('');
}

console.log(n2n(5, 25));
console.log(n2n(5, 3));
console.log(n2n(1, 1));
console.log(n2n(5,4));
console.log(n2n(43, 7));
console.log(n2n(999, 9));

And here's the new code:

function n2n(n, k) {
  
  let a = Math.pow(n, k);
  let b = Array.from(a.toString()).map(Number);
  return b.slice((b.length-k),).join('');
}

console.log(n2n(5, 25));
console.log(n2n(5, 3));
console.log(n2n(1, 1));
console.log(n2n(5,4));
console.log(n2n(43, 7));
console.log(n2n(999, 9));

Make sure you run the code to see the difference.

  • This is clearly not what the author asked for. The issue is that with large numbers `NaN` will appear, but for small number it's working properly. Your answer changes the output completely, also for small numbers. – JSON Derulo Apr 28 '23 at 14:11