Counting by remembering
A drummer in a Carnatic ensemble, or a poet composing in Sanskrit or Prakrit, builds rhythms from short beats (one unit of time) and long beats (two units). How many different rhythms fill exactly units? For there are . Indian prosodists noticed — more than five centuries before Leonardo of Pisa wrote about rabbits — that each count is the sum of the previous two: a rhythm of length either ends with a short beat (after a rhythm of length ) or with a long beat (after a rhythm of length ).
That one observation is a recurrence: it defines a sequence by its own past. Recurrences are everywhere. The number of moves needed to solve the Tower of Hanoi, the running time of mergesort, the balance of a loan in Nairobi or Lima with monthly interest and repayments, the population of a species with age classes, the number of ways to bracket a long product — each is described most naturally by saying how the next value depends on the earlier ones.
This lesson answers three questions about such sequences:
- Exact solution. When can we replace "the next term is …" by a formula for the -th term? For linear recurrences with constant coefficients the answer is always, and the method is the characteristic polynomial.
- Packaging. A generating function turns a whole sequence into a single algebraic object, so that recurrences become equations and counting problems become multiplication.
- Growth. When an exact formula is out of reach — or too detailed to be useful — how fast does the sequence grow? This is the language of , and that every analysis of an algorithm uses.
You will need combinatorics (binomial coefficients, counting in two ways) and sequences and series (summation, induction, geometric series).
Unroll, guess, verify — and the sequence as one object
Take the rhythms again, and write for the number of rhythms of length . We have , and . These are the Fibonacci numbers shifted by one: with , , we have . The same count answers "how many ways can a strip be tiled by squares and dominoes?":
How would we ever find a formula for ? Two complementary ideas run through the lesson.
Guess an exponential. The numbers grow by a factor that settles near . So try . Substituting into and dividing by gives — a quadratic equation, with two roots and . The recurrence is linear, so any combination is a solution too, and two free constants are exactly enough to match the two starting values.
Package the sequence. Write all the terms into one expression . The recurrence says that has almost every coefficient zero, so : the infinite sequence is captured by a fraction of two tiny polynomials. Algebra on that fraction — partial fractions — gives back the same formula.
Definitions
Definition— recurrence relation
A recurrence relation for a sequence expresses in terms of earlier terms. It is linear of order with constant coefficients if there are constants (real or complex) with such that
and homogeneous because there is no extra term depending only on . Adding a term on the right gives an inhomogeneous recurrence. The values are the initial values.
Definition— characteristic polynomial
The characteristic polynomial of is
Its roots (in ) are the characteristic roots. For the Fibonacci recurrence, .
Definition— ordinary generating function
The ordinary generating function of a sequence is the formal power series
We write for "the coefficient of in ".
Definition— formal power series
For a field (we use , or ), the ring of formal power series consists of all sequences of elements of , written , with
Each coefficient of a sum or product is a finite computation, so no question of convergence ever arises: is a symbol, not a number. Polynomials are the series with finitely many non-zero coefficients.
Definition— asymptotic notation
Let be real-valued functions on (or on ).
- if there are constants and with for all — " grows at most as fast as , up to a constant".
- if — at least as fast, up to a constant (Knuth's convention).
- if both and .
- if ; and if (for eventually non-zero).
The "" in is a one-way abbreviation for " belongs to the class ": is true, but "" means nothing.
Worked examples and non-examples
Example— Distinct real roots
Solve with , . The characteristic polynomial is , so . The initial values give and , so , and
Check: . ✓
Example— A repeated root
Solve with , . Now has the single root , and has only one free constant — not enough. The extra solution is (proved below), so . From and we get : , giving
Example— Complex roots give oscillation
The recurrence has with roots . The general solution can be rewritten with real constants as , since . With , the solution is : complex characteristic roots of modulus produce oscillation (here periodic), just as produces .
Example— The Tower of Hanoi: an inhomogeneous recurrence
To move discs, move the top to the spare peg, move the largest disc, then move the back on top: , . Adding to both sides gives , so and . (One can also show that no solution uses fewer moves: the largest disc must move at least once, and before its first move all other discs sit on one peg.)
Example— Non-examples
- , the logistic map, is a recurrence but not linear; for it is chaotic, and there is no characteristic polynomial to help.
- (so ) is linear but its coefficient depends on ; the characteristic method does not apply.
- refers back a variable distance; divide-and-conquer recurrences like this need other tools (see the section on growth).
Linear recurrences: the characteristic-root theorem
We work with order , , where every idea is visible; the general statement follows the proofs.
Theorem— Lemma 1 (linearity)
If and satisfy , then so does for any constants . So the solutions form a vector space.
Proof
.
∎
Theorem— Lemma 2 (initial values determine the solution)
If two solutions and of the same recurrence satisfy and , then for all .
Proof
We prove by induction on the statement : " and ". is the hypothesis. If holds, then , and
so holds. Hence for all . (The induction must carry two consecutive terms because the recurrence looks back two steps.)
∎
Together, the lemmas say: the map sending a solution to its initial values is a linear bijection onto . The solution space has dimension exactly — and for a recurrence of order , dimension , by the same argument.
Theorem— Lemma 3 (geometric solutions)
is a solution if and only if , i.e. is a root of (for ).
Proof
. If this is for every . Conversely, if it is for , then .
∎
Theorem— Theorem 1 (distinct roots)
If has two distinct roots (possibly complex), then the solutions of are exactly the sequences , where
Proof
By Lemmas 1 and 3, is a solution for every . With the given we have and . By Lemma 2, for all .
∎
Theorem— Theorem 2 (a repeated root)
If with , i.e. and , then the solutions are exactly the sequences .
Proof
First, is a solution: . So every is a solution by Lemma 1. Its initial values are and ; since , any prescribed are matched by , . Lemma 2 finishes the proof.
∎
Theorem— Corollary (Binet's formula)
With , , , and , ,
Moreover is the integer nearest to .
Proof
The roots of are , with . Theorem 1 with , gives and . For the second claim, , so .
∎
The formula is remarkable: an integer sequence, expressed through irrational numbers whose irrational parts cancel exactly.
The general theorem. For order , factor over with distinct roots . Then the solutions are exactly
Lemmas 1–3 generalise word for word; the remaining work is to check that () are solutions and that these sequences are linearly independent. The generating-function proof in the next section does both at once.
Inhomogeneous recurrences. If is one solution of , then every solution is with a solution of the homogeneous recurrence — the difference of two solutions solves the homogeneous one. A particular solution can often be guessed in the shape of : for try a constant, ; for try , giving , . (When the guess shape is itself a homogeneous solution, multiply it by , exactly as for differential equations.)
Generating functions
Theorem— Proposition (inverses in K[[x]])
A formal power series has a multiplicative inverse in if and only if .
Proof
If then comparing constant terms gives , so . Conversely, if we solve coefficient by coefficient: , and for the equation determines from .
∎
For example , so as an identity of formal power series — true whatever "" is, because it is a statement about coefficients. More generally, for a constant , , and differentiating formally (or expanding),
The coefficient is a polynomial in of degree , times .
Theorem— Theorem 3 (rational generating functions)
Let . A sequence satisfies for all if and only if its generating function is for a polynomial of degree less than .
Proof
The coefficient of in is . So the recurrence holds for all if and only if has no terms of degree , i.e. is a polynomial of degree less than . Since has constant term , it is invertible, and .
∎
Note that : the characteristic polynomial written backwards. So if then , and partial fractions write as a sum of terms with . By the expansion above, the coefficient of in each term is a polynomial in of degree less than times — which proves the general characteristic-root theorem. Every solution has this form; conversely every such sequence has a generating function , hence solves the recurrence.
Example— Fibonacci, again
With and , : , so
using , and . Reading off the coefficient of gives Binet's formula a second time.
Counting with products. Generating functions turn combinatorial constructions into algebra. If counts objects of one kind by size and another, then the coefficient of in is : the number of pairs of total size . A choice repeated any number of times corresponds to (when ).
Example— Paying with coins
In how many ways can a shopkeeper in Chennai make rupees from coins of , and rupees? Choosing how many coins of each kind is choosing a pair, then a triple, so the answer is the coefficient of in
Group by the number of five-rupee coins: with none, rupees from s and s ( ways: zero to five s); with one, rupees ( ways); with two, way. Total .
Example— Compositions
A composition of into parts and is a sequence of s and s with sum — a rhythm, in the language of the motivation. One part has generating function , so sequences of parts have . The rhythm count falls out without any recurrence.
Catalan numbers
A Dyck path of semilength is a sequence of up-steps and down-steps , starting at height and never going below height . Reading as "(" and as ")" identifies Dyck paths with balanced bracket sequences of pairs. Let be their number: , (()), ((()), ()()), .
Theorem— Proposition (Segner's recurrence)
for .
Proof
Every non-empty balanced sequence starts with "(", and that bracket is closed at a unique matching ")": the sequence is with and balanced. If has pairs then has , and the decomposition is a bijection, so .
∎
Theorem— Theorem 4 (the Catalan formula)
Proof
(The reflection argument.) There are paths from to with up- and down-steps. Call such a path bad if it touches height . For a bad path, reflect the part before its first visit to height in the line . The result is a path from to . Conversely, every path from to must cross height ; reflecting its part before the first visit gives back a bad path. So bad paths are in bijection with paths from height to height in steps, which have up-steps and down-steps: there are of them. Hence
since .
∎
A second proof, by generating functions. Let . Segner's recurrence says exactly that . Multiplying by and completing the square, . In the series has exactly two square roots, , where is the unique square root with constant term (its coefficients are found one at a time, as for inverses). Since has constant term , . The binomial series (it has constant term , and its square is by Vandermonde's identity , a polynomial identity in and ) then gives
(The identity for is a direct computation with .) Every step is algebra in ; that the series also converges for is a bonus, not a requirement.
The Catalan numbers count well over two hundred families of objects (Stanley's monograph lists 214), among them triangulations of a convex -gon, binary trees with internal nodes, ways to bracket a product of factors, and non-crossing handshakes among people seated at a round table.
How fast does it grow?
Exact formulas are not always available, and not always wanted: an engineer choosing between two algorithms needs to know that one takes about steps and the other about , not the exact counts. Asymptotic notation says this precisely.
Example— Using the definitions
- : for , . The constants and and the threshold are the witnesses.
- , but : is unbounded.
- , but : constant factors are hidden, but a change of base is not.
- , since . So the base of a logarithm inside does not matter.
- and in fact , by Binet's formula.
Theorem— Proposition (the scale of growth)
For every , and : , and .
Proof
For : the ratio satisfies , so from some point on with , and geometrically. For : , and every factor with is at most . For : put ; then , which tends to as (for instance by L'Hôpital's rule).
∎
Precise asymptotics often need analysis. Stirling's formula gives
which the second experiment below checks numerically.
Divide-and-conquer recurrences. Mergesort sorts items by sorting two halves and merging them with at most comparisons. Its cost satisfies (up to rounding and lower-order terms). These recurrences jump back by a factor, so the characteristic method does not apply directly — but on powers of two they become linear.
Theorem— Proposition (unrolling)
If and for , then , i.e. for a power of two.
Proof
Put . Dividing by gives , with . So .
∎
Picture the recursion as a tree: level has subproblems of size , costing in total, and there are levels. The general pattern is the master theorem (Bentley, Haken and Saxe, 1980), stated here without proof (see Cormen et al., §4.5): for with , , put ; then
- if for some , then — the leaves dominate;
- if , then — every level costs the same (mergesort: );
- if for some and for some and all large , then — the root dominates.
Karatsuba's multiplication, , lands in the first case: , beating the schoolbook . Recurrences of this kind are the bread and butter of algorithm design.
Recurrences in Lean and Mathlib
The Lean module follows the lesson's proofs. First, what it means to solve the recurrence , and Lemmas 1–3 — notice that Lemma 2's induction carries the pair of statements a n = b n ∧ a (n + 1) = b (n + 1), exactly as in the human proof:
/-- `IsSol p q a` says that the real sequence `a` satisfies the second-order linear recurrence
with constant coefficients `aₙ₊₂ = p · aₙ₊₁ + q · aₙ` for every `n`. Its characteristic
polynomial is `x² − p x − q`. -/
def IsSol (p q : ℝ) (a : ℕ → ℝ) : Prop :=
∀ n, a (n + 2) = p * a (n + 1) + q * a n/-- **Guess an exponential.** If `r` is a root of the characteristic polynomial, `r² = p r + q`,
then `aₙ = rⁿ` solves the recurrence: `rⁿ⁺² = rⁿ · r² = rⁿ (p r + q) = p rⁿ⁺¹ + q rⁿ`. -/
theorem isSol_geom {p q r : ℝ} (hr : r ^ 2 = p * r + q) : IsSol p q (fun n => r ^ n) := by
intro n
calc r ^ (n + 2) = r ^ n * r ^ 2 := by ring
_ = r ^ n * (p * r + q) := by rw [hr]
_ = p * r ^ (n + 1) + q * r ^ n := by ring/-- **Linearity.** If `a` and `b` solve the recurrence, so does every combination `A a + B b`.
So the solutions form a vector space. -/
theorem isSol_add_smul {p q : ℝ} {a b : ℕ → ℝ} (ha : IsSol p q a) (hb : IsSol p q b)
(A B : ℝ) : IsSol p q (fun n => A * a n + B * b n) := by
intro n
simp only
rw [ha n, hb n]
ring/-- **Two initial values determine a solution.** If `a` and `b` solve the same recurrence and
agree at `0` and `1`, they agree everywhere. The induction carries *two* consecutive values at
once: from `aₙ = bₙ` and `aₙ₊₁ = bₙ₊₁` the recurrence gives `aₙ₊₂ = bₙ₊₂`. -/
theorem isSol_unique {p q : ℝ} {a b : ℕ → ℝ} (ha : IsSol p q a) (hb : IsSol p q b)
(h0 : a 0 = b 0) (h1 : a 1 = b 1) : ∀ n, a n = b n := by
have key : ∀ n, a n = b n ∧ a (n + 1) = b (n + 1) := by
intro n
induction n with
| zero => exact ⟨h0, h1⟩
| succ k ih =>
refine ⟨ih.2, ?_⟩
rw [ha k, hb k, ih.1, ih.2]
exact fun n => (key n).1Theorem 1 is assembled from the lemmas, and Theorem 2's extra solution is a one-line computation:
/-- **The characteristic-root theorem (distinct roots).** If `r ≠ s` are roots of
`x² − p x − q`, every solution has the form `aₙ = A rⁿ + B sⁿ`, with `A` and `B` fixed by the
initial values: `A = (a₁ − s a₀)/(r − s)` and `B = (r a₀ − a₁)/(r − s)`.
Proof: the right-hand side solves the recurrence (`isSol_geom`, `isSol_add_smul`) and has the
right values at `0` and `1`, so it equals `a` by `isSol_unique`. -/
theorem distinct_roots {p q r s : ℝ} (hrs : r ≠ s) (hr : r ^ 2 = p * r + q)
(hs : s ^ 2 = p * s + q) {a : ℕ → ℝ} (ha : IsSol p q a) (n : ℕ) :
a n = (a 1 - s * a 0) / (r - s) * r ^ n + (r * a 0 - a 1) / (r - s) * s ^ n := by
have hne : r - s ≠ 0 := sub_ne_zero.mpr hrs
refine isSol_unique ha (isSol_add_smul (isSol_geom hr) (isSol_geom hs) _ _) ?_ ?_ n
· field_simp
ring
· field_simp
ring/-- **A repeated root.** If the characteristic polynomial is `(x − r)² = x² − 2r x + r²`, then
besides `rⁿ` the sequence `n rⁿ` is a solution of `aₙ₊₂ = 2r aₙ₊₁ − r² aₙ`. -/
theorem isSol_repeated (r : ℝ) : IsSol (2 * r) (-r ^ 2) (fun n => (n : ℝ) * r ^ n) := by
intro n
push_cast
ring/-- `aₙ = 3ⁿ` solves `aₙ₊₂ = 5aₙ₊₁ − 6aₙ`, because `3` is a root of `x² − 5x + 6 = (x − 2)(x − 3)`:
`3² = 5 · 3 − 6`. An instance of `isSol_geom`. -/
theorem three_pow_isSol : IsSol 5 (-6) (fun n => (3 : ℝ) ^ n) :=
isSol_geom (by norm_num)Binet's formula then follows from Theorem 1 exactly as in the corollary. Mathlib already has it, as Real.coe_fib_eq, and also a general theory of linear recurrences of any order (LinearRecurrence), including the dimension of the solution space and "guess an exponential":
/-- The Fibonacci numbers (Mathlib's `Nat.fib`, with `F₀ = 0`, `F₁ = 1`) solve
`Fₙ₊₂ = Fₙ₊₁ + Fₙ`, i.e. `IsSol 1 1`. -/
theorem fib_isSol : IsSol 1 1 (fun n => (Nat.fib n : ℝ)) := by
intro n
simp only [one_mul]
rw [Nat.fib_add_two, Nat.cast_add, add_comm]/-- **Binet's formula**, `Fₙ = (φⁿ − ψⁿ)/√5` with `φ = (1 + √5)/2` and `ψ = (1 − √5)/2`, proved
as in the lesson: `φ` and `ψ` are the distinct roots of `x² − x − 1`, so `distinct_roots`
applies with `F₀ = 0` and `F₁ = 1`, and `φ − ψ = √5`. -/
theorem binet (n : ℕ) : (Nat.fib n : ℝ) = (goldenRatio ^ n - goldenConj ^ n) / √5 := by
have hφ : goldenRatio ^ 2 = 1 * goldenRatio + 1 := by rw [goldenRatio_sq]; ring
have hψ : goldenConj ^ 2 = 1 * goldenConj + 1 := by rw [goldenConj_sq]; ring
have hne : goldenRatio ≠ goldenConj := by
intro h
have : goldenRatio - goldenConj = 0 := by rw [h, sub_self]
rw [goldenRatio_sub_goldenConj] at this
exact absurd this (by positivity)
rw [distinct_roots hne hφ hψ fib_isSol n, goldenRatio_sub_goldenConj]
simp only [Nat.fib_zero, Nat.fib_one, Nat.cast_zero, Nat.cast_one, mul_zero, sub_zero,
zero_sub]
ring/-- Binet's formula as it appears in Mathlib: `Real.coe_fib_eq`. Mathlib proves it with its
general `LinearRecurrence` theory, the same argument as `binet` above. -/
theorem binet_library (n : ℕ) : (Nat.fib n : ℝ) = (goldenRatio ^ n - goldenConj ^ n) / √5 :=
Real.coe_fib_eq n/-- Mathlib's version of "guess an exponential", for a linear recurrence `E` of any order over
a commutative ring without zero divisors: `qⁿ` is a solution exactly when `q` is a root of the
characteristic polynomial. This applies `LinearRecurrence.geom_sol_iff_root_charPoly`. -/
theorem geom_sol_iff_root (E : LinearRecurrence ℝ) (q : ℝ) :
E.IsSolution (fun n => q ^ n) ↔ E.charPoly.IsRoot q :=
E.geom_sol_iff_root_charPoly qTwo recurrences from algorithms. The divide-and-conquer recurrence is defined by well-founded recursion — Lean demands a proof that before it accepts the definition, a small reminder that a recurrence only defines something if it terminates:
/-- The number of moves the recursive solution of the Tower of Hanoi makes with `n` discs:
move `n − 1` discs aside, move the largest disc, move the `n − 1` discs back on top. -/
def hanoi : ℕ → ℕ
| 0 => 0
| n + 1 => 2 * hanoi n + 1/-- The Hanoi recurrence `Hₙ₊₁ = 2Hₙ + 1`, `H₀ = 0` has solution `Hₙ = 2ⁿ − 1`, stated without
subtraction as `Hₙ + 1 = 2ⁿ`: adding `1` turns the recurrence into `Hₙ₊₁ + 1 = 2 (Hₙ + 1)`. -/
theorem hanoi_eq (n : ℕ) : hanoi n + 1 = 2 ^ n := by
induction n with
| zero => rfl
| succ k ih =>
calc hanoi (k + 1) + 1 = 2 * (hanoi k + 1) := by simp only [hanoi]; ring
_ = 2 ^ (k + 1) := by rw [ih]; ring/-- The cost recurrence of a divide-and-conquer algorithm such as mergesort, with the halves
rounded down: `T(n) = 2 T(⌊n/2⌋) + n` for `n ≥ 2`, and `T(0) = T(1) = 0`. Lean accepts the
definition because `⌊n/2⌋ < n` when `n ≥ 2` (checked by `omega`). -/
def splitCost (n : ℕ) : ℕ :=
if n ≤ 1 then 0 else 2 * splitCost (n / 2) + n
decreasing_by omega/-- **Unrolling a divide-and-conquer recurrence.** On powers of two, `T(2ᵏ) = k · 2ᵏ`: each of
the `k` levels of the recursion costs `2ᵏ` in total. So `T(n) = n log₂ n` when `n = 2ᵏ`. -/
theorem splitCost_pow_two (k : ℕ) : splitCost (2 ^ k) = k * 2 ^ k := by
induction k with
| zero => simp [splitCost]
| succ k ih =>
have hgt : ¬ 2 ^ (k + 1) ≤ 1 := by
have : 2 ≤ 2 ^ (k + 1) := by
calc 2 = 2 ^ 1 := rfl
_ ≤ 2 ^ (k + 1) := Nat.pow_le_pow_right (by norm_num) (by omega)
omega
have hhalf : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; simp
rw [splitCost]
simp only [hgt, ite_false]
rw [hhalf, ih]
ringFormal power series. Mathlib's PowerSeries ℚ, written ℚ⟦X⟧, is literally the ring of the definitions section: a power series is a function from exponents to coefficients, and PowerSeries.coeff n reads off a coefficient. The Fibonacci generating function is verified coefficient by coefficient — and the factorial series, which converges nowhere except at , is nevertheless invertible:
/-- **The geometric series as an identity of formal power series**: in `ℚ⟦X⟧`,
`(1 + X + X² + ⋯)(1 − X) = 1`. No convergence is involved: the coefficients of the product are
finite sums. This applies Mathlib's `PowerSeries.mk_one_mul_one_sub_eq_one`. -/
theorem geometric_series : (mk 1 : ℚ⟦X⟧) * (1 - X) = 1 :=
mk_one_mul_one_sub_eq_one ℚ/-- **The generating function of the Fibonacci numbers.** With `F(X) = Σ Fₙ Xⁿ` in `ℚ⟦X⟧`,
`(1 − X − X²) F(X) = X`, i.e. `F(X) = X / (1 − X − X²)`. Coefficient by coefficient: the
coefficient of `Xⁿ⁺²` is `Fₙ₊₂ − Fₙ₊₁ − Fₙ = 0`, and those of `1` and `X` are `F₀ = 0` and
`F₁ − F₀ = 1`. -/
theorem fib_gf :
(1 - X - X ^ 2 : ℚ⟦X⟧) * mk (fun n => (Nat.fib n : ℚ)) = X := by
ext n
rw [sub_mul, sub_mul, one_mul, map_sub, map_sub, coeff_mk]
rcases n with _ | _ | n
· simp
· simp [coeff_X, pow_two, mul_assoc]
· rw [pow_two, mul_assoc, coeff_succ_X_mul, coeff_succ_X_mul, coeff_succ_X_mul, coeff_mk,
coeff_mk, coeff_X, Nat.fib_add_two]
simp only [Nat.cast_add, show n + 1 + 1 ≠ 1 by omega, ite_false]
ring/-- **A formal power series need not converge to be useful.** The series `Σ n! Xⁿ` converges
only at `X = 0`, yet it is invertible in `ℚ⟦X⟧`, because its constant term `0! = 1` is
invertible (Mathlib's criterion `PowerSeries.isUnit_iff_constantCoeff`). -/
theorem factorial_series_isUnit : IsUnit (mk (fun n => (Nat.factorial n : ℚ))) := by
rw [isUnit_iff_constantCoeff, ← coeff_zero_eq_constantCoeff_apply, coeff_mk]
simpCatalan numbers. Mathlib defines catalan by Segner's recurrence and proves the closed form:
/-- **The Catalan formula** `Cₙ = C(2n, n)/(n + 1)`. Mathlib *defines* `catalan` by Segner's
recurrence `Cₙ₊₁ = Σᵢ Cᵢ Cₙ₋ᵢ` (`catalan_succ`) and proves this closed form in
`catalan_eq_centralBinom_div`; `Nat.centralBinom n` is `C(2n, n)`, and the division is exact. -/
theorem catalan_formula (n : ℕ) : catalan n = n.centralBinom / (n + 1) :=
catalan_eq_centralBinom_div n/-- A computation: `C₅ = 42` (the number of balanced strings of five pairs of brackets, and of
triangulations of a convex heptagon), evaluated from the closed form `C(10, 5)/6 = 252/6`. -/
theorem catalan_five : catalan 5 = 42 := by
rw [catalan_formula]
decideAsymptotics. Mathlib's f =O[atTop] g is the definition of the lesson, phrased with filters: "eventually, ". The first proof supplies the witnesses and ; the second uses , itself proved by a two-step induction:
/-- `3n² + 5n = O(n²)`: for `n ≥ 1`, `5n ≤ 5n²`, so `3n² + 5n ≤ 8n²`. The constant `8` and the
threshold `1` are witnesses; big-O hides both. -/
theorem quadratic_isBigO :
(fun n : ℕ => (3 * (n : ℝ) ^ 2 + 5 * n)) =O[atTop] (fun n : ℕ => (n : ℝ) ^ 2) := by
refine IsBigO.of_bound 8 ?_
filter_upwards [eventually_ge_atTop 1] with n hn
have hn' : (1 : ℝ) ≤ n := by exact_mod_cast hn
have h0 : (0 : ℝ) ≤ n := by linarith
rw [Real.norm_eq_abs, Real.norm_eq_abs, abs_of_nonneg (by positivity),
abs_of_nonneg (by positivity)]
nlinarith/-- `Fₙ ≤ φⁿ` for every `n`, by a two-step induction that uses only `φ² = φ + 1`:
`Fₙ₊₂ = Fₙ₊₁ + Fₙ ≤ φⁿ⁺¹ + φⁿ = φⁿ (φ + 1) = φⁿ⁺²`. -/
theorem fib_le_gold_pow (n : ℕ) : (Nat.fib n : ℝ) ≤ goldenRatio ^ n := by
have key : ∀ n, (Nat.fib n : ℝ) ≤ goldenRatio ^ n ∧
(Nat.fib (n + 1) : ℝ) ≤ goldenRatio ^ (n + 1) := by
intro n
induction n with
| zero => simp [one_lt_goldenRatio.le]
| succ k ih =>
refine ⟨ih.2, ?_⟩
calc (Nat.fib (k + 2) : ℝ) = Nat.fib (k + 1) + Nat.fib k := by
rw [Nat.fib_add_two, Nat.cast_add, add_comm]
_ ≤ goldenRatio ^ (k + 1) + goldenRatio ^ k := add_le_add ih.2 ih.1
_ = goldenRatio ^ k * (goldenRatio + 1) := by ring
_ = goldenRatio ^ (k + 2) := by rw [← goldenRatio_sq]; ring
exact (key n).1/-- `Fₙ = O(φⁿ)`, from `fib_le_gold_pow` with constant `1`. (Binet's formula shows more:
`Fₙ = Θ(φⁿ)`, since `|ψⁿ| ≤ 1`.) -/
theorem fib_isBigO :
(fun n : ℕ => (Nat.fib n : ℝ)) =O[atTop] (fun n : ℕ => goldenRatio ^ n) := by
refine IsBigO.of_bound 1 (Eventually.of_forall fun n => ?_)
rw [Real.norm_eq_abs, Real.norm_eq_abs, abs_of_nonneg (by positivity),
abs_of_nonneg (by positivity), one_mul]
exact fib_le_gold_pow n/-- **Polynomials lose to exponentials**: `nᵏ = o(rⁿ)` for every `k` and every `r > 1`. This
applies Mathlib's `isLittleO_pow_const_const_pow_of_one_lt`. -/
theorem poly_isLittleO_exp (k : ℕ) {r : ℝ} (hr : 1 < r) :
(fun n : ℕ => (n : ℝ) ^ k) =o[atTop] (fun n : ℕ => r ^ n) :=
isLittleO_pow_const_const_pow_of_one_lt k hrWhat is and is not formalised. Proved here: Lemmas 1–3, Theorem 1, the extra solution in Theorem 2 (not the full "exactly these" statement), Binet's formula, , the Hanoi formula, , the Fibonacci generating function, and two big-O facts. Cited from Mathlib: the general-order theory of linear recurrences, the geometric series in , the invertibility criterion, the Catalan closed form and . Not formalised: partial fractions, the reflection proof, the generating-function proof of the Catalan formula, Stirling-type asymptotics and the master theorem.
Experiments: four routes to Fibonacci, four routes to Catalan
The first experiment computes Fibonacci numbers from the recurrence, from the generating function by exact power-series division, and by repeated squaring of the matrix , and checks that they agree. Then it evaluates Binet's formula in floating point and finds where the rounded answer first goes wrong. The formula is a theorem; its floating-point evaluation is not — a theme of numerical methods.
"""Three ways to compute Fibonacci numbers, and where floating point fails.
The recurrence F(n+2) = F(n+1) + F(n) gives exact integers. Binet's formula
F(n) = (phi^n - psi^n)/sqrt(5) is an exact theorem, but evaluating it with
64-bit floats is not exact: phi and sqrt(5) are rounded, and once F(n) has
more than about 16 significant digits the rounded answer must go wrong. The
generating function x/(1 - x - x^2) gives the same numbers again, by dividing
power series exactly. Finally, the matrix identity
[[1, 1], [1, 0]]^n = [[F(n+1), F(n)], [F(n), F(n-1)]] computes F(n) with only
about log2(n) matrix multiplications, and its determinant gives Cassini's
identity F(n+1)F(n-1) - F(n)^2 = (-1)^n.
"""
import math
def fib_list(count):
"""The first `count` Fibonacci numbers F(0), F(1), ..., exactly."""
out = []
a, b = 0, 1
for _ in range(count):
out.append(a)
a, b = b, a + b
return out
def series_divide(num, den, count):
"""First `count` coefficients of num(x)/den(x) as a formal power series.
`num` and `den` are coefficient lists; den[0] must be 1 so that only
integer arithmetic is needed. Coefficient n is found from
num[n] = sum(den[k] * q[n - k]), solved for q[n].
"""
if not den or den[0] != 1:
raise ValueError("the denominator must have constant term 1")
q = []
for n in range(count):
c = num[n] if n < len(num) else 0
for k in range(1, min(n, len(den) - 1) + 1):
c -= den[k] * q[n - k]
q.append(c)
return q
def mat_mul(x, y):
"""Product of 2 x 2 integer matrices given as ((a, b), (c, d))."""
(a, b), (c, d) = x
(e, f), (g, h) = y
return ((a * e + b * g, a * f + b * h), (c * e + d * g, c * f + d * h))
def fib_matrix(n):
"""F(n) from [[1, 1], [1, 0]]^n by repeated squaring; also the multiplications used."""
if n < 0:
raise ValueError("n must be non-negative")
result, power, mults = ((1, 0), (0, 1)), ((1, 1), (1, 0)), 0
while n:
if n & 1:
result, mults = mat_mul(result, power), mults + 1
n >>= 1
if n:
power, mults = mat_mul(power, power), mults + 1
return result[0][1], mults
def binet_float(n):
"""Binet's formula evaluated in floating point and rounded to an integer."""
root5 = math.sqrt(5)
phi, psi = (1 + root5) / 2, (1 - root5) / 2
return round((phi ** n - psi ** n) / root5)
def first_binet_failure(limit):
"""Smallest n < limit where the rounded float Binet value is wrong, or None."""
for n, exact in enumerate(fib_list(limit)):
if binet_float(n) != exact:
return n
return None
def cassini_holds(count):
"""Check F(n+1)F(n-1) - F(n)^2 == (-1)^n for 1 <= n < count."""
f = fib_list(count + 1)
return all(f[n + 1] * f[n - 1] - f[n] ** 2 == (-1) ** n
for n in range(1, count))
def main():
f = fib_list(300)
print("F(0..12) from the recurrence:", f[:13])
gf = series_divide([0, 1], [1, -1, -1], 300)
print("Coefficients of x/(1 - x - x^2) agree for n < 300:", gf == f)
same = all(fib_matrix(n)[0] == f[n] for n in range(300))
print("Matrix powers agree for n < 300:", same)
print("Cassini F(n+1)F(n-1) - F(n)^2 = (-1)^n for 1 <= n < 300:",
cassini_holds(300))
value, mults = fib_matrix(1000)
print(f"F(1000) has {len(str(value))} digits; found with {mults} "
"matrix products")
print(f"(n log10(phi) predicts {1000 * math.log10((1 + 5 ** 0.5) / 2):.1f}"
" digits)")
n = first_binet_failure(200)
print(f"Rounded float Binet first fails at n = {n}:")
print(f" exact F({n}) = {f[n]}")
print(f" float Binet = {binet_float(n)}")
print(f" F({n}) has {len(str(f[n]))} digits. A float carries about 16, and")
print(" the relative rounding error of phi grows about n-fold in phi^n.")
print()
print("Epistemic status: the agreements for n < 300 are exact integer")
print("computations, i.e. exhaustive checks of finitely many cases; the")
print("proofs for all n are in the lesson (and in Lean). The Binet failure")
print("is a fact about 64-bit floating point, not about the formula,")
print("which is exact in real arithmetic.")
if __name__ == "__main__":
main()The second experiment counts balanced bracket strings by brute force and compares the counts with Segner's recurrence, the closed form, and the coefficients of obtained by iterating on truncated series. Watch the iteration: each round fixes at least one more coefficient. This is convergence of formal power series in the "-adic" sense — two series are close when they agree in many leading coefficients — with no analysis at all. Finally it measures against .
"""Catalan numbers: brute force, a recurrence, a generating function, a formula.
We count balanced strings of n pairs of brackets by checking all 4^n strings
of length 2n (feasible for n <= 8), and compare with Segner's recurrence
C(n+1) = sum C(i) C(n-i), with the closed form binom(2n, n)/(n + 1), and with
the generating function C(x), which satisfies C(x) = 1 + x C(x)^2. We find
the coefficients of C(x) by iterating C <- 1 + x C^2 on truncated power
series: each round fixes at least one more coefficient, which is exactly how
formal power series "converge" with no analysis at all. Finally we compare
C(n) with the asymptotic formula 4^n / (sqrt(pi) n^(3/2)).
"""
import itertools
import math
def is_balanced(s):
"""True if the string of '(' and ')' is a balanced bracket sequence."""
depth = 0
for ch in s:
depth += 1 if ch == "(" else -1
if depth < 0:
return False
return depth == 0
def brute_force_count(n):
"""Number of balanced strings with n pairs, by checking all 4^n strings."""
return sum(is_balanced(s) for s in itertools.product("()", repeat=2 * n))
def segner(count):
"""C(0), ..., C(count - 1) from C(0) = 1 and C(n+1) = sum C(i) C(n - i)."""
c = [1]
while len(c) < count:
n = len(c) - 1
c.append(sum(c[i] * c[n - i] for i in range(n + 1)))
return c[:count]
def closed_form(n):
"""binom(2n, n) / (n + 1), which is always an integer."""
q, r = divmod(math.comb(2 * n, n), n + 1)
if r:
raise ArithmeticError("binom(2n, n) should be divisible by n + 1")
return q
def series_fixed_point(count, rounds):
"""Iterate C <- 1 + x*C^2, keeping the first `count` coefficients."""
c = [0] * count
for _ in range(rounds):
square = [sum(c[i] * c[k - i] for i in range(k + 1))
for k in range(count)]
c = [1] + square[: count - 1]
return c
def asymptotic_ratio(n):
"""C(n) divided by 4^n / (sqrt(pi) n^(3/2)); computed with logarithms."""
log_c = math.lgamma(2 * n + 1) - 2 * math.lgamma(n + 1) - math.log(n + 1)
log_a = n * math.log(4) - 0.5 * math.log(math.pi) - 1.5 * math.log(n)
return math.exp(log_c - log_a)
def main():
rec = segner(16)
print(" n brute force Segner binom(2n,n)/(n+1)")
for n in range(9):
print(f"{n:2d} {brute_force_count(n):11d} {rec[n]:6d} "
f"{closed_form(n):17d}")
ok = all(rec[n] == closed_form(n) for n in range(16))
print("Segner = closed form for n < 16:", ok)
for rounds in (3, 6, 16):
c = series_fixed_point(16, rounds)
agree = sum(1 for a, b in zip(c, rec) if a == b)
print(f"{rounds:2d} rounds of C <- 1 + x C^2: {c[:7]}, "
f"{agree}/16 correct")
print("C(n) / (4^n / (sqrt(pi) n^1.5)):")
for n in (10, 100, 1000):
print(f" n = {n:4d}: {asymptotic_ratio(n):.5f}")
print("(the ratio is close to 1 - 9/(8n), so it tends to 1)")
print()
print("Epistemic status: the table is an exhaustive check of all strings")
print("for n <= 8 plus exact integer arithmetic for n < 16; it proves")
print("nothing about larger n (the reflection proof in the lesson does).")
print("The ratios are floating-point evidence for the asymptotic formula,")
print("which is proved with Stirling's formula.")
if __name__ == "__main__":
main()Explore: ratios and growth rates
The ratio satisfies — divide the recurrence by . So the ratios are the orbit of the map starting from : The cobweb diagram shows them spiralling into the fixed point , where , i.e. . The spiral alternates sides because the derivative there, , is negative; its size is , which is exactly the factor by which the error shrinks per step according to Binet's formula.
Fibonacci ratios: iterating x ↦ 1 + 1/x
Start at x₀ = 1. The fixed point is the golden ratio φ ≈ 1.618.
Iterates xₖ against k
- y = f(x)
- y = x
- cobweb path
Last iterates: x18 = 1.61803, x19 = 1.61803, x20 = 1.61803
The orbit appears to settle at a fixed point x* ≈ 1.61803.
Fixed points in view (numerical): x* = 1.618 (attracting: |f′(x*)| < 1)
Based on 3,000 iterations in floating-point arithmetic: numerical evidence, not proof.
Big-O hides constants, but constants matter for small inputs. Below, is compared with and . With the curves and cross (for ) at ; increase and watch the crossover move right — yet always wins eventually, because . The exponential leaves the picture almost at once.
Constants versus growth rates
Move the sliders and trace along the curves to read off values.
- c · n log₂ n
- n²
- 2ⁿ
- c · n log₂ n: y = 0
- n²: y = 1
- 2ⁿ: y = 2
Exercises
1. Recurrences and generating functions
Warm-upHumanIn how many ways can a strip be tiled by squares and dominoes?
2. Recurrences and generating functions
Warm-upHumanFind the characteristic roots of the recurrence . Enter them as a set, e.g. 4, -1.
3. Recurrences and generating functions
CoreHumanThe sequence satisfies with and . Find a closed formula for and use it to compute .
4. Recurrences and generating functions
CoreHumanWhich formula describes all solutions of (with , arbitrary constants)?
5. Recurrences and generating functions
CoreHumanThe power series converges only at . Which statement about it, as an element of the ring of formal power series, is true?
6. Recurrences and generating functions
CoreHumanWhich of these statements are true? (Choose all that apply.)
7. Recurrences and generating functions
CoreHumanIn the proof of Lemma 2 (two solutions of with the same and agree everywhere), the induction proves the statement " and " rather than just "". Why?
8. Recurrences and generating functions
CoreFormalUsing the lesson's definition IsSol p q a (meaning ∀ n, a (n + 2) = p * a (n + 1) + q * a n), which Lean statement correctly formalises: every real sequence satisfying with and is the Fibonacci sequence?
9. Recurrences and generating functions
CoreHumanIn how many ways can the product be fully bracketed, keeping the factors in order? (For three factors there are two: and .)
10. Recurrences and generating functions
StretchComputationalWrite series_inverse(a, N) that returns the first N coefficients of , where a is the list of coefficients of a power series with a[0] == 1 (missing coefficients count as ). Use the recursion from the Proposition on inverses: and . Then check that gives the Fibonacci numbers .
Common misconceptions
Misconception
"A formal power series has to converge to mean anything." In a series is its sequence of coefficients, and sums and products are computed coefficient by coefficient with finite sums. converges only at , yet it is a perfectly good element of with an inverse (proved in Lean above). Convergence matters only when you want to substitute a number for , or to use analysis to read off asymptotics.
Misconception
"Big-O is the exact running time" or "big-O is a lower bound". is an upper bound up to a constant factor, valid for large . Mergesort is as well as — both are true, the second is sharper. For "exactly this order" use , for a lower bound . And because constants are hidden, an algorithm can be slower than an one for all inputs you will ever meet.
Misconception
"The first few terms reveal the formula." Place points on a circle and join every pair by a chord; for points in general position the regions number for — and then , not . (The true count is .) A guessed formula must be proved, for instance by checking that it satisfies the recurrence and the initial values.
Misconception
"Any recurrence can be solved by the characteristic polynomial." The method needs a recurrence that is linear with constant coefficients and a fixed look-back. It does not apply to , to , or directly to .
Misconception
"One initial value is enough." A recurrence of order needs initial values: with , gives the Lucas numbers , not the Fibonacci numbers. The solution space has dimension .
Many ways of seeing
Algebra— The shift operator, and recurrences as linear algebra
Let be the shift on sequences, . The recurrence says : solutions are the kernel of the linear operator . Since factors as and the factors commute, the kernel splits into the kernels of , each spanned by for . This is the same algebra as solving for the differentiation operator , where replaces and replaces — compare ordinary differential equations.
In matrix form, with the companion matrix , and indeed . The characteristic roots are the eigenvalues of , and diagonalising is Binet's formula. Taking determinants, gives Cassini's identity . A repeated root corresponds to a companion matrix that cannot be diagonalised, and comes from its Jordan block. See linear algebra.
Analysis— Singularities decide growth
Substitute a complex number for , and a generating function with positive radius of convergence becomes an analytic function. By the Cauchy–Hadamard formula, : the exponential growth rate of the coefficients is the reciprocal of the distance to the nearest singularity. For the singularities are the roots and of , so grows like . For the Catalan function the singularity is at , so the growth rate is ; its type — a square root — produces the factor . Turning the shape of a singularity into precise asymptotics is the subject of analytic combinatorics (Flajolet and Sedgewick), whose transfer theorems (Flajolet and Odlyzko, 1990) make this rigorous. See complex analysis.
Number theory— The arithmetic of Fibonacci numbers
Fibonacci numbers have striking divisibility: (Lucas, 1876), so divides whenever divides , and can be prime only if or is prime ( shows the converse fails). Whether there are infinitely many Fibonacci primes is an open problem. Reduced modulo any , the sequence is periodic (the Pisano period): there are only possible consecutive pairs, so some pair repeats, and since the recurrence can be run backwards () the sequence is purely periodic. Modulo the period is , which is why last digits of Fibonacci numbers repeat every terms. A related recurrence powers the Lucas–Lehmer test: for an odd prime , the Mersenne number is prime if and only if , where and . It is how the largest known primes are found.
Combinatorics— The symbolic method
Generating functions translate constructions into operations: disjoint union of classes ↦ sum, pairs ↦ product, sequences ↦ . So "a rhythm is a sequence of beats of size or " translates at once into , and "a binary tree is empty or a root with a left and a right subtree" into — the Catalan equation, which explains why binary trees are counted by . The same dictionary, with (exponential generating functions), handles labelled objects such as permutations and set partitions. A nice consequence in the other direction is Zeckendorf's theorem: every positive integer is uniquely a sum of non-consecutive Fibonacci numbers with (for example ). See enumerative combinatorics.
Probability— Waiting for two heads
Toss a fair coin until two heads appear in a row. Let be the expected number of tosses from the start, and the expected number still needed just after a head. Conditioning on the next toss gives the linear system and , so . The probability of no two consecutive heads in tosses is , because such strings are counted by the rhythm recurrence — so it decays like . The probability generating function of the waiting time satisfies a recurrence-derived equation too, and . Linear recurrences for probabilities are the backbone of Markov chains.
Computation— Three algorithms for Fₙ
Computing by the naive recursion fib(n) = fib(n-1) + fib(n-2) makes calls — exponentially many, , because the same subproblems are solved again and again. Storing the values (memoisation, the idea behind dynamic programming) brings this down to additions. The matrix identity, with , needs only multiplications. But has about decimal digits, so for large the cost is dominated by multiplying huge integers, not by the number of operations. In the other direction, Gabriel Lamé (1844) proved that if Euclid's algorithm on takes division steps then — so the number of steps is at most about five times the number of decimal digits of , with consecutive Fibonacci numbers as the worst case. It is one of the first analyses of an algorithm's running time. See algorithm design.
History and global context
- Indian prosody. Pingala's Chandaḥśāstra (its date is uncertain, perhaps the last centuries BCE) studied the enumeration of poetic metres. The rule that the number of metres of total length , built from short and long syllables, is the sum of the two previous counts is stated explicitly by Virahāṅka (between 600 and 800 CE), and again by Gopāla (before 1135) and Hemacandra (c. 1150).
- Leonardo of Pisa. In Liber Abaci (1202) Leonardo — later called Fibonacci — posed the rabbit problem whose answers are . Johannes Kepler remarked (1611) that the ratios of consecutive terms approach the golden ratio. The name "Fibonacci numbers" is due to Édouard Lucas in the 1870s, who also proved their divisibility properties.
- Generating functions. Abraham de Moivre introduced generating functions to solve linear recurrences ("recurrent series") in Miscellanea Analytica (1730), obtaining the closed form for the Fibonacci numbers; Daniel Bernoulli found it independently (1728), and Jacques Binet rediscovered it in 1843, giving it his name. Leonhard Euler used generating functions for partitions (Introductio in analysin infinitorum, 1748), and Pierre-Simon Laplace made them a central tool of probability (Théorie analytique des probabilités, 1812).
- Catalan numbers. The Mongolian astronomer and mathematician Minggatu (Ming Antu) found them in the 1730s in power-series expansions of trigonometric functions (published posthumously in 1839). Euler counted triangulations of polygons in a letter to Christian Goldbach (1751), Johann Andreas von Segner gave the recurrence (1758), and Eugène Catalan studied bracketings (1838). The reflection argument is traditionally credited to Désiré André (1887), whose own proof of the closely related ballot theorem was in fact a slightly different bijection.
- Asymptotic notation. Paul Bachmann introduced (1894) and Edmund Landau popularised it and added (1909). Donald Knuth proposed the now-standard meanings of and for the analysis of algorithms (1976); G. H. Hardy and J. E. Littlewood had earlier used in a different sense (1914). The master theorem is due to Jon Bentley, Dorothea Haken and James Saxe (1980).
Further reading
- Ronald L. Graham, Donald E. Knuth and Oren Patashnik, Concrete Mathematics (1989; 2nd edition 1994) — chapters 6–9 on special numbers, generating functions and asymptotics, with superb exercises.
- Herbert S. Wilf, generatingfunctionology (1990; 3rd edition 2006) — a short, joyful introduction to generating functions as a working tool.
- Philippe Flajolet and Robert Sedgewick, Analytic Combinatorics (2009) — the symbolic method and singularity analysis in full.
- Richard P. Stanley, Enumerative Combinatorics, Volume 1 (1986; 2nd edition 2012), and Catalan Numbers (2015).
- Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein, Introduction to Algorithms (1990; 4th edition 2022) — chapter 4 on divide-and-conquer recurrences and the master theorem.
- Parmanand Singh, "The so-called Fibonacci numbers in ancient and medieval India", Historia Mathematica 12 (1985) — the Indian history of the sequence.
Next in the Lumenary: algorithm design and analysis, enumerative combinatorics and Markov chains.