Mathematical Induction & Recursive Definitions

Discrete Mathematics — Session 3 (Full Course v2 · combined, rich in examples)

Notes By Pr. El Hadiq Zouhair

1 / 38

Outline of the combined course

Part I — Mathematical induction

  1. Well-ordering principle and weak induction — equivalence proved.
  2. Method template + sum formulas: $\sum k$, $\sum k^2$, $\sum k^3$, $\sum 2^k$, $\sum k\cdot k!$.
  3. Inequalities: $2^n > n^2$, $n! \geq 2^n$, AM–GM warm-up.
  4. Divisibility: $3 \mid n^3 + 2n$, $7 \mid (8^n - 1)$, $6 \mid n^3 - n$.
  5. Set / counting examples: $2^n$ subsets, lines from $n$ points.
  6. Geometry: triangulating a polygon, planar regions.
  7. Strong induction: prime factorization, postage stamps, Fibonacci bound.
  8. Classical pitfalls (all horses, hidden base case).

Part II — Recursive definitions

  1. Recursive functions: base + recursive case; factorial, Fibonacci, Ackermann.
  2. Recurrence relations — linear homogeneous with constant coefficients.
  3. Closed-form derivation: characteristic equation; Binet's formula for $F_n$.
  4. Tower of Hanoi $H_n = 2^n - 1$ (proved).
  5. Recursively defined sets: strings, lists, trees.
  6. Structural induction: every tree, list, formula.
  7. Memoization, dynamic programming, time complexity.
  8. Tail recursion ↔ iteration; mutual recursion; fixed points.
  9. Recursion and fractals (Cantor set, Sierpinski).
  10. Common pitfalls; summary; Python toolbox; bibliography.

References: Rosen 8e §§5.1–5.4; Velleman 3e ch. 6; Concrete Mathematics ch. 1; Spivak ch. 2.

2 / 38
Part I

Mathematical induction

Well-ordering · weak induction · strong induction · structural induction · 12 worked examples.

3 / 38

1.1 Well-ordering principle (WOP)

Well-ordering principle. Every non-empty subset $S \subseteq \mathbb{N}$ has a least element: $\exists m \in S,\;\forall n \in S,\;m \leq n$.

WOP is taken as an axiom of the natural numbers. It is equivalent to weak induction (next slide) and to strong induction (slide 16). Any one of the three can be taken as the foundational principle and the other two proved as theorems.

The set $S = \{n \in \mathbb{N} : n^2 \geq 20\}$ is non-empty (contains $5$). By WOP it has a least element. Computing: $4^2 = 16 < 20$ but $5^2 = 25 \geq 20$, so $\min S = 5$.
WOP fails on $\mathbb{Z}$: the set $\mathbb{Z}$ itself has no least element. It also fails on $\mathbb{Q}_{\geq 0}$: the interval $(0, 1) \cap \mathbb{Q}$ is non-empty and has no minimum. WOP is the defining property of $\mathbb{N}$ — it expresses the absence of an "infinite descent".

Rosen 8e §5.2; Halmos §17.

4 / 38

1.2 Weak induction (PWI)

Principle of weak induction. Let $P(n)$ be a predicate on $\mathbb{N}$. If
  1. Base case: $P(0)$ holds, and
  2. Inductive step: for every $k \in \mathbb{N}$, $P(k) \to P(k+1)$,
then $\forall n \in \mathbb{N},\, P(n)$.

The base may be shifted: if $P(n_0)$ holds and $\forall k \geq n_0,\, P(k) \to P(k+1)$, then $\forall n \geq n_0,\, P(n)$.

WOP $\Leftrightarrow$ PWI.
(WOP ⇒ PWI.) Suppose WOP and the two PWI hypotheses. Let $S = \{n : \neg P(n)\}$. If $S \neq \varnothing$, by WOP it has a minimum $m$; $m \neq 0$ (base), so $m - 1 \in \mathbb{N}$. By minimality, $P(m-1)$ holds; by the step, $P(m)$ holds — contradiction.
(PWI ⇒ WOP.) Suppose PWI. Let $S \subseteq \mathbb{N}$ non-empty without minimum; we derive a contradiction by induction on $n$ that $n \notin S$ for every $n$ (so $S = \varnothing$). Base: $0 \notin S$ (else $0$ would be the minimum). Step: if no $k \leq n$ is in $S$ then $n+1 \notin S$ (else $n+1$ would be the minimum). By PWI, $\forall n, n \notin S$. $\square$

Rosen 8e §5.2 Theorem 1; Velleman 3e §6.4.

5 / 38

2. The induction template

To prove $\forall n \geq n_0,\, P(n)$ by induction:

  1. Base case. Show $P(n_0)$ explicitly.
  2. Inductive hypothesis (IH). Fix an arbitrary $k \geq n_0$ and assume $P(k)$.
  3. Inductive step. Using IH, prove $P(k+1)$.

Conclude: by induction, $\forall n \geq n_0,\, P(n)$. $\square$

Skipping the base case is fatal: the step alone proves only "if $P(n_0)$, then $\forall n \geq n_0,\,P(n)$" — useless when $P(n_0)$ is false.

The next slides apply this template to 12 different examples: sums, inequalities, divisibility, sets, and geometry.

Rosen 8e §5.1; Velleman 3e §6.1.

6 / 38

3.1 Example — $\sum_{i=1}^{n} i = n(n+1)/2$

