Topics › Basics › Learn Basic Recursion
Factorial of a given number
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given a non-negative integer N, return its factorial N! = 1 × 2 × 3 × ... × N. By definition 0! = 1 (the empty product).
Some sheets phrase the row as “find all factorial numbers less than or equal to N”, where a factorial number is any value of the form k!: 1, 2, 6, 24, 120, .... We solve the main problem with three approaches and cover the variant at the end, since it falls out of the same loop.
Constraints: 0 ≤ N ≤ 20. The bound is not arbitrary: 20! is about 2.4 × 10^18, the largest factorial that fits in a 64-bit long; 21! overflows. Already 13! overflows a 32-bit int, so every method returns long.
Example 1
N = 5
answer = 120 // 1 * 2 * 3 * 4 * 5
Example 2
N = 0
answer = 1 // the empty product is 1 by convention
Example 3
N = 20
answer = 2432902008176640000 // the largest factorial that fits in a long
Approach 1: Loop
Intuition. Keep a running product and multiply in 2, 3, ..., N. Starting the loop at 2 (rather than 1) saves a pointless multiplication and makes N = 0 and N = 1 fall out naturally: the loop body never runs and the initial value 1 is returned.
Algorithm.
- Set
result = 1. - For
ifrom2toN, doresult *= i. - Return
result.
static long iterative(int n) {
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
Complexity. Time O(n): one multiplication per number. Space O(1).
Approach 2: Functional recursion
Intuition. The factorial has a recursive definition: n! = n × (n - 1)! with 0! = 1. Translate it literally. The call for n trusts that the call for n - 1 returns the correct smaller factorial and multiplies it by n after that call returns. This is functional recursion: the function’s return value is the answer, and the combination step happens on the way back up the stack.
The base case n <= 1 covers both 0! = 1 and 1! = 1, and also stops the recursion for negative input instead of running forever.
Algorithm.
- If
n <= 1, return1. - Otherwise return
n * recursive(n - 1).
static long recursive(int n) {
if (n <= 1) return 1; // base case: 0! = 1! = 1
return n * recursive(n - 1);
}
Dry run on N = 4:
| call | returns |
|---|---|
recursive(4) |
4 * recursive(3) = 4 * 6 = 24 |
recursive(3) |
3 * recursive(2) = 3 * 2 = 6 |
recursive(2) |
2 * recursive(1) = 2 * 1 = 2 |
recursive(1) |
1 (base case) |
Note that n is an int and recursive(n - 1) is a long, so the multiplication is performed in long. That is what keeps 20! from overflowing.
Complexity. Time O(n): n calls. Space O(n) for the call stack; every frame must stay alive until its callee returns because it still has a multiplication to do.
Approach 3: Parameterised (tail) recursion
Intuition. Instead of multiplying on the way back, carry the product so far down into the recursion as a parameter. Each call multiplies the current i into the accumulator and passes it on; the base case has nothing left to multiply and simply returns the accumulator. Nothing happens after the recursive call, so the answer is complete at the bottom and is handed back untouched. This is the same shape as a loop, with the loop variable and the accumulator turned into parameters.
Algorithm.
- Start with
multiplyDown(n, 1). - In
multiplyDown(i, product): ifi <= 1, returnproduct. - Otherwise return
multiplyDown(i - 1, product * i).
static long tailRecursive(int n) {
return multiplyDown(n, 1);
}
private static long multiplyDown(int i, long product) {
if (i <= 1) return product; // base case: everything has been multiplied in
return multiplyDown(i - 1, product * i);
}
Complexity. Time O(n). Space O(n) on the JVM, which does not eliminate tail calls; in a language that does, this version would run in O(1) space, which is the practical reason to know the shape.
Variant: all factorial numbers up to a limit
Intuition. If the sheet asks for every k! that does not exceed limit, generate factorials in increasing order with the same running product and stop at the first one that is too big. Because factorials grow so fast, there are never more than 20 of them within long range, so the loop is tiny.
The one subtlety is overflow: for a huge limit the running product could wrap around to a negative number, which would compare as “still small” and corrupt the output (and could even loop forever, because k! for k ≥ 66 has so many factors of two that the wrapped long becomes 0). Checking fact > Long.MAX_VALUE / i before the multiplication detects the overflow one step early.
static List<Long> factorialsUpTo(long limit) {
List<Long> out = new ArrayList<>();
long fact = 1;
for (int i = 2; fact <= limit; i++) {
out.add(fact);
if (fact > Long.MAX_VALUE / i) break; // the next factorial would overflow long
fact *= i;
}
return out;
}
For limit = 6 this returns [1, 2, 6]; for limit = 100 it returns [1, 2, 6, 24]; for limit = 0 it returns an empty list; for limit = Long.MAX_VALUE it returns exactly 1! through 20!.
Complexity. Time O(k) for k results, which is at most 20. Space O(k) for the output.
Edge cases and pitfalls
0! = 1. Every approach must return1forN = 0, not0. Initialising the product to1and using the base casen <= 1handles it.intoverflow.12! = 479,001,600fits inint,13! = 6,227,020,800does not. Make the return type and the accumulatorlong. Inn * recursive(n - 1), thelongoperand promotes the whole expression, butn * (n - 1) * ...written with allintoperands would silently wrap.longoverflow.20!fits,21!does not. For largerNyou need arbitrary precision (java.math.BigInteger) or the problem asks for the result modulo some prime.- Negative input. The base case
n <= 1returns1for negativeninstead of recursing forever. Whether that is the right answer is a matter of specification; the important thing is that the recursion terminates. - Recursion depth. Not a concern here because
N ≤ 20, but in general each recursive level costs one stack frame, and the loop version is the safe default for large inputs. - Variant termination. Without the overflow guard,
factorialsUpTo(Long.MAX_VALUE)would add garbage values and could spin forever. Always reason about what happens when the running product passes the type’s limit.
Related problems
- Sum of First N Numbers uses the same two recursion styles with addition, and is the gentlest introduction to them.
- Print N to 1 using Recursion is the output-only version of counting down with a parameter.
- Fibonacci Number is functional recursion with two recursive calls, where the naive version becomes exponential.
- Pow(x, n) is the next step in recursive arithmetic: halving the exponent instead of decrementing it.