SkillGridLearn computer science by doing

Induction: the domino chain

Base case, inductive step, strong induction, and why the recursive algorithms you already write actually work.

15 min · 50 XP

A direct proof works when you can see the whole argument at once, like the case split in the previous lesson. It breaks down the moment the claim contains three dots. The sum one plus two plus three up to n has infinitely many instances, and no page holds infinitely many lines. Writing and so on is not a proof, it is a promise that a proof exists somewhere. Induction is the tool that keeps that promise with finitely many words, and it has exactly two parts.

claim: for every integer n >= 1,

  1 + 2 + 3 + ... + n  =  n * (n + 1) / 2

n = 1    1            = 1
n = 2    1 + 2        = 3
n = 3    1 + 2 + 3    = 6
n = 4    1+2+3+4      = 10

base case:      n = 1
inductive step: if it holds for n, it holds for n + 1

The base case is the claim with the smallest legal n substituted in, checked by hand. The inductive step is a conditional: assuming the claim holds for some n, show that it then holds for n plus one. Notice what the step does not do. It says nothing about whether the claim is true for any particular n. It is a machine that turns a truth at n into a truth at n plus one, and a machine with no input produces no output. Together the two parts give everything: the base case starts the chain, and the step is what carries it forward forever.

Base case n = 1Step n -> n + 1All n >= 1the first domino is downdomino n falls, so n + 1 fallsthe step alone proves nothingevery domino has a predecessornothing stops the chainno n is left standing

Here is why both halves are needed, and it is worth seeing a real failure of each kind rather than taking it on faith. First, the step alone. If you assume n plus one equals n and add one to both sides, you get n plus two equals n plus one, which is the same claim with n replaced by n plus one. The step is perfectly valid. The claim is still false for every n, because no first domino ever fell.

claim A: for every integer n >= 1,  n + 1 = n

step: n + 1 = n
      (n + 1) + 1 = n + 1          holds

base: n = 1  ->  1 + 1 = 1       false

step = T, base = F

Now the other failure: a base case with no working step. The polynomial below was genuinely believed to be prime for all n, and it passes at n equals one. It even survives all the way to n equals 39. At n equals 40 it factors, and the chain stops there. This is the failure mode that finds you in code: one hundred array indices all behave, and index zero or index n minus one was never in the range you tested.

claim B: for every integer n >= 1,  n^2 + n + 41  is prime

base: n = 1   ->  43              prime
step: n = 39  ->  1601            prime
      n = 40  ->  1681 = 41 * 41   composite

base = true, step broken at 39 -> 40

Sometimes assuming only the case n is not enough. If you split a number into two smaller factors, you need to know that both of them factor into primes, and both are smaller than n rather than equal to n minus one. Strong induction assumes the claim for every value below n, which gives you more to work with and is not actually a stronger requirement: anything provable by strong induction is provable by ordinary induction, just with a clumsier statement. The distinction matters in practice because strong induction is the correct shape for divide and conquer.

claim: every integer n >= 2 factors into primes

base:        n = 2 is prime
strong step: assume the claim for every k, 2 <= k < n

  n prime    ->  done
  n = a * b  ->  a < n and b < n
                 T(k) holds for 2 <= k < n

T(n) = T(a) and T(b)

That brings us to the reason this lesson sits in a programming course. Merge sort calls itself twice on halves and merges the results. Students usually accept that it works because they have seen it work. The inductive proof is the actual reason. Base case: an array of length zero or one is sorted, and the function returns it. Step: assume merge sort returns a sorted array for every input shorter than n. Then the two halves come back sorted, and merging two sorted arrays is sorted. By induction the function is correct for arrays of every length. This is not decoration: the base case below is exactly the condition in the if statement, and the step is exactly the correctness of merge.

def merge_sort(a):
    if len(a) <= 1:
        return a
    mid = len(a) // 2
    left = merge_sort(a[:mid])
    right = merge_sort(a[mid:])
    return merge(left, right)

# T(1) = c
# T(n) = 2 * T(n / 2) + O(n)
# depth = log2(n), work per level = O(n) -> O(n log n)Recursive merge sort. The base case of the induction is the first line, and the inductive step is the merge.

