Sunday, January 19, 2025

22 - Recursion

The following is the famous Fibonacci sequence. 

$$ 1,\;1,\;2,\;3,\;5,\;8,\;13,\;21,\;\ldots $$

From 2 onwards, each number is the sum of the previous two. This is an example of a recursive definition.

Here we'll see how to define and work with a recursively defined function.


Task

The following is a recursively defined function $f_{n}$. It takes a natural number n, and returns a natural number.

$$f_{n}=\begin{cases}\:0 & \;\text{if }n=0\\\:2n-1+f_{n-1} & \;\text{otherwise}\end{cases}\tag{1}\label{22.1}$$

Let's see how it works.

  • For $n=0$, the definition tells us $f_{0}=0$.
  • For $n=1$, the definition tells us $f_{1}=2\cdot1-1+f_{0}$. Using $f_{0}=0$, this becomes $f_{1}=1$.
  • For $n=2$, the definition tells us $f_{2}=2\cdot2-1+f_{1}$. Using $f_{1}=1$, this gives us $f_{2}=4$.
  • Try $n=3$ yourself. You should get $f_{3}=9$.

What makes the definition recursive is that, apart from $f_{0}$, each value of the function $f_{n}$ is defined in terms of an earlier value $f_{n-1}$. Informally, the function is defined in terms of itself.

Our task is to prove this recursively defined function has a much neater closed form,

$$f_{n}=n^{2}$$


Similarity To Induction

It is worth pausing to reflect on the process of calculating each $f_{n}$. 

It looks very similar to the process of induction we saw in the last chapter.


Maths

We'll try a proof by induction. Let $P(n)$ be the proposition $f_{n}=n^{2}$. 

The base case $P(0)$ is the proposition $f_{0}=0^{2}$. The definition tells us $f_{0}=0$ so the base case $P(0)$ is indeed true.

The inductive step is the implication $P(n)\implies P(n+1)$. To prove this we assume $P(n)$ is true and derive $P(n+1)$. That is, we assume $f_{n}=n^{2}$ and derive $f_{n+1}=(n+1)^{2}$.

  • The definition (1) tells us $f_{n+1}=2(n+1)-1+f_{n}$. 
  • By assumption $f_{n}=n^{2}$, so $f_{n+1}=2(n+1)-1+n^{2}$.
  • That $2(n+1)-1+n^{2}$ simplifies to $(n+1)^{2}$.
  • Putting all this together, we have $f_{n+1}=(n+1)^{2}$.

We have shown the base case $P(0)$ and the inductive step $P(n)\implies P(n+1)$ are true, and so, by induction, $P(n)$ is true for all natural numbers $n$. That is, $f_{n}=n^{2}$ is true for all $n$.

Let's write this out as preparation for a Lean proof. 

$$\begin{align}P(n):f_{n}&=n^{2}&&\text{proof objective}\\&&&\\\text{base case }P(0)&&&\\&&&\\f(0)&=0&&\text{by definition }(\ref{22.1})\\&=0^{2}&&\text{so }P(0)\text{ is true}\\&&&\\\text{inductive step }P(n)&\implies P(n+1)&&\\&&&\\P(n):f_{n}&=n^{2}&&\text{induction hypothesis}\tag{2}\label{22.2}\\&&&\\f_{n+1}&=2\cdot n-1+f_{n}&&\text{by definition }(\ref{22.1})\\&=2\cdot n-1+n^{2}&&\text{by }(\ref{22.2})\\&=(n+1)^{2}&&\text{so }P(n+1)\text{ is true}\\&&&\\P(n):f_{n}&=n^{2}&&\text{by induction}\tag*{\(\Box\)}\end{align}$$


Code

The following Lean program proves, by induction, that $f_{n}=n^{2}$.


-- 22 - Recursion

import Mathlib.Tactic

def f : ℕ → ℕ
  | 0 => 0
  | n + 1 => 2 * n + 1 + f n

#eval f 2

example {n: ℕ} : f n = n^2 := by
  induction n with
  | zero =>
    calc
      f 0 = 0 := by rw [f]
      _ = 0^2 := by norm_num
  | succ n ih =>
    calc
      f (n + 1) = 2 * n + 1 + f n := by rw [f]
      _ = 2 * n + 1 + n^2 := by rw [ih]
      _ = (n + 1)^2 := by ring

This code is in three parts. Let's look at each in turn.


Definition

The first part is the recursive definition of f.

The f : ℕ → ℕ declares f as a function which maps natural numbers to natural numbers. 

Even though the code is new, we can see how it maps 0 to 0, and n + 1 to 2 * n + 1 + f n. Here f n means “f applied to n”, which is just $f_{n}$.

Our earlier definition (1) mapped $n\mapsto2n-1+f_{n-1}$. We can't use $f_{n-1}$ in the Lean definition because $(n-1)$ does not exist for all natural numbers $n$. By replacing $n$ with $n+1$, we get the equivalent $n+1\mapsto2n+1+f_{n}$. 


Evaluation

The second part tests our function behaves as we expect.

The #eval is a special instruction which evaluates an expression and prints the answer in the Infoview. Just as #eval 2 + 3 will print 5, #eval f 2 will evaluate the function f applied to 2, and print the answer. Try evaluating f with other parameters.


Proof

The third part is the proof of the proposition f n = n^2

This proof by induction is not very different to the one we saw in the last chapter.

  • The base case needs to prove f 0 = 0 ^ 2. We do this with a calc section. The first step f 0 = 0 is justified by rw [f] which uses the first part of the recursive definition | 0 => 0.
  • The inductive step needs to prove f (n + 1) = (n + 1) ^ 2, and also uses a calc section. The first step which equates f (n + 1) with 2 * n + 1 + f n is similarly justified by rw [f], which this time uses the recursive part of the definition.


Recursion & Induction

We saw earlier the similarity between the induction process and a recursively defined function.

We now see the similarity between an induction proof and a recursively defined function.

The two are clearly linked, and it may not surprise you that inside Lean, induction and recursion are unified.


Infoview

Placing the cursor after #eval f 2 shows the result of the function f applied to 2.


4

Changing the code to #eval f 3 shows the result of the function f applied to 3.


9



Exercise

The following is a recursively defined function $g_{n}$, which maps natural numbers to integers,

$$g_{n}=\begin{cases}\:1 & \;\text{if }n=0\\\:(-1)\cdot g_{n-1} & \;\text{otherwise}\end{cases}$$

Write a recursive definition for $g_n$ in Lean, then show, by induction, that $g_{n}=(-1)^{n}$.