Contents
Deep Breakdown
What it tells me (Situation Awareness): (read "n factorial") is the product of all positive integers from 1 up to . It counts the number of ways to arrange distinct items in a sequence. Factorials appear constantly in combinatorics (which underpins probability and statistics), in the formulas for combinations and permutations, in Taylor series expansions of , , and , and in the denominator of many probability mass functions (Poisson, Binomial). If you have seen (n-choose-k), its denominator is .
Flowchart
Real-world use
Where it is used
Counting arrangements and selections in probability, computing combination and permutation formulas, appearing in Taylor series (used in deriving logistic regression and probability distributions), and in the denominator of Poisson PMF ().
Problem it solves
How many ways can you arrange 5 different products on a shelf? The answer is . How many ways can a recommendation engine rank 10 items? . Factorial gives you the exact count of ordered arrangements, which is the foundation of probability calculations involving sequences.
How it solves the problem
Each position in a sequence reduces your remaining choices by one. For items: first position has options, second has , ..., last has 1. Multiplying these choices gives .
Example in a business
Computing the probability of a specific sequence of events in a Poisson process. The Poisson PMF is . The in the denominator prevents overcounting all the orderings of identical events. Without it, you would be treating "3 events in order ABC" as different from "3 events in order BAC" when in reality they are the same count.
The formula, piece by piece
Breaking the notation:
- : any non-negative integer.
- : the factorial operator. Postfix notation (written after the number).
- : multiplication.
- : "continuing the pattern." Full recursive definition:
Expanded (product notation): Key values to memorize:
- (by convention and by the empty product rule)
- (grows explosively)
The formula in words
" means: start at , multiply by every integer below it down to 1. It counts how many distinct orderings of unique objects exist." The recursive view: " equals times the factorial of ." This is because: if you have items, fix the first one ( choices) and then arrange the remaining in ways. Why is ? There is exactly one way to arrange zero objects: do nothing. The "empty arrangement" is a valid, unique arrangement. Also: it makes the combination formula work cleanly.
Critical Concepts
- Explosive growth: factorials grow faster than any polynomial or exponential. billion. Be careful of overflow when computing them directly for large .
- : not zero. This is a definition that ensures consistency in combination formulas and the empty product convention.
- Stirling's approximation: . Used to estimate factorials of large numbers without computing them exactly.
- Log factorial: for large , computing using the log-sum-of-logs is numerically safer than computing directly (avoids overflow). Many statistical distributions work in log-space for this reason.
- Combinations use factorial: .
The binomial coefficient counts how many ways to choose items from without regard to order.
Step-by-step calculation (tiny example)
Input: compute .
Step 1: Write out the product
Step 2: Multiply left to right
Step 3: Verify the interpretation There are exactly ways to arrange 5 distinct items (say, books on a shelf) in a row. List them mentally: with 5 choices for the first position, 4 for the second, etc.: .
Code examples
Beginner example (Python)
import math
# Python's math.factorial()
print(math.factorial(0)) # 1 (0! = 1)
print(math.factorial(1)) # 1
print(math.factorial(5)) # 120
print(math.factorial(10)) # 3628800
# Manual implementation to understand the recursion
def factorial(n: int) -> int:
"""n! = n * (n-1) * ... * 1, with 0! = 1."""
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
# Log factorial (numerically safe for large n)
log_factorial_10 = sum(math.log(k) for k in range(1, 11))
print(f"ln(10!) = {log_factorial_10:.4f}")
print(f"ln(10!) = {math.log(math.factorial(10)):.4f}") # SameBeginner example (R)
# R's factorial() function
factorial(0) # 1
factorial(1) # 1
factorial(5) # 120
factorial(10) # 3628800
# Manual recursive version
my_factorial <- function(n) {
if (n == 0 || n == 1) return(1)
n * my_factorial(n - 1)
}
my_factorial(5) # 120
# Log factorial (safer for large n)
lfactorial(10) # log factorial: log(10!) in RProfessional example (Python)
import math
import numpy as np
# Scenario: computing Poisson PMF from scratch to understand where factorial is used
# P(X = k) = e^(-lambda) * lambda^k / k!
def poisson_pmf(k: int, lam: float) -> float:
"""
Poisson probability mass function.
P(X=k) = e^(-lambda) * lambda^k / k!
Uses log-space arithmetic to avoid overflow for large k.
"""
log_prob = -lam + k * math.log(lam) - math.lgamma(k + 1)
# math.lgamma(k+1) = ln(k!) via the Gamma function: Gamma(k+1) = k!
return math.exp(log_prob)
# Lambda = 3 (average 3 events per interval)
lam = 3.0
print(f"Poisson PMF with lambda={lam}:")
for k in range(0, 10):
prob = poisson_pmf(k, lam)
bar = "#" * int(prob * 100)
print(f" P(X={k:2d}) = {prob:.4f} {bar}")
# Verify: probabilities sum to ~1
total = sum(poisson_pmf(k, lam) for k in range(0, 50))
print(f"\nSum of first 50 terms: {total:.6f}") # Should be ~1.0Professional example (R)
# Scenario: Poisson PMF from scratch; P(X=k) = e^(-lambda) * lambda^k / k!
# Uses log-space: lgamma(k+1) = ln(k!)
poisson_pmf <- function(k, lam) {
log_prob <- -lam + k * log(lam) - lgamma(k + 1)
exp(log_prob)
}
lam <- 3.0
cat("Poisson PMF with lambda=", lam, ":\n", sep = "")
for (k in 0:9) {
prob <- poisson_pmf(k, lam)
bar <- strrep("#", as.integer(prob * 100))
cat(sprintf(" P(X=%2d) = %.4f %s\n", k, prob, bar))
}
total <- sum(sapply(0:49, function(k) poisson_pmf(k, lam)))
cat(sprintf("\nSum of first 50 terms: %.6f\n", total))Source: 1300 p. 326