For every $n \geq 1$, $\;\displaystyle \sum_{i=1}^{n} i = \frac{n(n+1)}{2}.$
Base ($n = 1$). LHS $= 1$, RHS $= 1 \cdot 2 / 2 = 1$. ✓
Step. Assume $P(k)$: $\sum_{i=1}^k i = k(k+1)/2$. Then $\sum_{i=1}^{k+1} i = \tfrac{k(k+1)}{2} + (k+1) = \tfrac{k(k+1) + 2(k+1)}{2} = \tfrac{(k+1)(k+2)}{2}.$ ✓ $\square$
Python — verification def somme_n(n): return sum(range(1, n + 1)) for n in (1, 5, 10, 100): print(f"sum(1..{n}) = {somme_n(n)}, formule = {n*(n+1)//2}")

Rosen 8e §5.1 Ex. 1; Gauss's school-day legend.

7 / 38

3.2 Examples — $\sum k^2$ and $\sum k^3$

$\displaystyle \sum_{i=1}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}$  and  $\displaystyle \sum_{i=1}^{n} i^3 = \left(\frac{n(n+1)}{2}\right)^{\!2}.$
For $\sum k^2$: base $n = 1$: $1 = 1 \cdot 2 \cdot 3/6$. Step: $\sum_{i=1}^{k+1} i^2 = \tfrac{k(k+1)(2k+1)}{6} + (k+1)^2 = \tfrac{(k+1)[k(2k+1) + 6(k+1)]}{6} = \tfrac{(k+1)(2k^2 + 7k + 6)}{6} = \tfrac{(k+1)(k+2)(2k+3)}{6}.$ ✓ $\square$
For $\sum k^3$: base $n = 1$ is $1 = 1$. Step: using the identity $(k+1)^3 = \bigl(\tfrac{(k+1)(k+2)}{2}\bigr)^{\!2} - \bigl(\tfrac{k(k+1)}{2}\bigr)^{\!2}$ (verify by expansion), the formula propagates. $\square$
Curious identity: $\sum_{i=1}^{n} i^3 = \bigl(\sum_{i=1}^n i\bigr)^{2}$. The cumulative sum of cubes equals the square of the cumulative sum — a fact apparent in the Nicomachus's "cubes as concentric L-shapes" diagram.

Rosen 8e §5.1; Concrete Mathematics §2.4.

8 / 38

3.3 Examples — $\sum 2^k$ and $\sum k \cdot k!$

$\displaystyle \sum_{k=0}^{n} 2^k = 2^{n+1} - 1.$  (Geometric sum with ratio $2$.)
Base $n = 0$: $2^0 = 1 = 2^1 - 1$. ✓   Step: $\sum_{k=0}^{n+1} 2^k = (2^{n+1} - 1) + 2^{n+1} = 2^{n+2} - 1$. ✓ $\square$
$\displaystyle \sum_{k=1}^{n} k \cdot k! = (n+1)! - 1.$
Base $n = 1$: $1 \cdot 1! = 1 = 2! - 1$. ✓   Step: $\sum_{k=1}^{n+1} k \cdot k! = (n+1)! - 1 + (n+1)(n+1)! = (n+1)![1 + (n+1)] - 1 = (n+2)! - 1.$ ✓ $\square$
Trick. Notice that $k \cdot k! = (k+1)! - k!$ — so the sum telescopes: $\sum_{k=1}^{n} \bigl[(k+1)! - k!\bigr] = (n+1)! - 1!$. This gives the same result without induction.

Concrete Mathematics §2.

9 / 38

4.1 Inequality — $2^n > n^2$ for $n \geq 5$

For every $n \geq 5$, $\;2^n > n^2$.
Base ($n = 5$). $2^5 = 32$, $5^2 = 25$; $32 > 25$. ✓
Step. Assume $2^k > k^2$ with $k \geq 5$. Then $2^{k+1} = 2 \cdot 2^k > 2 k^2 = k^2 + k^2.$ It suffices to show $k^2 \geq 2k + 1$, i.e. $k^2 - 2k - 1 \geq 0$. For $k \geq 5$: $k^2 - 2k - 1 \geq 25 - 10 - 1 = 14 > 0$. ✓ Hence $2^{k+1} > k^2 + (2k+1) = (k+1)^2$. By induction, $\forall n \geq 5,\;2^n > n^2$. $\square$
Bound is tight: at $n = 4$, $2^4 = 16 = 4^2$; at $n = 3$, $2^3 = 8 < 9 = 3^2$. Choosing the right $n_0$ matters.

Rosen 8e §5.1 Ex. 4.

10 / 38

4.2 Inequality — $n! \geq 2^n$ for $n \geq 4$

For every $n \geq 4$, $\;n! \geq 2^n$.
Base ($n = 4$). $4! = 24$, $2^4 = 16$; $24 \geq 16$. ✓
Step. Assume $k! \geq 2^k$ with $k \geq 4$. Then $(k+1)! = (k+1) \cdot k! \geq (k+1) \cdot 2^k.$ Since $k \geq 4$, $k + 1 \geq 5 \geq 2$. Hence $(k+1) \cdot 2^k \geq 2 \cdot 2^k = 2^{k+1}$. ✓ $\square$
More generally, $n! / 2^n \to \infty$ — factorials beat exponentials. By Stirling, $n!/n^n \cdot e^n \to \sqrt{2\pi n}$.

