-2

I'm solving LeetCode #172:

Given an integer n, return the number of trailing zeroes in n!

Constraints:

  • 0 <= n <= 104

My code finds the answer of n! first and then counts the number of trailing zeroes. However, running the code throws a stack overflow exception, and I can't for the life of me figure out why.

This is the code:

class Solution {
    public int trailingZeroes(int n){ 
        int fact = findFactorial(n);   // 120
        int ans = 0;
        
        // how many zeroes does fact have? 
        String ansString = Integer.toString(fact);
    
        // edge - if string is only one character long
        if (ansString.length()==1) {
          return 0;  
        } 
        
        // loop from the end counting the continuous zeroes
        for (int i= ansString.length()-1 ; i > 0; i--){
            Character cha = ansString.charAt(i);
            
            if (cha.equals('0')) {
                ans++;
            }
            else {
                break;
            }
        }
        
        return ans;
    }
    
    public int findFactorial(int n){
        // base case
        if (n==1) return 1;
        
        // reduct towards base case
        else {
            int f = n * findFactorial(n-1);
            return f;
        }
    }

}
user229044
  • 232,980
  • 40
  • 330
  • 338
  • 1
    Did you try debugging? – shmosel Dec 30 '21 at 01:10
  • 4
    The range of n is 0 <= n <= 10,000. They don't want you using recursion to solve this. You'll also run into overflows, so you probably want to do something clever with math and what trailing zeros represent to solve it. – David Ehrmann Dec 30 '21 at 01:12
  • 3
    A signed integer can hold values up to `2,147,483,648`. `120!` is `6689502913449127057588118054090372586752746333138029810295671352301633557244962989366874165271984981308157637893214090552534408589408121859898481114389650005964960521256960000000000000000000000000000`. You can't use integers for this, regardless of whether you use recursion or a simple `for` loop. – user229044 Dec 30 '21 at 01:14
  • Why? Because the number of stack frames (depth of recursion) exceeds the size of the stack your allocated. You do understand that each recursive call stacks one more frame? – passer-by Dec 30 '21 at 01:25
  • 6
    HINT: You do not need to calculate the actual value of the factorial in order to determine the number of trailing zeroes... ask yourself which multiplication operation(s) cause the addition of another trailing zero. – Jim Garrison Dec 30 '21 at 01:27
  • @DavidEhrmann, @ meagar, @ Jim Garrison thank you, that was helpful! – SomeoneLearning17 Dec 30 '21 at 17:20

4 Answers4

2

The reason you get a stack overflow error, is because you use recursion when you calculate the factorial with findFactorial. Change it to use a loop instead of recursion, as shown here Find factorial of large numbers in Java. Thus your method findFactorial becomes:

BigInteger findFactorial(int n) {
    BigInteger fact = BigInteger.valueOf(1);
    for (int i = 2; i <= n; i++)
        fact = fact.multiply(BigInteger.valueOf(i));
    return fact;
}

Then in your method that calls findFactorial change the lines:

int fact = findFactorial(n);
String ansString = Integer.toString(fact);

to

BigInteger fact = findFactorial(n);
String ansString = BigInteger.toString(fact);
  • How long does this take when n = 10^4? – tgdavies Dec 30 '21 at 01:52
  • @tgdavies I do not think the runtime was part of the question. You can create a new question asking about runtime or optimization. I tried to stay as close to the OP's question as possible. –  Dec 30 '21 at 01:57
  • This seems horribly inefficient, when all we care about is the number of zeroes. This solution spends most of its time calculating digits that are never going to be used. Sure, it works, and will probably produce an answer this year. It just doesn't feel optimum. – Dawood ibn Kareem Dec 30 '21 at 01:59
  • This *is* a valid and correct answer to the question the OP asked, even though it's not going to actually solve the specific coding challenge problem. Walking OP through a solution that doesn't involve computing the actual factorial is out-of-scope for this question – user229044 Dec 30 '21 at 02:03
  • @KlavsPeder Fair enough, I I'd just mention to the OP that this probably won't solve their problem... – tgdavies Dec 30 '21 at 02:16
2

You said:

Given an integer n, return the number of trailing zeroes in n!

Constraints:

  • 0 <= n <= 104

First, your solution won't work because an int can't hold that large a number. You need to use BigInteger as shown below.

The following recursive form will compute 104! without much noticeable delay.

public static BigInteger factorial(int n) {
     if (n == 1 || n == 0) {
         return BigInteger.ONE;
     }
     return factorial(n-1).multiply(BigInteger.valueOf(n));
}
String fact = factorial(1000).toString();
System.out.println(fact.replaceAll("\\d+?(0*)$", "$1").length());

prints

249

But you don't need to compute the factorial to solve the actual problem. Consider the following.

The product of all the numbers from 1 to N must have divisors of 10 (i.e. 2 and 5). 5 will occur the least number of times so that is where you need to focus. The number of trailing zeros is equal to the number of times that 10 divides N. And since 5 may divide a given term more than once (e.g. 25, and 125) you need to update the divisor as well.

int n = 1000; // factorial candidate
int sum = 0;
int k;
for (int d = 5; (k = n/d) > 0; d*=5) {
       sum += k;
}
System.out.printf("%d! has %d trailing zeros", n, sum);

prints

1000! has 249 trailing zeros

And here is the recursive solution (although not as efficient).

public static int trailingZeros (int n) {
    if (n > 0) {
        return trailingZeros(n/5) + n/5;
    }
    return 0;
}
WJS
  • 36,363
  • 4
  • 24
  • 39
1

Here's a simple way using for loops,

Change the value of your_number to any number before running this code.

public static void main(String[] args) {
            String factorial = findFactorial(new BigInteger("your_number")).toString();
            char[] factorialArray = factorial.toCharArray();
            int numberOfZeros = 0;
    
            for (int i = factorialArray.length - 1; i >= 0; i--) {
                if(factorialArray[i] == '0') {
                    numberOfZeros++;
                }
                else {
                    break;
                }
            }
    
            System.out.println(numberOfZeros);
        }
    
        static BigInteger fact = new BigInteger("1");
    
        static BigInteger findFactorial(BigInteger integer) {
            for (int i = 1; i <= integer.intValueExact(); i++) {
                fact = fact.multiply(BigInteger.valueOf(i));
            }
    
            return fact;
        }
-5

A properly constructed recursive implementation with the default stack size for the JVM on my machine will work up to at least 9000! (seems to go to 9119!) before there is a stack overflow. So the 10000 limit was probably choose explicitly to trigger this issue so you would think of a non-recursive implementation.

Here I have implemented both a loop and recursion.

import java.math.BigInteger;

public class BigFactorial {
    public static void main(String[] args) {
        printResult(120, factorialLoop(120));
        printResult(120, factorialRecursive(120));
    }

    static BigInteger factorialLoop(int n) {
        BigInteger f = BigInteger.ONE;
        for (int i = 2; i <= n; i++) {
            f = f.multiply(BigInteger.valueOf(i));
        }
        return f;
    }
    
     static BigInteger factorialRecursive(int n) {
        if (n == 1)
            return BigInteger.ONE;
        return BigInteger.valueOf(n).multiply(factorialRecursive(n-1));
    }

    static void printResult(int n, BigInteger f) {
        System.out.println(n + "! = " + f);
        String fStr = f.toString();
        int len = fStr.length();
        for (int i = len - 1; i >= 0; i--) {
            if (fStr.charAt(i) != '0') {
                System.out.println("There are " + (len - 1 - i) + " trailing zeros.");
                break;
            }
        }

    }
}
swpalmer
  • 3,890
  • 2
  • 23
  • 31