Problem overview
The factorial of a non-negative integer n, written n!, is the product of all whole numbers from 1 to n. The method factorial(n) returns that product. For n = 5 the result is 1 * 2 * 3 * 4 * 5 = 120.
By definition 0! is 1, because a product with no factors is 1. The lesson asks for O(n) time and O(1) space.
How to calculate a factorial in JavaScript
The idea is a running product. Start with 1, then multiply by 1, by 2, and so on up to n. For n = 4:
fact = 1
fact = 1 * 1 = 1 (1!)
fact = 1 * 2 = 2 (2!)
fact = 2 * 3 = 6 (3!)
fact = 6 * 4 = 24 (4!)
A for loop does this for any n. The running product is a variable that the loop updates, so it has to be declared with let, not const.
Step by step
- Declare
factand set it to 1. 1 is the starting value for a product, and it makesn = 0work: the loop never runs, and 1 is returned. - Write a
forloop withifrom 1 whilei <= n. The<=includesnitself. Every factor from 1 throughnis visited once. - Multiply inside the loop.
fact *= iis short forfact = fact * i. - Return
factafter the loop.
The solution in JavaScript
class Solution {
factorial(n) {
let fact = 1;
for (let i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
}
The loop runs exactly n times, so the time is O(n). It only stores fact and i, so the extra space is O(1).
Factorial grows fast
JavaScript numbers are 64-bit floating-point values. They store whole numbers exactly only up to Number.MAX_SAFE_INTEGER, which is 9007199254740991. Factorials pass that limit quickly:
18!is 6402373705728000, still a safe integer.19!is 121645100408832000, which is larger than the safe limit, so results from here on can't be trusted to be exact.22!already prints in exponent form:1.1240007277776077e+21.170!is about7.257415615307994e+306, the largest factorial a number can hold.171!comes out asInfinity.
For exact large results, use BigInt. Its literals end in n, and it can't be mixed with ordinary numbers in arithmetic:
function factorialBig(n) {
let fact = 1n;
for (let i = 1n; i <= BigInt(n); i++) {
fact *= i;
}
return fact;
}
console.log(factorialBig(20));
console.log(factorialBig(25));
2432902008176640000n
15511210043330985984000000n
The ordinary-number loop prints 1.5511210043330986e+25 for 25!, which is only an approximation of the same value.
The recursive version
Factorial also has a recursive definition: n! = n * (n - 1)!, with 0! = 1 and 1! = 1.
const factorial = n => (n <= 1 ? 1 : n * factorial(n - 1));
console.log(factorial(5));
120
It gives the same results, but it also takes O(n) space for the call stack, and a very large n such as 1e6 throws RangeError: Maximum call stack size exceeded. The loop avoids both problems, which is why the lesson uses it.
Edge cases and common mistakes
n = 0. Returns 1, becausefactstarts at 1 and the loop doesn't run.- Starting
factat 0. Every product would be 0. - Starting the loop at 0. Multiplying by
i = 0turns the product into 0 for everyn. - Using
i < n. It leaves out the last factor:factorial(5)would give 24. - Negative numbers. Factorial isn't defined for them. This code returns 1 for
-3, because the loop condition is false at once. Check for it first if your input can be negative. - Time complexity. It is
O(n)multiplications, counting each as one step. The numbers themselves get large, so withBigInteach multiplication takes longer as the result grows.
Testing
factorial(0)gives 1.factorial(1)gives 1.factorial(5)gives 120.factorial(10)gives 3628800.