Rosen 8e §5.1; Spivak ch. 22.

11 / 38

5.1 Divisibility — $3 \mid (n^3 + 2n)$

For every $n \geq 0$, $\;3 \mid (n^3 + 2n)$.
Base ($n = 0$). $0^3 + 0 = 0$ and $3 \mid 0$. ✓
Step. Assume $k^3 + 2k = 3m$. Then $(k+1)^3 + 2(k+1) = k^3 + 3k^2 + 3k + 1 + 2k + 2 = (k^3 + 2k) + 3(k^2 + k + 1) = 3m + 3(k^2 + k + 1)$ which is divisible by $3$. ✓ $\square$
For every $n \geq 0$, $\;7 \mid (8^n - 1)$.
Base $n = 0$: $8^0 - 1 = 0$, and $7 \mid 0$. Step: $8^{k+1} - 1 = 8 \cdot 8^k - 1 = 8(8^k - 1) + 7$, divisible by $7$. $\square$
For every $n \geq 0$, $\;6 \mid (n^3 - n)$.
Note $n^3 - n = n(n-1)(n+1)$ — product of three consecutive integers, which always contains a multiple of $2$ and a multiple of $3$. (Or direct induction.) $\square$

Rosen 8e §5.1 Ex. 5; Hardy & Wright §1.

12 / 38

6.1 Set example — $2^n$ subsets

