The Bohr–Mollerup theorem
The gamma function is defined for by Euler’s integral
It extends the factorial, , and satisfies . But infinitely many functions interpolate the factorial. The Bohr–Mollerup theorem Bohr & Mollerup, 1922, given the elegant treatment reproduced here by Artin Artin, 1964, singles out by one extra qualitative condition: logarithmic convexity.
Theorem (Bohr–Mollerup). The gamma function is the unique function that simultaneously satisfies
;
for all ;
is convex on .
Here “log-convex” means that is a convex function; equivalently for .
Lean: the three properties GammaLike and the theorem bohr_mollerup
/-- The three Bohr–Mollerup hypotheses on a function `f : ℝ → ℝ`, packaged as a predicate:
`f` is positive and log-convex on `(0, ∞)`, satisfies `f (x + 1) = x · f x`, and `f 1 = 1`.
The theorem below says `Real.Gamma` is the unique such function. -/
structure GammaLike (f : ℝ → ℝ) : Prop where
/-- `f` is positive on the positive reals. -/
pos : ∀ x, 0 < x → 0 < f x
/-- The Gamma functional equation `f (x + 1) = x · f x`. -/
functional_eq : ∀ x, 0 < x → f (x + 1) = x * f x
/-- `log ∘ f` is convex on the positive reals (i.e. `f` is log-convex). -/
log_convex : ConvexOn ℝ (Ioi 0) (Real.log ∘ f)
/-- Normalization `f 1 = 1`. -/
one : f 1 = 1/-- **The Bohr–Mollerup theorem.** `Real.Gamma` is `GammaLike`, and it is the *unique* `GammaLike`
function: every function that is positive and log-convex on `(0, ∞)`, satisfies
`f (x + 1) = x · f x`, and has `f 1 = 1`, coincides with `Real.Gamma` on `(0, ∞)`. -/
theorem bohr_mollerup :
GammaLike Real.Gamma ∧ ∀ f, GammaLike f → EqOn f Real.Gamma (Ioi 0) :=
⟨gammaLike_Gamma, fun _ hf => hf.eq_Gamma⟩The proof has two halves: uniqueness (any with properties 1–3 is forced to equal a specific limit, hence equals ) and existence ( itself has all three properties). The heart of the argument is uniqueness.
Step 1: the functional equation reproduces the factorials¶
Assume satisfies properties 1–3. Property 2 applied repeatedly, with , gives by induction
so interpolates the factorials just like . More generally, iterating exactly times yields the product formula
The consequence is decisive: (3) lets us recover for any from its values on the unit interval . So it suffices to pin down on .
Lean proof: GammaLike.f_nat (factorials) and GammaLike.f_add_nat (product formula)
/-- **The functional equation forces the factorials.** From `f (x + 1) = x · f x` and `f 1 = 1`,
induction gives `f (n + 1) = n !` for every natural number `n`; in particular `f` interpolates the
factorials, `f n = (n − 1)!`. -/
theorem GammaLike.f_nat (hf : GammaLike f) (n : ℕ) : f (n + 1) = n ! := by
induction n with
| zero => simpa using hf.one
| succ k ih =>
have hk : (0 : ℝ) < (k : ℝ) + 1 := by positivity
have hrec := hf.functional_eq ((k : ℝ) + 1) hk
rw [Nat.cast_succ, hrec, ih, Nat.factorial_succ]
push_cast
ring/-- **The functional equation as a product.** Iterating `f (x + 1) = x · f x` `n` times gives
`f (x + n) = f x · x (x + 1) ⋯ (x + n − 1)`, i.e. `f (x + n) = f x · ∏_{m < n} (x + m)`. Knowing `f`
on `(0, 1]` therefore determines it everywhere on `(0, ∞)`. -/
theorem GammaLike.f_add_nat (hf : GammaLike f) (hx : 0 < x) (n : ℕ) :
f (x + n) = f x * ∏ m ∈ Finset.range n, (x + m) := by
induction n with
| zero => simp
| succ k ih =>
have hxk : 0 < x + (k : ℝ) := by positivity
have hrec := hf.functional_eq (x + (k : ℝ)) hxk
have hcast : x + ((k : ℕ) + 1 : ℕ) = (x + (k : ℝ)) + 1 := by push_cast; ring
rw [hcast, hrec, ih, Finset.prod_range_succ]
ringStep 2: log-convexity sandwiches the values¶
Fix with and write , which is convex by property 3. For let
be the slope of the secant of over . Convexity is exactly the statement that is monotonically increasing in each argument. For an integer consider the four points . Monotonicity of the slopes gives
By (2), and , so the outer slopes collapse to logarithms of integers, and , while the middle slope is . Multiplying (5) by and adding turns it into a sandwich of between two lines in :
Lean proof: GammaLike.log_sandwich (the two-line sandwich)
/-- **The log-convexity sandwich.** For `0 < x ≤ 1` and `n ≥ 2`, convexity of `log ∘ f` forces the
secant slopes of `log ∘ f` to be monotone, which traps `log (f (n + x))` between the two lines in
`x` through the lattice values of `log ∘ f`:
`log (f n) + x·log (n − 1) ≤ log (f (n + x)) ≤ log (f n) + x·log n`. -/
theorem GammaLike.log_sandwich (hf : GammaLike f) (hn : 2 ≤ n) (hx : 0 < x) (hx' : x ≤ 1) :
Real.log (f n) + x * Real.log ((n : ℝ) - 1) ≤ Real.log (f (n + x)) ∧
Real.log (f (n + x)) ≤ Real.log (f n) + x * Real.log n := by
refine ⟨?_, ?_⟩
· simpa [Function.comp_apply] using
Real.BohrMollerup.f_add_nat_ge hf.log_convex hf.log_comp_feq hn hx
· simpa [Function.comp_apply] using
Real.BohrMollerup.f_add_nat_le hf.log_convex hf.log_comp_feq (by omega : n ≠ 0) hx hx'Step 3: the Euler–Gauss limit¶
Now feed the product formula (3) into the sandwich (6). Taking logarithms of (3),
and, exponentiating (6) after substitution and solving for , one obtains for every the two-sided bound
Because (8) holds for every , the left- and right-hand sides may use different indices. Sending and using (equivalently ), the two sides converge to a common value, squeezing to Gauss’s product limit
It is convenient to record this in logarithmic form. Setting
(so that is exactly the fraction in (9)), the squeeze says .
Lean proof: GammaLike.logGammaSeq_tendsto (the squeeze) and GammaLike.gauss_product (the Gauss product limit)
/-- **The Euler/Gauss limit formula for `f`.** Exponentiating and letting `n → ∞` in the sandwich of
step 2 squeezes the Gauss sequence
`logGammaSeq x n = x·log n + log n! − ∑_{m ≤ n} log (x + m)` (the logarithm of
`nˣ · n! / (x (x+1) ⋯ (x+n))`) to `log (f x)`. Because the limit does not depend on `f`, this is the
uniqueness engine of the theorem. -/
theorem GammaLike.logGammaSeq_tendsto (hf : GammaLike f) (hx : 0 < x) :
Tendsto (Real.BohrMollerup.logGammaSeq x) atTop (𝓝 (Real.log (f x))) := by
have h := Real.BohrMollerup.tendsto_logGammaSeq hf.log_convex hf.log_comp_feq hx
simpa only [Function.comp_apply, hf.one, Real.log_one, sub_zero] using h/-- **The explicit Gauss product.** Unwinding step 3, the value of any admissible `f` is Gauss's
product limit `f x = limₙ nˣ · n! / (x (x+1) ⋯ (x+n))`. -/
theorem GammaLike.gauss_product (hf : GammaLike f) (hx : 0 < x) :
Tendsto (fun n : ℕ => (n : ℝ) ^ x * (n ! : ℝ) / ∏ j ∈ Finset.range (n + 1), (x + j))
atTop (𝓝 (f x)) := by
have h := Real.GammaSeq_tendsto_Gamma x
rw [← hf.eq_Gamma hx] at h
exact hHere logGammaSeq x n is Mathlib’s name for , and the squeeze itself
(monotone secant slopes plus ) is provided by Mathlib’s
Real.BohrMollerup.tendsto_logGammaSeq, which the Lean proof above specializes to
our GammaLike hypotheses.
Step 4: uniqueness¶
The right-hand side of (9) makes no reference to : it is a fixed sequence built from and the integers. Hence its value is determined by alone. So if and both satisfy properties 1–3, then for every
and the two functions coincide. In particular, since (as we check in Step 5) satisfies properties 1–3, and it too obeys , the uniqueness of limits forces ; as is injective on and both values are positive, for all .
Lean proof: GammaLike.eq_Gamma (every admissible function equals Γ) and GammaLike.unique (any two agree)
/-- **Uniqueness (each admissible `f` equals `Γ`).** Both `f` and `Real.Gamma` make `logGammaSeq x`
converge — to `log (f x)` and to `log (Γ x)` respectively — so those logs are equal by uniqueness of
limits, and injectivity of `log` on the positive reals gives `f x = Γ x` for all `x > 0`. -/
theorem GammaLike.eq_Gamma (hf : GammaLike f) : EqOn f Real.Gamma (Ioi 0) := by
intro x hx
have hx0 : 0 < x := hx
have hlog : Real.log (f x) = Real.log (Real.Gamma x) :=
tendsto_nhds_unique (hf.logGammaSeq_tendsto hx0) (Real.BohrMollerup.tendsto_log_gamma hx0)
exact Real.log_injOn_pos (hf.pos x hx0) (Real.Gamma_pos_of_pos hx0) hlog/-- Any two functions with the three Bohr–Mollerup properties agree on `(0, ∞)`. -/
theorem GammaLike.unique (hf : GammaLike f) (hg : GammaLike g) : EqOn f g (Ioi 0) :=
fun _ hx => (hf.eq_Gamma hx).trans (hg.eq_Gamma hx).symmStep 5: existence — qualifies¶
Uniqueness would be vacuous without a function that actually meets all three conditions. The gamma function (1) does:
Normalization. .
Functional equation. Integration by parts gives .
Log-convexity. Applying Hölder’s inequality to Euler’s integral shows is convex on .
Together with positivity of on , this establishes existence.
Lean proof: gammaLike_Gamma (Γ has all three properties)
/-- **Existence.** `Real.Gamma` satisfies the three properties: it is positive
(`Real.Gamma_pos_of_pos`), obeys `Γ (x + 1) = x · Γ x` (`Real.Gamma_add_one`), is log-convex
(`Real.convexOn_log_Gamma`), and is normalized `Γ 1 = 1` (`Real.Gamma_one`). -/
theorem gammaLike_Gamma : GammaLike Real.Gamma where
pos := fun _ hx => Real.Gamma_pos_of_pos hx
functional_eq := fun _ hx => Real.Gamma_add_one hx.ne'
log_convex := Real.convexOn_log_Gamma
one := Real.Gamma_oneConclusion¶
Steps 1–4 show that at most one positive function on can satisfy
properties 1–3, and Step 5 shows is such a function. Therefore is
the unique function with these properties — the Bohr–Mollerup theorem. The Lean
statement bohr_mollerup in the first dropdown packages both halves: is
GammaLike, and every GammaLike function equals on .
A pleasant by-product of the proof is the explicit Gauss product (9) for the gamma function, which fell out of the uniqueness argument without any extra work.
References¶
H. Bohr and J. Mollerup, Lærebog i matematisk Analyse, vol. III (1922) Bohr & Mollerup, 1922 — the original source.
E. Artin, The Gamma Function Artin, 1964 — the standard modern treatment, whose argument this note follows.
Bohr–Mollerup theorem on Wikipedia Wikipedia, 2025.
- Bohr, H., & Mollerup, J. (1922). Lærebog i matematisk Analyse: Vol. III. Jul. Gjellerup.
- Artin, E. (1964). The Gamma Function (M. Butler, Trans.). Holt, Rinehart.
- Wikipedia. (2025). Bohr–Mollerup theorem. https://en.wikipedia.org/wiki/Bohr%5C%25E2%5C%2580%5C%2593Mollerup_theorem