Contents
(within 1% for )
Deep Breakdown
What it tells me (Situation Awareness): Stirling's approximation gives a closed-form estimate of that grows as in log-space, making it tractable for large where direct computation overflows. It is essential in information theory (entropy calculations), statistical mechanics, combinatorics at scale, and anywhere log-factorials appear in ML likelihood functions.
Flowchart
Real-world use
Where it is used
When is large (e.g., vocabulary size in NLP, number of documents, large combinatorial counts) and you need for information-theoretic or likelihood computations without overflow.
Problem it solves
for large overflows 64-bit floats ( for double precision). Stirling's allows working in log-space exactly where needed.
How it solves the problem
Replaces the exact factorial with a smooth, computable expression via , derived from the Laplace method applied to the Gamma function integral.
Example in a business
Computing the entropy of a language model vocabulary distribution: where terms involve for token counts . Stirling makes this tractable for vocabulary sizes of 50,000+.
The formula, piece by piece
- : a polynomial correction factor.
- : the dominant exponential term.
- : Euler's number.
- In log-space: .
The formula in words
" factorial is approximately the square root of , times divided by , all raised to the -th power." For : exact ; Stirling (error ).
Critical Concepts
- Error is : improves as grows.
- Never use for small (e.g., ): error exceeds 5%.
- In practice, use
math.lgamma(n+1)in Python orlgamma(n+1)in R — they use better approximations than raw Stirling and are exact at integer points. - Log form is always preferred over raw Stirling to avoid overflow.
Step-by-step calculation (tiny example)
Step 1: Choose n
Step 2: Compute each component
Step 3: Multiply
Step 4: Compare to exact . Error .
Code examples
Beginner example (Python)
import math
n = 10
stirling = math.sqrt(2 * math.pi * n) * (n / math.e) ** n
exact = math.factorial(n)
error_pct = abs(stirling - exact) / exact * 100
print(f"Stirling: {stirling:,.0f}")
print(f"Exact: {exact:,}")
print(f"Error: {error_pct:.2f}%")Beginner example (R)
n <- 10
stirling <- sqrt(2 * pi * n) * (n / exp(1))^n
exact <- factorial(n)
error_pct <- abs(stirling - exact) / exact * 100
cat(sprintf("Stirling: %s\n", format(round(stirling), big.mark=",")))
cat(sprintf("Exact: %s\n", format(exact, big.mark=",")))
cat(sprintf("Error: %.2f%%\n", error_pct))Professional example (Python)
import math
import numpy as np
def log_factorial_stirling(n: float) -> float:
"""Log-factorial via Stirling approximation. Uses the log form: log(n!) ≈ 0.5*log(2*pi*n) + n*log(n) - n. Valid for n > 10; for exact use, prefer math.lgamma(n+1). Args: n: Positive real number. Returns: Approximation of log(n!). """
return 0.5 * math.log(2 * math.pi * n) + n * math.log(n) - n
def log_factorial_exact(n: int) -> float:
"""Exact log(n!) via lgamma."""
return math.lgamma(n + 1) # Accuracy comparison across n
N_VALUES = [5, 10, 20, 50, 100, 500, 1000]
print(f"{'n':>6} {'Stirling log(n!)':>20} {'Exact log(n!)':>18} {'Rel Error %':>12}")
for n_val in N_VALUES:
stirling_log = log_factorial_stirling(n_val)
exact_log = log_factorial_exact(n_val)
rel_error = abs(stirling_log - exact_log) / abs(exact_log) * 100
print(f"{n_val:>6} {stirling_log:>20.6f} {exact_log:>18.6f} {rel_error:>12.6f}")Source: AIO p. 83