The same shape gives the running time, and this is where O(log n) finally comes from rather than being a shape you memorise from a graph. At every level of recursion the total work is n, and the number of levels is how many times you can halve n before reaching one element, which is log base 2 of n. Multiply the work per level by the number of levels and you get n log n. Binary search is the sharper case: it does constant work per level and throws away half the data, so the cost is exactly the number of halvings. Doubling the input adds one step, which is why a search over a million elements takes twenty comparisons and not a million. Induction is what makes that sentence true instead of merely observed.

/* sorted array, search space halved */

  1000000  ->  500000  ->  250000  ->  125000  ->  ...

  2^10  = 1024             10 halvings
  2^20  = 1048576          20 halvings

T(1) = 1
T(n) = T(n / 2) + c   ->   T(n) = O(log n)
/* loop invariant over a[0 .. n - 1] */

init:  i = 0,  m = a[0]
test:  while i < len(a):
step:    if a[i] > m: m = a[i]
         i = i + 1
exit:  i == len(a)  ->  m = max(a)

Practice

  1. Match each piece of an induction proof with what it actually does.

    Hints
    • One row is the single case you verify by hand; the others describe how truth travels from one n to the next.
    • A chain can fail either because nobody pushed the first domino, or because one domino cannot reach the next one.
  2. Put the induction proof of this claim in order. Claim: for every n at least 1, the sum of 1 through n equals n times n plus 1 divided by 2.

    1. 1inductive step: assume 1 + 2 + ... + n = n * (n + 1) / 2 for some n
    2. 2put the right side over a common denominator: (n * (n + 1) + 2 * (n + 1)) / 2
    3. 3add the next term to both sides: 1 + 2 + ... + n + (n + 1) = n * (n + 1) / 2 + (n + 1)
    4. 4the base case holds and the step is valid for every n at least 1, so the claim holds for all such n
    5. 5factor out (n + 1): (n + 1) * (n + 2) / 2, which is the claim for n + 1
    6. 6base case n = 1: the left side is 1 and the right side is 1 * 2 / 2 = 1
    Hints
    • The base case is the statement itself with the smallest legal n substituted in; the step begins by assuming exactly that statement for one n.
    • Never replace the sum with a closed formula before you have added the next term to the left-hand side.
  3. Someone claims that for every n at least 1 the sum of the first n odd numbers equals n squared plus n. They write: base case n = 2 gives 1 + 3 = 4 and 4 + 2 = 6, so the base case fails; inductive step, adding the next odd number to n squared plus n gives n squared plus n plus 2n plus 1, which is the claim for n plus 1. What is wrong with this argument?

    Hints
    • Check the claim by hand for n = 1, n = 2, n = 3 before you look at the two written steps.
    • One of the two steps is only valid for a particular value of n. Find that value and see whether the claim ever gets there.

Questions about this lesson

Why start with logic and sets?

Because everything else is built on it. Databases are sets, programs are boolean decisions, and "is this fast enough?" is the mathematics of growth. Skip it and later subjects feel like memorisation.

What does Big-O actually tell me?

How the work grows when the input grows. It is not a stopwatch: it tells you whether doubling your data doubles the time, or squares it. That difference decides whether your program survives real use.

Is it free?

Yes. The first level of every subject is free and needs no account. Deeper levels and the senior grades are paid — that is what keeps the project running.

What age is this for?

It is written for curious teenagers from about 12 and for adults starting from zero. Nothing assumes prior programming experience; lessons about networking and physics need no mathematics beyond school level.

Do I need to know programming already?

No. The logic, physics and networking tracks start from nothing. The languages track is where you actually write code, and it also starts from the beginning.

Do I need to install anything?

No. Everything runs in the browser: theory, practice and the interactive simulations. There is nothing to download and no environment to configure.

How long does one subject take?

A lesson takes 10 to 15 minutes. A junior grade is roughly 12 to 20 lessons, so expect a few weeks of steady practice rather than one evening.

Do I have to follow the order?

Within a grade, levels open one after another, because each builds on the previous one. Between subjects you are free: start with logic, physics or networking.