For every $n \geq 0$, every set of $n$ elements has exactly $2^n$ subsets.
Base ($n = 0$). $\mathcal{P}(\varnothing) = \{\varnothing\}$, so $|\mathcal{P}(\varnothing)| = 1 = 2^0$. ✓
Step. Suppose every $k$-set has $2^k$ subsets. Let $|A| = k + 1$; pick $a \in A$ and let $B = A \setminus \{a\}$, $|B| = k$. Each subset of $A$ either contains $a$ or does not — the two classes are disjoint and each has $2^k$ subsets of $B$ (those not containing $a$) plus $2^k$ subsets of the form $S \cup \{a\}$ (containing $a$). Total: $2^k + 2^k = 2^{k+1}$. ✓ $\square$
Counting lines. $n$ points in general position (no three collinear) determine exactly $\binom{n}{2} = n(n-1)/2$ distinct lines.
Base $n = 2$: one line. Step: adding a new point $P$ to a set of $k$ points gives $k$ new lines (one to each existing point); the previous $\binom{k}{2}$ lines remain, so the total becomes $\binom{k}{2} + k = \binom{k+1}{2}$ (Pascal's identity). $\square$

Rosen 8e §5.1 Ex. 10.

13 / 38

7.1 Geometry — triangulating a convex $n$-gon

Every convex polygon with $n \geq 3$ vertices admits a triangulation by non-crossing diagonals, and every such triangulation uses exactly $n - 2$ triangles.
Base $n = 3$: a triangle is already triangulated into $3 - 2 = 1$ triangle. ✓
Step: a convex $(k+1)$-gon has at least one diagonal $d$ (for $k + 1 \geq 4$). Cutting along $d$ splits the polygon into two convex polygons sharing $d$, with $j_1$ and $j_2$ vertices where $j_1 + j_2 = (k+1) + 2$. Both $j_i \leq k$, so by IH each has $(j_i - 2)$ triangles, summing to $(k+1) + 2 - 4 = (k+1) - 2$. ✓ $\square$
Plane regions. $n$ lines in general position divide the plane into $1 + n + \binom{n}{2}$ regions.
Base $n = 0$: $1$ region. Step: a new line, in general position with the existing $k$ lines, crosses each of them in $1$ point, so it is divided into $k + 1$ arcs; each arc cuts an existing region into two. So $R_{k+1} = R_k + (k+1) = 1 + k + \binom{k}{2} + k + 1 = 1 + (k+1) + \binom{k+1}{2}$. ✓ $\square$

Rosen 8e §5.2 Ex. 12; Steiner's theorem (1826).

14 / 38

8. Two classical inductive inequalities

Bernoulli's inequality. For every $x > -1$ and every $n \geq 0$, $(1 + x)^n \geq 1 + nx$.
Base $n = 0$: $(1+x)^0 = 1 = 1 + 0$. ✓   Step: $(1+x)^{k+1} = (1+x)(1+x)^k \geq (1+x)(1+kx)$ since $1 + x > 0$ and IH. Expanding: $(1+x)(1+kx) = 1 + (k+1)x + kx^2 \geq 1 + (k+1)x$ since $kx^2 \geq 0$. ✓ $\square$
AM–GM, $n = 2^m$ case. For positive reals $a_1, \ldots, a_n$: $\;\dfrac{a_1 + \cdots + a_n}{n} \geq \sqrt[n]{a_1 \cdots a_n}.$
By induction on $m$: base $m = 0$ ($n = 1$): trivial. Base $m = 1$ ($n = 2$): $(a_1 + a_2)/2 \geq \sqrt{a_1 a_2}$ by $(a_1 - a_2)^2 \geq 0$ (re-arrange). Step: assume for $n = 2^m$. Pair up $2^{m+1}$ numbers as $(a_1, a_2), \ldots, (a_{2^{m+1}-1}, a_{2^{m+1}})$. By the $n = 2$ case applied $2^m$ times, replace each pair by its arithmetic mean; the overall mean is unchanged, and the product is no larger. Apply IH to the $2^m$ pair-means. $\square$

Cauchy's proof of AM–GM; Bernoulli (1689).

15 / 38

9. Strong induction (course-of-values)

Principle of strong induction. If $P(n_0)$ holds and for every $k \geq n_0$ $\bigl(\forall j,\; n_0 \leq j \leq k \Rightarrow P(j)\bigr) \;\to\; P(k+1),$ then $\forall n \geq n_0,\; P(n)$.

The strong-step inductive hypothesis is generous: instead of just $P(k)$, we may use all $P(j)$ for $j \leq k$. This is needed when the recurrence reaches back more than one step.

Strong induction $\Leftrightarrow$ Weak induction.
(PSI ⇒ PWI.) The weak step is the special case of the strong step where only $j = k$ is used.
(PWI ⇒ PSI.) Define $Q(n) :\equiv \bigl(\forall j \leq n,\, P(j)\bigr)$. Apply weak induction to $Q$: base $Q(n_0)$ is $P(n_0)$; step $Q(k) \to Q(k+1)$ is the strong step. By PWI, $\forall n, Q(n)$, so $\forall n, P(n)$. $\square$

Rosen 8e §5.2 Theorem 1; Velleman 3e §6.4.

16 / 38

10. Two key strong-induction examples

Existence of prime factorization. Every integer $n \geq 2$ is a product of primes.
Base $n = 2$: $2$ is prime. Strong step: suppose $P(j)$ for all $2 \leq j \leq k$. For $k + 1$: if it is prime, $P(k+1)$ holds trivially. Otherwise $k + 1 = ab$ with $2 \leq a, b \leq k$; by IH both are products of primes, so $k + 1 = a \cdot b$ is too. $\square$
Frobenius / postage stamps. Every postage $n \geq 12$ cents can be formed with $4$ and $5$ cent stamps.
Bases: $12 = 4\cdot3$, $13 = 4\cdot2 + 5$, $14 = 4 + 5\cdot2$, $15 = 5\cdot3$. ✓ (Four bases.)
Step: for $k + 1 \geq 16$, $k - 3 \geq 12$, so by IH $k - 3 = 4a + 5b$; thus $k + 1 = (k - 3) + 4 = 4(a+1) + 5b$. $\square$
Fibonacci bound. $F_n \leq \varphi^{n-1}$ where $\varphi = (1+\sqrt 5)/2$.
Bases $n = 1, 2$: $F_1 = 1 \leq \varphi^0$, $F_2 = 1 \leq \varphi$. Step: $F_{k+1} = F_k + F_{k-1} \leq \varphi^{k-1} + \varphi^{k-2} = \varphi^{k-2}(\varphi + 1) = \varphi^{k-2}\cdot \varphi^2 = \varphi^k.$ using $\varphi^2 = \varphi + 1$. $\square$

Rosen 8e §5.2 Theorems 2, 3, 4.

17 / 38

11. Common induction pitfalls

"All horses have the same colour." A famous "proof": base $n = 1$ is trivial. Step: remove a horse from a group of $k+1$, the remaining $k$ have the same colour (IH); put it back, remove a different one — same colour. So all $k+1$ have the same colour.
Where it breaks. At $k+1 = 2$, the two subsets of size $1$ are disjoint, so the "common colour" cannot be transmitted. The step is only valid for $k \geq 2$, but the base only gives $k = 1$.
Missing base case. "Theorem: $n = n + 1$ for all $n \in \mathbb{N}$." "Proof": step works algebraically ($k = k + 1 \Rightarrow k + 1 = k + 2$). But the base $0 = 1$ is false, so the chain never starts.
Bound creep. If the step requires $k \geq 3$, the conclusion is $\forall n \geq 3,\,P(n)$, not $\forall n \geq 0$. Always match the base and the step.
Hidden non-arbitrariness. Each application of the inductive step must work for an arbitrary $k$. A proof that secretly assumes "$k$ is large enough" is no proof at all.

Pólya's classical pedagogical examples; Velleman 3e §6.1.

18 / 38
Part II

Recursive definitions

Recursive functions · recurrences · closed forms · recursive sets & structures · structural induction · fractals.

19 / 38

12. Recursive functions — definition

A recursive definition of a function $f : \mathbb{N} \to X$ specifies two pieces:
  1. Base case(s). The value $f(n)$ is given explicitly for some initial $n_0$ (and possibly $n_0 + 1, \ldots, n_0 + k - 1$).
  2. Recursive case. For $n > n_0 + k - 1$, $f(n)$ is computed from one or more earlier values $f(n - 1), f(n - 2), \ldots$
Well-definedness is justified by induction on $n$.
Factorial. $f(0) = 1$; $f(n) = n \cdot f(n-1)$ for $n \geq 1$. Then $f(n) = n!$.
Fibonacci. $F_0 = 0$, $F_1 = 1$; $F_n = F_{n-1} + F_{n-2}$ for $n \geq 2$. First terms: $0, 1, 1, 2, 3, 5, 8, 13, 21, 34, \ldots$
Lucas. $L_0 = 2$, $L_1 = 1$; $L_n = L_{n-1} + L_{n-2}$.
Ackermann. $A(0, n) = n + 1$; $A(m, 0) = A(m-1, 1)$; $A(m, n) = A(m-1, A(m, n-1))$ — grows faster than any primitive recursive function.
Python — recursive implementations def fact(n): return 1 if n == 0 else n * fact(n - 1) def fib(n): return n if n < 2 else fib(n-1) + fib(n-2) def lucas(n): return 2 if n == 0 else 1 if n == 1 else lucas(n-1) + lucas(n-2) print([fact(k) for k in range(7)]) # [1, 1, 2, 6, 24, 120, 720] print([fib(k) for k in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Rosen 8e §5.3; Concrete Mathematics §1.1.

20 / 38

13. From recurrence to closed form

A linear homogeneous recurrence with constant coefficients of order $k$ is $a_n = c_1 a_{n-1} + c_2 a_{n-2} + \cdots + c_k a_{n-k}, \;\; c_k \neq 0, \; c_i \in \mathbb{R}.$ Its characteristic equation is $x^k = c_1 x^{k-1} + \cdots + c_k.$
If the characteristic equation has $k$ distinct real roots $r_1, \ldots, r_k$, then every solution has the form $a_n = \alpha_1 r_1^n + \cdots + \alpha_k r_k^n,$ where $\alpha_i$ are determined by the $k$ initial values.
Each $r_i^n$ satisfies the recurrence (substitute and divide by $r_i^{n-k}$). Linearity preserves the property. The initial-value Vandermonde system has a unique solution since the $r_i$ are distinct. By induction the formula extends to all $n$. $\square$
Constant recurrence. $a_n = a_{n-1}$, $a_0 = c$: solution $a_n = c$.
Doubling. $a_n = 2 a_{n-1}$, $a_0 = 1$: solution $a_n = 2^n$.
Linear repeated. $a_n = 2 a_{n-1} - a_{n-2}$: root $1$ with multiplicity $2$, solution $a_n = (\alpha + \beta n) \cdot 1^n = \alpha + \beta n$ (arithmetic progression).

Rosen 8e §8.2 Theorem 1.

21 / 38

14. Binet's formula for Fibonacci

Let $\varphi = (1+\sqrt 5)/2$ and $\psi = (1-\sqrt 5)/2$. Then for every $n \geq 0$: $F_n = \dfrac{\varphi^n - \psi^n}{\sqrt 5}.$
The recurrence $F_n = F_{n-1} + F_{n-2}$ has characteristic equation $x^2 = x + 1$, i.e. $x^2 - x - 1 = 0$, with roots $\varphi, \psi$. So $F_n = \alpha \varphi^n + \beta \psi^n$ for some $\alpha, \beta$. Initial conditions: $F_0 = 0 \Rightarrow \alpha + \beta = 0;\quad F_1 = 1 \Rightarrow \alpha\varphi + \beta\psi = 1.$ Solve: $\beta = -\alpha$, $\alpha(\varphi - \psi) = 1$, $\alpha\sqrt 5 = 1$, $\alpha = 1/\sqrt 5$, $\beta = -1/\sqrt 5$. $\square$
Since $|\psi| < 1$, $\psi^n \to 0$, so $F_n \sim \varphi^n / \sqrt 5$ — Fibonacci grows geometrically with ratio $\varphi \approx 1.618$. In fact, $F_n$ is the nearest integer to $\varphi^n / \sqrt 5$ for all $n \geq 0$.
SymPy — derives Binet automatically from sympy import Function, symbols, rsolve n = symbols('n', integer=True, nonnegative=True) a = Function('a') print(rsolve(a(n+2) - a(n+1) - a(n), a(n), {a(0): 0, a(1): 1})) # (sqrt(5)/5) * ((1/2 + sqrt(5)/2)**n - (1/2 - sqrt(5)/2)**n) — Binet

Rosen 8e §8.2 Ex. 6; Binet (1843).

22 / 38

15. Tower of Hanoi — $H_n = 2^n - 1$

Three pegs $A, B, C$; $n$ disks of decreasing size stacked on $A$. Move the entire stack to $C$, one disk at a time, never placing a bigger disk on a smaller one.

Let $H_n$ be the minimum number of moves. Then $H_n = 2^n - 1$.
Recurrence. To move $n$ disks $A \to C$: (i) move the top $n-1$ to $B$ via $C$, using $H_{n-1}$ moves; (ii) move the largest disk $A \to C$, $1$ move; (iii) move the $n-1$ from $B$ to $C$ via $A$, $H_{n-1}$ moves. Total: $H_n = 2 H_{n-1} + 1$, with $H_0 = 0$.
Closed form by induction. Base: $H_0 = 0 = 2^0 - 1$. Step: $H_{k+1} = 2 H_k + 1 = 2(2^k - 1) + 1 = 2^{k+1} - 1$. ✓ $\square$
Python — recursive solver def hanoi(n, src='A', dst='C', via='B', moves=None): if moves is None: moves = [] if n == 0: return moves hanoi(n - 1, src, via, dst, moves) moves.append(f"{src} → {dst}") hanoi(n - 1, via, dst, src, moves) return moves m = hanoi(3) print(len(m), m) # 7 ['A → C', 'A → B', 'C → B', 'A → C', 'B → A', 'B → C', 'A → C']
With $64$ disks (Brahma legend), at one move per second the puzzle requires $2^{64} - 1 \approx 5.85 \times 10^{17}$ seconds $\approx 585$ billion years — longer than the age of the universe.

Rosen 8e §8.1 Ex. 2; Lucas (1883).

23 / 38

16. Recursively defined sets

To define a set $S$ recursively:
  1. Specify base elements: $b_1, b_2, \ldots \in S$.
  2. Specify construction rules: "if $x_1, \ldots, x_k \in S$ then $f(x_1, \ldots, x_k) \in S$".
  3. Implicit closure: $S$ contains nothing else — only what is forced by 1 and 2.
The natural numbers. Peano: $0 \in \mathbb{N}$; if $n \in \mathbb{N}$ then $S(n) \in \mathbb{N}$ (successor).
The set of binary strings $\Sigma^*$ on $\Sigma = \{0, 1\}$. $\varepsilon \in \Sigma^*$ (empty string); if $w \in \Sigma^*$ then $w0 \in \Sigma^*$ and $w1 \in \Sigma^*$.
Palindromes. $\varepsilon$ and every single character $a$ are palindromes; if $w$ is a palindrome and $a$ a character, then $awa$ is a palindrome.
Well-formed propositional formulas. Every variable $p_i$ is a wff; if $\varphi, \psi$ are wffs, so are $\neg\varphi$, $(\varphi \wedge \psi)$, $(\varphi \vee \psi)$, $(\varphi \to \psi)$, $(\varphi \leftrightarrow \psi)$ (Lesson 2).

Rosen 8e §5.3; Halmos §13.

24 / 38

17. Recursively defined structures

Lists. A list is either nil (the empty list) or cons(x, L) where $x$ is an element and $L$ is a list. (Same as Lisp's (x . L).)
Binary trees. A binary tree is either a leaf, or a node(L, R) where $L$ and $R$ are binary trees. Internal nodes have two children; leaves have none.
Recursive functions on these structures.
Python def length(L): return 0 if L == [] else 1 + length(L[1:]) def leaves(t): # binary tree as tuple ('leaf',) or ('node', L, R) return 1 if t[0] == 'leaf' else leaves(t[1]) + leaves(t[2]) t = ('node', ('leaf',), ('node', ('leaf',), ('leaf',))) print(leaves(t)) # 3

Rosen 8e §5.3; Knuth, TAOCP, vol. 1 §2.3.

25 / 38

18. Structural induction

To prove $\forall x \in S,\, P(x)$ for a recursively defined set $S$:
  1. Verify $P(b)$ for every base element $b$.
  2. For each constructor $f$: show $P(x_1) \wedge \ldots \wedge P(x_k) \to P(f(x_1, \ldots, x_k))$.
Then $\forall x \in S,\,P(x)$.
For every binary tree $t$, $\;\ell(t) = i(t) + 1.$
Base $t = \text{leaf}$: $\ell(t) = 1$, $i(t) = 0$, $i(t) + 1 = 1$. ✓
Step $t = \text{node}(L, R)$ with IH on $L$ and $R$: $\ell(t) = \ell(L) + \ell(R) = (i(L) + 1) + (i(R) + 1) = i(L) + i(R) + 2 = i(t) + 1.$ ✓ $\square$
For all lists $L, M$, $\;\mathrm{length}(\mathrm{append}(L, M)) = \mathrm{length}(L) + \mathrm{length}(M).$
Structural induction on $L$. Base $L = \text{nil}$: LHS $= \mathrm{length}(M) = 0 + \mathrm{length}(M) =$ RHS. Step $L = \text{cons}(x, L')$: $\mathrm{length}(\mathrm{append}(\text{cons}(x, L'), M)) = \mathrm{length}(\text{cons}(x, \mathrm{append}(L', M))) = 1 + \mathrm{length}(\mathrm{append}(L', M)) \;\overset{IH}{=}\; 1 + \mathrm{length}(L') + \mathrm{length}(M) = \mathrm{length}(L) + \mathrm{length}(M).$ ✓ $\square$

Rosen 8e §5.3 Theorem 3.

26 / 38

19. Examples on strings

On the alphabet $\Sigma$, the concatenation of $w_1 = a_1 \cdots a_m$ and $w_2 = b_1 \cdots b_n$ is $w_1 \cdot w_2 = a_1 \cdots a_m b_1 \cdots b_n$. Recursively: $\varepsilon \cdot w = w$; $(aw_1) \cdot w_2 = a (w_1 \cdot w_2)$.
The length $|w|$: $|\varepsilon| = 0$, $|aw| = 1 + |w|$.
The reverse $w^R$: $\varepsilon^R = \varepsilon$, $(aw)^R = w^R \cdot a$.
For all strings $w_1, w_2$, $\;|w_1 \cdot w_2| = |w_1| + |w_2|.$
Structural induction on $w_1$. Base $w_1 = \varepsilon$: $|w_2| = 0 + |w_2|$. ✓ Step $w_1 = a w_1'$: $|aw_1' \cdot w_2| = |a(w_1' \cdot w_2)| = 1 + |w_1' \cdot w_2| \overset{IH}{=} 1 + |w_1'| + |w_2| = |w_1| + |w_2|$. ✓ $\square$
For all strings $w_1, w_2$, $\;(w_1 \cdot w_2)^R = w_2^R \cdot w_1^R$   (reverse swaps the order!).
Induction on $w_1$. Base $w_1 = \varepsilon$: $(w_2)^R = w_2^R = w_2^R \cdot \varepsilon = w_2^R \cdot w_1^R$. ✓ Step $w_1 = a w_1'$: $(aw_1' \cdot w_2)^R = (a(w_1' \cdot w_2))^R = (w_1' \cdot w_2)^R \cdot a \overset{IH}{=} (w_2^R \cdot w_1'^R) \cdot a = w_2^R \cdot (w_1'^R \cdot a) = w_2^R \cdot (aw_1')^R = w_2^R \cdot w_1^R$. ✓ $\square$

Rosen 8e §5.3 Theorem 1; Hopcroft–Ullman §1.

27 / 38

20. Memoization & dynamic programming

The naive Fibonacci recursion fib(n) = fib(n-1) + fib(n-2) recomputes the same subproblems exponentially many times: $\Theta(\varphi^n)$ calls. Storing previously computed values transforms the complexity from $\Theta(\varphi^n)$ to $\Theta(n)$.

Python — three variants import functools, time def fib_naive(n): return n if n < 2 else fib_naive(n-1) + fib_naive(n-2) @functools.lru_cache(maxsize=None) def fib_memo(n): return n if n < 2 else fib_memo(n-1) + fib_memo(n-2) def fib_iter(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a # Benchmark on n = 30 for f in (fib_naive, fib_memo, fib_iter): t = time.time(); v = f(30); print(f"{f.__name__:10s} = {v} ({time.time()-t:.3f}s)")
Typical output fib_naive = 832040 (0.350s) fib_memo = 832040 (0.000s) fib_iter = 832040 (0.000s)
General lesson. If a recursive function has overlapping sub-problems and an optimal substructure, memoization (top-down) or dynamic programming (bottom-up) turns exponential into polynomial.

Rosen 8e §8.1; CLRS, Introduction to Algorithms, ch. 15.

28 / 38

21. Tail recursion ↔ iteration

A function is tail-recursive if every recursive call is its last action — there is nothing left to do once the recursive call returns. Such a call can be replaced by a jump, eliminating the stack frame and giving constant-space iteration.
Factorial — naive vs tail-recursive.
Python def fact(n): # NOT tail-recursive if n == 0: return 1 return n * fact(n - 1) # multiplication after the call def fact_tail(n, acc=1): # tail-recursive if n == 0: return acc return fact_tail(n - 1, n * acc) # call is the last action # Python doesn't optimise tail calls, but the equivalent iteration is: def fact_iter(n): acc = 1 for k in range(1, n + 1): acc *= k return acc
Functional languages (Scheme, Haskell, OCaml, Scala) guarantee tail-call optimisation. Python explicitly does not. For deeply recursive algorithms on Python use an iterative form or sys.setrecursionlimit cautiously.

Abelson & Sussman, SICP, §1.2.

29 / 38

22. Mutual recursion

Two functions are mutually recursive when each calls the other in its definition. Correctness still follows by induction on a common measure (e.g. the size of the input).
Parity by mutual recursion.
Python def even(n): return True if n == 0 else odd(n - 1) def odd(n): return False if n == 0 else even(n - 1) print([even(k) for k in range(6)]) # [True, False, True, False, True, False]
Hofstadter female–male sequences. $F(0) = 1, \; M(0) = 0;$ $F(n) = n - M(F(n-1)),\; M(n) = n - F(M(n-1)).$
Python from functools import lru_cache @lru_cache(maxsize=None) def F(n): return 1 if n == 0 else n - M(F(n - 1)) @lru_cache(maxsize=None) def M(n): return 0 if n == 0 else n - F(M(n - 1)) print([F(k) for k in range(10)]) # [1, 1, 2, 2, 3, 3, 4, 5, 5, 6] print([M(k) for k in range(10)]) # [0, 0, 1, 2, 2, 3, 4, 4, 5, 6]

Hofstadter, Gödel, Escher, Bach, 1979.

30 / 38

23. Fixed points and fractals

A fixed point of a function $f$ is an element $x$ with $f(x) = x$. Recursive structures often arise as fixed points of set-valued functions, e.g. the smallest $S$ such that $f(S) \subseteq S$.
Cantor set. Start with $C_0 = [0, 1]$. At step $n$, $C_n$ is the union of $2^n$ closed intervals of length $3^{-n}$ each. The Cantor set $C = \bigcap_n C_n$ is the fixed point of the contraction $C \mapsto (C/3) \cup ((C + 2)/3)$.
Sierpinski triangle. Start with an equilateral triangle $T_0$. Replace $T_n$ by three half-size copies, one at each vertex: $T_{n+1} = T_n/2 \cup (T_n/2 + v_1) \cup (T_n/2 + v_2)$ — the union of three contracted copies of itself.
Python — Cantor set as nested intervals def cantor(n, a=0.0, b=1.0): if n == 0: return [(a, b)] third = (b - a) / 3 return cantor(n - 1, a, a + third) + cantor(n - 1, b - third, b) for k in range(4): print(f"Level {k}: {cantor(k)}")

Fractals are the prototypical example of a "structure equal to a union of smaller copies of itself" — a recursive definition made geometric.

Mandelbrot (1982); Falconer, Fractal Geometry.

31 / 38

24. Recursive algorithms — Mergesort & master theorem

Mergesort. To sort an array $A$ of length $n$:
  1. If $n \leq 1$, $A$ is already sorted.
  2. Split $A$ into halves $A_1, A_2$ (length $\lfloor n/2 \rfloor$ and $\lceil n/2 \rceil$).
  3. Recursively sort $A_1$ and $A_2$.
  4. Merge the two sorted halves into a single sorted array.
Time complexity. $T(n) = 2 T(n/2) + \Theta(n) = \Theta(n \log n)$.
Unrolling: $T(n) = 2 T(n/2) + cn = 4 T(n/4) + 2cn = \cdots = 2^k T(n/2^k) + kcn$. Stop at $k = \log_2 n$: $T(n) = n \cdot T(1) + cn \log n = \Theta(n \log n)$. $\square$
Master theorem (simplified). If $T(n) = a T(n/b) + \Theta(n^d)$ with $a \geq 1, b > 1, d \geq 0$, then
Mergesort: $a = b = 2, d = 1, \log_b a = 1$ → middle case, $\Theta(n \log n)$. Binary search: $a = 1, b = 2, d = 0$ → middle, $\Theta(\log n)$. Strassen's matrix multiplication: $a = 7, b = 2, d = 2$, $\log_2 7 \approx 2.81 > 2$ → third case, $\Theta(n^{\log_2 7})$.

CLRS ch. 4; Knuth, TAOCP, vol. 3.

32 / 38

25. Programming connection — induction proves correctness

A recursive function's correctness is exactly an induction proof on the recursion measure (typically the input size).

Worked example — proof of correctness of factorial

Python def fact(n): if n == 0: return 1 return n * fact(n - 1)
For every $n \geq 0$, fact(n) returns $n!$.
Induction on $n$. Base $n = 0$: the code returns $1 = 0!$. Step: assume fact(k) returns $k!$. For input $n = k + 1$, the recursive call fact(k) returns $k!$ by IH; the function returns $(k+1) \cdot k! = (k+1)!$. ✓ $\square$

Worked example — loop invariant for the iterative factorial

Python def fact_iter(n): acc = 1 for k in range(1, n + 1): # invariant: after this iteration, acc == k! acc *= k return acc

The loop invariant "after the $k$-th iteration, acc == k!" is proved by induction on $k$ — the iterative version is the loop-form of the recursive proof.

CLRS §2.1; SICP §1.

33 / 38

26. Common recursion pitfalls

Forgetting the base case. The function recurses forever; in Python, you get a RecursionError: maximum recursion depth exceeded.
Wrong base case. A subtle bug: $f(0) = 0$ instead of $f(0) = 1$ in factorial — all values become zero.
Non-decreasing recursion. The recursive call must reduce the argument (or some well-founded measure). Recursing on $f(n)$ with $f(n) = f(n)$ or $f(n+1)$ never terminates.
Exponential blow-up by overlapping sub-problems. Naive Fibonacci is $\Theta(\varphi^n)$. Always check whether memoization saves work.
Stack overflow. Python defaults to a recursion limit of ~1000. Deep recursion needs iteration or an explicit stack.

Rosen 8e §5.4; SICP §1.

34 / 38

27. Strategy — induction vs recursion

SituationTool
Prove $\forall n \geq n_0,\,P(n)$ where the step only uses $P(k)$Weak induction (§2)
Prove $\forall n,\,P(n)$ where the step needs $P(k-1), P(k-2),\ldots$Strong induction (§9)
Prove $\forall x \in S,\,P(x)$ for an inductively defined setStructural induction (§18)
Compute a sequence defined by recurrenceClosed form via characteristic equation (§13)
Recursive algorithm with overlapping subproblemsMemoization / DP (§20)
Recursive algorithm with tail callTail recursion ↔ iteration (§21)
Recurrence $T(n) = a T(n/b) + n^d$Master theorem (§24)
Self-similar geometryFixed-point / fractal recursion (§23)
35 / 38

28. Python / SymPy toolbox

Python — imports import functools, sys from sympy import Function, symbols, rsolve, Sum, simplify n = symbols('n', integer=True, nonnegative=True) a = Function('a')
SymPy — solve recurrences automatically print(rsolve(a(n+1) - 2*a(n) - 1, a(n), {a(0): 0})) # 2**n - 1 → Tower of Hanoi closed form print(rsolve(a(n+2) - a(n+1) - a(n), a(n), {a(0): 0, a(1): 1})) # Fibonacci → Binet's formula print(rsolve(a(n+2) - 3*a(n+1) + 2*a(n), a(n), {a(0): 0, a(1): 1})) # 2**n - 1 → recurrence with characteristic roots 1, 2
SymPy — verify induction step symbolically from sympy import symbols, Eq, simplify, Rational k = symbols('k', integer=True, positive=True) # Inductive step for sum_{i=1}^n i = n(n+1)/2 lhs_kp1 = k*(k+1)/2 + (k+1) # IH + extra term rhs_kp1 = (k+1)*(k+2)/2 # formula at k+1 print(simplify(lhs_kp1 - rhs_kp1)) # 0 → step holds # Inductive step for sum_{i=1}^n i^2 = n(n+1)(2n+1)/6 lhs2 = k*(k+1)*(2*k+1)/6 + (k+1)**2 rhs2 = (k+1)*(k+2)*(2*k+3)/6 print(simplify(lhs2 - rhs2)) # 0
Python — increase recursion limit when needed sys.setrecursionlimit(10_000) # default ~1000; use sparingly
36 / 38

29. Summary — what we proved

Part I — Induction

Part II — Recursion

37 / 38

Bibliography

Notes By Pr. El Hadiq Zouhair

38 / 38