Mathematics for Computer Science 201-N11 • CEGEP, Montreal

Corrected exercises: induction and recursion (201-N11)

This is the corrected exercise set on mathematical induction and recursion for Mathematics for Computer Science, 201-N11, the discrete mathematics course taken at CEGEP by students in computer science programs (Computer Science Technology, and Computer Science and Mathematics): the reasoning that proves infinitely many statements at once, its translation into programs, and the cost analysis that follows from it.

The thread of the set fits in one sentence: a proof by induction has TWO steps, and leaving one out does not give an incomplete proof, it gives a wrong one. The same concern for rigour carries over to programs, where a missing base case turns a function into an infinite loop.

Part A builds the reasoning, its strong and two-step variants, then its translation into Python. Part B analyzes: exact solution of a linear recurrence, cost of binary search and merge sort, the Towers of Hanoi, and finally the criterion that tells whether a slow algorithm can be fixed by memoization or not at all. Every solution is on the page, with the Python code.

10 corrected exercises • 100 points • 150 minutes

This chapter is part of 201-N11, every chapter with its set and sheet Before you start Revision sheet: the traps and the method for this chapter

Self-checking set Type your answers and check them question by question: the page tells you right or wrong before you open the solution. With an account, every answer right on the first try earns points, and your right answers stay filled in when you come back.

Course recap

  • • INDUCTION: (1) BASE CASE, P(n0)P(n_{0}) is true; (2) INDUCTIVE STEP, for every k≥n0k\geq n_{0}, P(k)P(k) implies P(k+1)P(k+1). Then P(n)P(n) is true for every n≥n0n\geq n_{0}. The inductive step is an IMPLICATION: you never assume that P(k)P(k) is true outright.
  • • THE TWO TYPICAL FAILURES: an inductive step without a base case, such as “n=n+1n=n+1”, whose inductive step is valid and whose conclusion is false; checks without an inductive step, such as “n2+n+41n^{2}+n+41 is prime”, true up to 39 and false at 40.
  • • STRONG INDUCTION: the hypothesis covers ALL ranks up to kk, which you need as soon as you split an object into pieces of unknown sizes. TWO-STEP INDUCTION: you need TWO base cases, as many as there are earlier ranks in the relation.
  • • RECURSIVE FUNCTION: it terminates if it has a BASE CASE and if every call is made on a strictly smaller argument that eventually reaches it. The CALL STACK keeps one record per call in progress, hence the limit of about a thousand nested calls in Python.
  • • STANDARD COSTS: T(n)=T(n2)+1T(n)=T\left(\frac{n}{2}\right)+1 gives log⁡2n\log_{2}n, binary search. T(n)=2T(n2)+nT(n)=2T\left(\frac{n}{2}\right)+n gives nlog⁡2nn\log_{2}n, merge sort. T(n)=2T(n−1)+1T(n)=2T(n-1)+1 gives 2n−12^{n}-1, the Towers of Hanoi. T(n)=T(n−1)+T(n−2)+1T(n)=T(n-1)+T(n-2)+1 gives 2Fn+1−12F_{n+1}-1, naive Fibonacci.
  • • MEMOIZATION: when the subproblems OVERLAP, storing the values already computed brings an exponential cost down to a linear one. When they do not overlap, as in the Towers of Hanoi, the cost is intrinsic to the problem and nothing will improve it.
  • • CHARACTERISTIC EQUATION: for un+2=aun+1+bunu_{n+2}=au_{n+1}+bu_{n}, solve r2=ar+br^{2}=ar+b and write un=Ar1n+Br2nu_{n}=A r_{1}^{n}+B r_{2}^{n}, the initial conditions fixing AA and BB. On Fibonacci, this gives Binet's formula.

Part A: the basics (/50)

Exercise 1: Proof by induction: two steps, not one

Induction is the only tool that proves infinitely many statements in two lines. The thread of the set: a proof by induction has TWO steps, and dropping either one does not produce an incomplete proof, it produces a wrong one.

P(0)P(1)P(2)P(3)P(4)P(5)P(6)P(7)push
  • a) State the principle of mathematical induction, naming its two steps precisely.
  • b) Prove by induction that 1+2+⋯+n=n(n+1)21+2+\dots+n=\frac{n(n+1)}{2} for every n≥1n\geq 1.
  • c) What happens if you forget the BASE CASE? Illustrate with the property “n=n+1n=n+1”.
  • d) What happens if you forget the INDUCTIVE STEP? Illustrate with “n2+n+41n^{2}+n+41 is prime”.
  • e) On the figure, say what the initial push and the chain of dominoes represent.

Type your answers, the page tells you right or wrong 0/12

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) Base case at n0n_{0}, then inductive step: for every k≥n0k \ge n_{0}, P(k)P(k) implies P(k+1)P(k+1)
  • b) True at n=1n=1, and k(k+1)2+(k+1)=(k+1)(k+2)2\frac{k(k+1)}{2}+(k+1) = \frac{(k+1)(k+2)}{2}
  • c) The inductive step of “n=n+1n=n+1” is valid and the property is false everywhere: with no first domino, nothing falls
  • d) n2+n+41n^{2}+n+41 is prime from n=0n=0 to n=39n=39, then 402+40+41=1681=41240^{2}+40+41 = 1681 = 41^{2}
  • e) The initial push is the BASE CASE, the chain is the INDUCTIVE STEP
a)

Let P(n)P(n) be a property depending on an integer nn. If (1) BASE CASE: P(n0)P(n_{0}) is true for a first rank n0n_{0}; and (2) INDUCTIVE STEP: for every k≥n0k\geq n_{0}, P(k)P(k) true implies P(k+1)P(k+1) true; then P(n)P(n) is true for every n≥n0n\geq n_{0}. The inductive step is an implication, not an assertion: you never claim that P(k)P(k) is true, you assume that it is.

b)

BASE CASE at n=1n=1: the left side equals 1, the right side 1×22=1\frac{1\times 2}{2}=1 ✓. INDUCTIVE STEP: assume 1+2+⋯+k=k(k+1)21+2+\dots+k=\frac{k(k+1)}{2} for some k≥1k\geq 1. Then 1+2+⋯+k+(k+1)=k(k+1)2+(k+1)=(k+1)(k2+1)=(k+1)(k+2)21+2+\dots+k+(k+1)=\frac{k(k+1)}{2}+(k+1)=(k+1)\left(\frac{k}{2}+1\right)=\frac{(k+1)(k+2)}{2}, which is indeed the formula at rank k+1k+1 ✓. Numerical check at n=10n=10: 10×112=55\frac{10\times 11}{2}=55, and the sum is indeed 55.

c)

Without a base case, the inductive step alone proves nothing. Take P(n)P(n): “n=n+1n=n+1”. The inductive step is PERFECTLY VALID: if k=k+1k=k+1, then adding 1 to both sides gives k+1=k+2k+1=k+2, that is, P(k+1)P(k+1). The chain of implications is flawless, and yet P(n)P(n) is false for every nn, for lack of a first domino to push. This is the counterexample to remember, because it shows that a valid inductive step is worth nothing on its own.

d)

Without an inductive step, checking cases proves nothing either. The property “n2+n+41n^{2}+n+41 is prime” is true for n=0n=0, which gives 41, for n=1n=1, which gives 43, and in fact for every integer from 0 to 39. It is FALSE at n=40n=40, because 402+40+41=1681=41240^{2}+40+41=1681=41^{2}. Forty successive checks are not worth a proof: the inductive step is what replaces infinitely many checks.

e)

The initial push, the hand that knocks over the first domino, represents the BASE CASE. The chain, that is, the fact that each domino stands close enough to the next one to knock it over, represents the INDUCTIVE STEP. Without the push, the chain stays standing; without the chain, only the first domino falls. You need both, and the picture also says why: each of the two ingredients is useless without the other.

Tick the exercises you have done or want to review: a free account, no password, keeps your ticks and your right answers from one visit to the next, tells you which chapter to tackle next and lets you ask for the exercise you are missing. Create your space, an email is enough.

Exercise 2: Three families of induction proofs: sums, inequalities, divisibility

The mechanism is always the same, but the move in the inductive step changes with the family: you add a term, you bound, or you bring out a multiple.

  • a) Prove that 12+22+⋯+n2=n(n+1)(2n+1)61^{2}+2^{2}+\dots+n^{2}=\frac{n(n+1)(2n+1)}{6} for every n≥1n\geq 1.
  • b) Prove that 2n>n22^{n}>n^{2} for every n≥5n\geq 5. First check why the starting rank is not 1.
  • c) Prove that 32n−2n3^{2n}-2^{n} is divisible by 7 for every n≥1n\geq 1.
  • d) In each of the three proofs, say in one sentence where the induction hypothesis was used.
  • e) Why is a proof by induction that never uses the hypothesis suspicious?

Type your answers, the page tells you right or wrong 0/14

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) True at n=1n=1, and (k+1)(2k2+7k+6)6=(k+1)(k+2)(2k+3)6\frac{(k+1)(2k^{2}+7k+6)}{6} = \frac{(k+1)(k+2)(2k+3)}{6}
  • b) Rank 5 is necessary, 25=32>252^{5}=32 > 25, and the inductive step goes through 2k2≥(k+1)22k^{2} \ge (k+1)^{2}, true as soon as k≥3k \ge 3
  • c) 32(k+1)−2k+1=7(9m+2k)3^{2(k+1)}-2^{k+1} = 7(9m+2^{k}), and the check gives 729−8=721=7×103729-8 = 721 = 7 \times 103
  • d) It replaces the sum by its closed form, supplies the lower bound to multiply by 2, supplies the form 7m7m to factor out
  • e) Because you would then have proved P(k+1)P(k+1) directly: induction was useless, and a mistake is likely
a)

BASE CASE at n=1n=1: on the left 1, on the right 1×2×36=1\frac{1\times 2\times 3}{6}=1 ✓. INDUCTIVE STEP: assuming the formula at rank kk, the sum at rank k+1k+1 equals k(k+1)(2k+1)6+(k+1)2=(k+1)[k(2k+1)+6(k+1)]6=(k+1)(2k2+7k+6)6=(k+1)(k+2)(2k+3)6\frac{k(k+1)(2k+1)}{6}+(k+1)^{2}=\frac{(k+1)\left[k(2k+1)+6(k+1)\right]}{6}=\frac{(k+1)\left(2k^{2}+7k+6\right)}{6}=\frac{(k+1)(k+2)(2k+3)}{6}, which is the formula at rank k+1k+1 ✓. The decisive step is factoring 2k2+7k+62k^{2}+7k+6 as (k+2)(2k+3)(k+2)(2k+3): you find it by looking for the expected form, never by expanding at random. Check at n=5n=5: 1+4+9+16+25=551+4+9+16+25=55 and 5×6×116=55\frac{5\times 6\times 11}{6}=55 ✓.

b)

Rank 5 is necessary: at n=2n=2, 4>44>4 is false; at n=3n=3, 8>98>9 is false; at n=4n=4, 16>1616>16 is false. At n=5n=5: 32>2532>25 ✓, the base case holds. INDUCTIVE STEP: assume 2k>k22^{k}>k^{2} with k≥5k\geq 5. Then 2k+1=2×2k>2k22^{k+1}=2\times 2^{k}>2k^{2}. It remains to show that 2k2≥(k+1)22k^{2}\geq(k+1)^{2}, that is, k2−2k−1≥0k^{2}-2k-1\geq 0, which is true as soon as k≥3k\geq 3 since k2−2k−1=(k−1)2−2k^{2}-2k-1=(k-1)^{2}-2. Hence 2k+1>(k+1)22^{k+1}>(k+1)^{2} ✓. An inequality proved by induction almost always needs this extra bounding step.

c)

BASE CASE: 32−2=9−2=73^{2}-2=9-2=7, divisible by 7 ✓. INDUCTIVE STEP: assume 32k−2k=7m3^{2k}-2^{k}=7m for an integer mm. Then 32(k+1)−2k+1=9×32k−2×2k=9(32k−2k)+9×2k−2×2k=9×7m+7×2k=7(9m+2k)3^{2(k+1)}-2^{k+1}=9\times 3^{2k}-2\times 2^{k}=9\left(3^{2k}-2^{k}\right)+9\times 2^{k}-2\times 2^{k}=9\times 7m+7\times 2^{k}=7\left(9m+2^{k}\right), a multiple of 7 ✓. The trick is to force the expression of rank kk to appear by adding and subtracting the missing term. Check at n=3n=3: 729−8=721=7×103729-8=721=7\times 103 ✓.

d)

SUM: the hypothesis replaces the sum of the first kk terms by its closed form, which turns a sum into an algebraic expression. INEQUALITY: the hypothesis supplies the lower bound 2k>k22^{k}>k^{2}, which you multiply by 2. DIVISIBILITY: the hypothesis supplies the form 32k−2k=7m3^{2k}-2^{k}=7m, which you factor out. In all three cases, it serves to REPLACE an expression of rank kk by something known.

e)

Because if the hypothesis is never used, you have proved P(k+1)P(k+1) directly, without assuming anything: induction was therefore not needed, and it is far more likely that a mistake was made, typically assuming what had to be proved. The check is worth doing every time: reread the inductive step and put your finger on the exact place where the hypothesis was used. If you cannot find one, something is wrong.

Exercise 3: Strong induction and two-step induction

Some properties do not follow from the previous rank alone: you need two ranks back, or all the ranks at once. The principle adapts, provided the base case is adjusted as well.

  • a) State the principle of STRONG induction and say how it differs from the ordinary principle.
  • b) Prove by strong induction that every integer n≥2n\geq 2 has at least one prime divisor.
  • c) The Fibonacci sequence is defined by F0=0F_{0}=0, F1=1F_{1}=1 and Fn+2=Fn+1+FnF_{n+2}=F_{n+1}+F_{n}. How many base cases do you need to prove a property of this sequence, and why?
  • d) Prove that Fn<2nF_{n}<2^{n} for every n≥0n\geq 0.
  • e) Can a two-step proof with a base case at a single rank fail? Give an example on the sequence defined by u0=1u_{0}=1, u1=3u_{1}=3 and un+2=3un+1−2unu_{n+2}=3u_{n+1}-2u_{n}.

Type your answers, the page tells you right or wrong 0/12

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) You assume ALL the ranks up to kk, not rank kk alone; the two principles are equivalent
  • b) If nn is prime, it divides itself; otherwise n=abn=ab and a prime divisor of aa divides nn
  • c) TWO, at ranks 0 and 1, since the inductive step uses Fn+1F_{n+1} AND FnF_{n}
  • d) Fk+2<2k+1+2k=3×2k<2k+2F_{k+2} < 2^{k+1}+2^{k} = 3 \times 2^{k} < 2^{k+2}, and F10=55<1024F_{10} = 55 < 1024
  • e) Yes: with a single base case, you cannot even produce u2u_{2}. Here un=2n+1−1u_{n} = 2^{n+1}-1
a)

STRONG INDUCTION: if P(n0)P(n_{0}) is true, and if for every kk the fact that P(n0),P(n0+1),…,P(k)P(n_{0}),P(n_{0}+1),\dots,P(k) are ALL true implies P(k+1)P(k+1), then P(n)P(n) is true for every n≥n0n\geq n_{0}. The difference lies in the hypothesis: you no longer have only rank kk but ALL the ranks up to kk. The two principles are in fact logically equivalent, but the strong form is far more convenient as soon as you split an object into pieces of unknown sizes.

b)

BASE CASE: 2 is prime, so it is its own prime divisor ✓. STRONG INDUCTIVE STEP: let n≥3n\geq 3, and assume the property true for all integers from 2 to n−1n-1. Two cases. If nn is prime, it has nn as a prime divisor ✓. Otherwise, nn can be written n=abn=ab with 2≤a<n2\leq a<n, and the strong hypothesis applied to aa gives a prime divisor pp of aa; since pp divides aa, which divides nn, pp divides nn ✓. You can see why ordinary induction would not be enough: the factor aa has no reason to be equal to n−1n-1.

c)

You need TWO, at ranks 0 and 1. The reason is that the inductive step derives rank n+2n+2 from ranks n+1n+1 AND nn: to get started, you must therefore have two consecutive values already established. With a single base case, you could never compute F2F_{2}, for lack of one of the two terms it depends on.

d)

BASE CASES: F0=0<1=20F_{0}=0<1=2^{0} ✓ and F1=1<2=21F_{1}=1<2=2^{1} ✓. INDUCTIVE STEP: assume Fk<2kF_{k}<2^{k} and Fk+1<2k+1F_{k+1}<2^{k+1}. Then Fk+2=Fk+1+Fk<2k+1+2k=2k(2+1)=3×2k<4×2k=2k+2F_{k+2}=F_{k+1}+F_{k}<2^{k+1}+2^{k}=2^{k}\left(2+1\right)=3\times 2^{k}<4\times 2^{k}=2^{k+2} ✓. The final bound is generous, and that is deliberate: you prove what is asked, not the best possible result. Check: F10=55<1024=210F_{10}=55<1024=2^{10} ✓.

e)

Yes, it fails. This sequence satisfies un=2n+1−1u_{n}=2^{n+1}-1: we have u0=1u_{0}=1, u1=3u_{1}=3, u2=3×3−2×1=7u_{2}=3\times 3-2\times 1=7, u3=3×7−2×3=15u_{3}=3\times 7-2\times 3=15. Suppose you want to prove “unu_{n} is odd” with a base case at rank 0 only. The inductive step un+2=3un+1−2unu_{n+2}=3u_{n+1}-2u_{n} needs un+1u_{n+1} AND unu_{n}; at the step that produces u2u_{2}, you only have u0u_{0}, and the proof stops dead. This is not a fine point of write-up: the number of base cases must always equal the number of earlier ranks the relation depends on.

Exercise 4: From recursive sequences to recursive functions

A definition by recursion translates almost word for word into a function that calls itself. Two rules are enough for the program to stop: a base case, and an argument that decreases.

def factorielle(n):
    if n == 0:
        return 1
    return n * factorielle(n - 1)

def factorielle_iter(n):
    resultat = 1
    for k in range(2, n + 1):
        resultat = resultat * k
    return resultat

def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)
  • a) Write the factorial function in Python, in a recursive version and then in an iterative version.
  • b) What are the two conditions for a recursive function to terminate? Give an example that violates each of them.
  • c) Write the naive recursive version of the Fibonacci function.
  • d) What happens if this recursive factorial function is called with a negative argument? How do you fix it?
  • e) What is the call stack, and why does Python stop a program after about a thousand nested calls?

Type your answers, the page tells you right or wrong 0/11

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) n!=n×(n−1)!n! = n \times (n-1)! with 0!=10! = 1; 5!=1205! = 120 and 10!=3,628,80010! = 3{,}628{,}800 in both versions
  • b) A base case, and a strictly decreasing argument that reaches it
  • c) Two base cases and the relation Fn=Fn−1+Fn−2F_{n} = F_{n-1}+F_{n-2}: correct, but very slow
  • d) The base case is never reached: you need a guard that rejects negative inputs
  • e) The area that keeps local variables and return addresses; the limit of about 1000 is a safeguard
a)

The recursive version is a literal translation of the definition n!=n×(n−1)!n!=n\times(n-1)! with 0!=10!=1. The iterative version accumulates the product in a loop. Both compute the same thing; the recursive one is closer to the mathematical definition, the iterative one uses constant memory. Check: 5!=1205!=120 and 10!=3,628,80010!=3{,}628{,}800 in both cases.

b)

(1) There must be a BASE CASE, that is, at least one value for which the function answers without calling itself. (2) Every recursive call must be made on a STRICTLY SMALLER argument, one that eventually reaches the base case. Violating the first: a function written only as f(n)=f(n−1)f(n)=f(n-1) never stops. Violating the second: f(n)=f(n)+1f(n)=f(n)+1, or more insidiously f(n)=f(n−2)f(n)=f(n-2) called on an odd number when the base case is f(0)f(0), which always misses its target by one.

c)

The naive version translates the relation Fn=Fn−1+Fn−2F_{n}=F_{n-1}+F_{n-2} with two base cases. It is correct but catastrophically slow, for the reason examined in exercise 5: it recomputes the same values over and over.

d)

With a negative argument, the base case n=0n=0 is never reached: the calls go down to −1-1, −2-2, and so on, without end. Python eventually raises a maximum recursion depth error. FIX: add a guard at the start of the function that rejects negative inputs, for example by raising an exception. This is exactly the illustration of the second condition of question b: the argument does decrease, but it never meets the base case.

e)

The CALL STACK is the memory area where the interpreter keeps, for each call in progress, the local variables and the place to return to once the computation is finished. A recursion of depth nn stacks nn records there at the same time. By default Python sets a limit of about a thousand nested calls, not for lack of memory but as a SAFEGUARD: it turns an infinite recursion, which would crash the process in an opaque way, into a readable exception. You can raise this limit, but it is almost always a sign that a loop should have been written.

Exercise 5: The cost of a naive recursion

The recursive Fibonacci function fits in three lines and becomes unusable from n=40n=40 on. Counting its calls explains why, and suggests the fix.

543322121101010
memo = {}

def fib_memo(n):
    if n < 2:
        return n
    if n in memo:
        return memo[n]
    memo[n] = fib_memo(n - 1) + fib_memo(n - 2)
    return memo[n]
  • a) On the tree in the figure, count the calls generated by fib(5)\mathrm{fib}(5) and find how many times fib(2)\mathrm{fib}(2) is computed.
  • b) Let C(n)C(n) be the total number of calls. Write the relation satisfied by CC, then compute C(0)C(0) to C(6)C(6).
  • c) Show that C(n)=2Fn+1−1C(n)=2F_{n+1}-1, where FF is the Fibonacci sequence.
  • d) Compute C(30)C(30). How long would the computation take at a million calls per second?
  • e) Memoization means storing the values already computed. How many calls are left then?

Type your answers, the page tells you right or wrong 0/14

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) 15 calls, of which 3 for fib(2)\mathrm{fib}(2) and 5 for fib(1)\mathrm{fib}(1)
  • b) C(n)=1+C(n−1)+C(n−2)C(n) = 1 + C(n-1) + C(n-2); 1,1,3,5,9,15,251, 1, 3, 5, 9, 15, 25
  • c) C(n)=2Fn+1−1C(n) = 2F_{n+1}-1, by two-step induction
  • d) C(30)=2,692,537C(30) = 2{,}692{,}537 calls, that is, about 2.72.7 seconds
  • e) About 2n+12n+1, that is, 61 calls for n=30n=30: a gain by a factor of 44,000
a)

The tree has 15 nodes, so 15 calls in all. The value fib(2)\mathrm{fib}(2) appears in it THREE times, and fib(1)\mathrm{fib}(1) five times. These recomputations are not a programming flaw: they are built into the structure of the tree, where two branches coming out of the same node go down to subproblems that overlap.

b)

A call to fib(n)\mathrm{fib}(n) counts for itself, plus all the calls of its two subtrees: C(n)=1+C(n−1)+C(n−2)C(n)=1+C(n-1)+C(n-2) for n≥2n\geq 2, with C(0)=C(1)=1C(0)=C(1)=1. Computations: C(2)=1+1+1=3C(2)=1+1+1=3; C(3)=1+3+1=5C(3)=1+3+1=5; C(4)=1+5+3=9C(4)=1+5+3=9; C(5)=1+9+5=15C(5)=1+9+5=15, which confirms the figure; C(6)=1+15+9=25C(6)=1+15+9=25.

c)

By two-step induction. BASE CASES: 2F1−1=2×1−1=1=C(0)2F_{1}-1=2\times 1-1=1=C(0) ✓ and 2F2−1=2×1−1=1=C(1)2F_{2}-1=2\times 1-1=1=C(1) ✓. INDUCTIVE STEP: assuming the formula at ranks kk and k+1k+1, we have C(k+2)=1+C(k+1)+C(k)=1+(2Fk+2−1)+(2Fk+1−1)=2(Fk+2+Fk+1)−1=2Fk+3−1C(k+2)=1+C(k+1)+C(k)=1+\left(2F_{k+2}-1\right)+\left(2F_{k+1}-1\right)=2\left(F_{k+2}+F_{k+1}\right)-1=2F_{k+3}-1 ✓. Check: C(5)=2F6−1=2×8−1=15C(5)=2F_{6}-1=2\times 8-1=15 ✓.

d)

C(30)=2F31−1=2×1,346,269−1=2,692,537C(30)=2F_{31}-1=2\times 1{,}346{,}269-1=2{,}692{,}537 calls, that is, about 2.7 million. At a million calls per second, this takes about 2.72.7 seconds for a computation whose result, F30=832,040F_{30}=832{,}040, fits in six digits. At n=50n=50 it would take more than eleven hours, and at n=80n=80 some 2,400 years: the growth is like φn\varphi^{n}, where φ≈1.618\varphi\approx 1.618 is the golden ratio.

e)

With memoization, each value fib(k)\mathrm{fib}(k) is computed only ONCE, for kk from 0 to nn: the number of actual calls becomes linear, on the order of 2n+12n+1. For n=30n=30, that makes 61 calls instead of 2.7 million, a gain by a factor of 44,000. The lesson goes beyond Fibonacci: as soon as the subproblems OVERLAP, storing the results turns an exponential cost into a linear cost. This is the principle of dynamic programming.

Part B: problems and reasoning (/50)

Exercise 6: Solving a linear recurrence relation

A sequence defined recursively is computed step by step; we would like a direct formula. For linear recurrences with constant coefficients, such a formula exists, and it comes from a quadratic equation.

  • a) We look for the sequences satisfying un+2=un+1+unu_{n+2}=u_{n+1}+u_{n} in the form un=rnu_{n}=r^{n}. Write the equation rr must satisfy and solve it.
  • b) Deduce the general form of the solutions, then Binet's formula for the Fibonacci sequence.
  • c) Check this formula for n=10n=10.
  • d) Show that FnF_{n} is the integer nearest to φn5\frac{\varphi^{n}}{\sqrt 5}, where φ\varphi is the golden ratio, and explain why.
  • e) Apply the same method to un+2=3un+1−2unu_{n+2}=3u_{n+1}-2u_{n} with u0=1u_{0}=1 and u1=3u_{1}=3.

Type your answers, the page tells you right or wrong 0/14

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) r2−r−1=0r^{2}-r-1 = 0, with roots φ=1+52\varphi = \frac{1+\sqrt5}{2} and ψ=1−52\psi = \frac{1-\sqrt5}{2}
  • b) un=Aφn+Bψnu_{n} = A\varphi^{n}+B\psi^{n}, and Fn=φn−ψn5F_{n} = \dfrac{\varphi^{n}-\psi^{n}}{\sqrt5}
  • c) 122.9919−0.00812.2360680=55=F10\dfrac{122.9919-0.0081}{2.2360680} = 55 = F_{10}
  • d) ∣ψn/5∣<12\lvert \psi^{n}/\sqrt5 \rvert < \frac{1}{2} from n=1n=1 on: FnF_{n} is the integer nearest to φn/5\varphi^{n}/\sqrt5
  • e) r2−3r+2=0r^{2}-3r+2 = 0, roots 1 and 2, A=−1A=-1 and B=2B=2: un=2n+1−1u_{n} = 2^{n+1}-1
a)

Substituting un=rnu_{n}=r^{n} into the relation: rn+2=rn+1+rnr^{n+2}=r^{n+1}+r^{n}, and dividing by rnr^{n}, which is not zero, r2=r+1r^{2}=r+1, that is, r2−r−1=0r^{2}-r-1=0. The discriminant is 1+4=51+4=5, so the two roots are φ=1+52≈1.618034\varphi=\frac{1+\sqrt 5}{2}\approx 1.618034 and ψ=1−52≈−0.618034\psi=\frac{1-\sqrt 5}{2}\approx-0.618034. This equation is called the characteristic equation of the recurrence.

b)

Since the relation is LINEAR, every combination un=Aφn+Bψnu_{n}=A\varphi^{n}+B\psi^{n} still satisfies it, and one can show that there are no others. The two initial conditions fix AA and BB: F0=0F_{0}=0 gives A+B=0A+B=0, so B=−AB=-A; F1=1F_{1}=1 gives Aφ−Aψ=A5=1A\varphi-A\psi=A\sqrt 5=1, so A=15A=\frac{1}{\sqrt 5}. Hence BINET'S FORMULA: Fn=φn−ψn5F_{n}=\frac{\varphi^{n}-\psi^{n}}{\sqrt 5}. A remarkable fact: this expression full of square roots gives an integer for every nn.

c)

φ10≈122.9919\varphi^{10}\approx 122.9919 and ψ10≈0.0081\psi^{10}\approx 0.0081, so 122.9919−0.00812.2360680≈122.98372.2360680≈55.0000\frac{122.9919-0.0081}{2.2360680}\approx\frac{122.9837}{2.2360680}\approx 55.0000 ✓, which is indeed F10=55F_{10}=55. Computing directly with the relation gives the same value, as it should.

d)

The second term ψn5\frac{\psi^{n}}{\sqrt 5} has absolute value 0.618034n2.236\frac{0.618034^{n}}{2.236}, which is less than 12\frac{1}{2} from n=1n=1 on and goes to 0 very fast. The gap between FnF_{n} and φn5\frac{\varphi^{n}}{\sqrt 5} is therefore always less than one half, which means exactly that FnF_{n} is the nearest integer. Check at n=10n=10: 122.99192.2360680≈55.0036\frac{122.9919}{2.2360680}\approx 55.0036, whose nearest integer is indeed 55. This dominance of one root over the other also explains the growth like φn\varphi^{n} observed in exercise 5.

e)

Characteristic equation: r2=3r−2r^{2}=3r-2, that is, r2−3r+2=0r^{2}-3r+2=0, with roots r=1r=1 and r=2r=2. General form: un=A×1n+B×2n=A+B2nu_{n}=A\times 1^{n}+B\times 2^{n}=A+B2^{n}. Initial conditions: u0=A+B=1u_{0}=A+B=1 and u1=A+2B=3u_{1}=A+2B=3. Subtracting, B=2B=2, then A=−1A=-1. Hence un=2n+1−1u_{n}=2^{n+1}-1. Check: u0=1u_{0}=1, u1=3u_{1}=3, u2=7u_{2}=7, u3=15u_{3}=15 ✓, which confirms the conjecture stated in exercise 3. The procedure is identical; only the roots change.

Exercise 7: Divide and conquer: binary search

Some recursive algorithms do not have overlapping subproblems: they cut the problem in two and keep only one half. The cost then collapses instead of exploding.

def dichotomie(tableau, cible, debut, fin):
    if debut > fin:
        return -1
    milieu = (debut + fin) // 2
    if tableau[milieu] == cible:
        return milieu
    if tableau[milieu] > cible:
        return dichotomie(tableau, cible, debut, milieu - 1)
    return dichotomie(tableau, cible, milieu + 1, fin)
  • a) Describe binary search in a SORTED array and write its recursive version in Python.
  • b) Write the recurrence relation satisfied by the number T(n)T(n) of comparisons in the worst case, then solve it.
  • c) How many comparisons are needed at most for an array of a million elements? Compare with linear search.
  • d) Why does binary search not have the recomputation problem of exercise 5?
  • e) Merge sort satisfies T(n)=2T(n2)+nT(n)=2T\left(\frac{n}{2}\right)+n. Guess its cost and justify it with a tree argument.

Type your answers, the page tells you right or wrong 0/11

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) You compare with the middle and keep only one half; the array must be SORTED
  • b) T(n)=T(n/2)+1T(n) = T(n/2)+1 with T(1)=1T(1)=1, so T(n)≈log⁡2n+1T(n) \approx \log_{2}n + 1
  • c) 20 comparisons against a million: a ratio of 50,000
  • d) Because you CHOOSE one subproblem and drop the other: the tree is a simple path
  • e) nlog⁡2nn\log_{2}n: log⁡2n\log_{2}n levels, and nn elements handled at each level
a)

You compare the value you are looking for with the MIDDLE element. If they are equal, you are done. If it is smaller, you start again in the left half; otherwise in the right half. At each round, the search interval is divided by two, as the figure shows: 16 cells, then 8, then 4, then 2, then 1. The essential condition is that the array be SORTED; otherwise the comparison with the middle tells you nothing about the two halves.

b)

T(n)=T(n2)+1T(n)=T\left(\frac{n}{2}\right)+1, with T(1)=1T(1)=1: one comparison with the middle, then the same problem on one half. Unrolling: T(n)=1+1+⋯+1T(n)=1+1+\dots+1 as many times as you can divide nn by 2 before reaching 1, that is, log⁡2n\log_{2}n times. Hence T(n)≈log⁡2n+1T(n)\approx\log_{2}n+1, a LOGARITHMIC cost.

c)

log⁡2(106)=6ln⁡10ln⁡2≈19.93\log_{2}\left(10^{6}\right)=\frac{6\ln 10}{\ln 2}\approx 19.93, so 20 comparisons are enough. Linear search would need up to a million, that is, 50,000 times more. In other words, doubling the size of the array adds ONE comparison to binary search and a million to linear search: that is the difference in nature between logarithmic and linear.

d)

Because its two subproblems are never both explored: you CHOOSE one and drop the other. The call tree of Fibonacci branches in two at every node, hence its exponential size; that of binary search is a simple PATH of length log⁡2n\log_{2}n. So it is not recursion that is expensive, it is branching with overlap.

e)

The cost is nlog⁡2nn\log_{2}n. Tree argument: the call tree has log⁡2n\log_{2}n levels, since the size is divided by two at each step down. At EACH level, merging handles nn elements in total, shared among the calls of that level: nn at the top, two times n2\frac{n}{2} at the next level, four times n4\frac{n}{4} after that, always nn in total. The overall cost is therefore the number of levels times the work per level, that is, nlog⁡2nn\log_{2}n. For a million elements, that is about 20 million operations, against 101210^{12} for a selection sort: this is why all the sorts used in practice are recursive.

16 cells, check the 8th8 cells left4 cells2 cells, then 14 steps for 16 cells: the cost is the base-2 logarithm

Exercise 8: Five statements to correct

Each of the following five statements is FALSE. Say why and give the correct statement.

  • 1) “If the inductive step is proved, the property is true for every nn.”
  • 2) “I checked the property for nn from 1 to 100, so it is proved.”
  • 3) “In the inductive step, we assume that P(k)P(k) is true, which amounts to assuming what we want to prove.”
  • 4) “A recursive function is always slower than its iterative version.”
  • 5) “To prove a property of the Fibonacci sequence, a single base case is enough.”

Type your answers, the page tells you right or wrong 0/10

1)
2)
3)
4)
5)
Show the solution

Answers

  • 1) FALSE: the BASE CASE is missing, without which the chain of implications never starts
  • 2) FALSE: n2+n+41n^{2}+n+41 is prime up to n=39n=39 and stops being prime at n=40n=40
  • 3) FALSE: you prove the IMPLICATION, not P(k)P(k). An implication can be true with a false hypothesis
  • 4) FALSE: recursive binary search and merge sort are optimal. What is expensive is branching with overlap
  • 5) FALSE: TWO base cases, since the relation depends on two earlier ranks
1)

FALSE. The BASE CASE is missing, and without it the inductive step proves nothing. The property “n=n+1n=n+1” has a perfectly valid inductive step, since k=k+1k=k+1 implies k+1=k+2k+1=k+2, and yet it is false at every rank. The inductive step sets up a chain of implications; something still has to set it off.

2)

FALSE. A hundred checks are not worth a proof, and Euler's example shows it: n2+n+41n^{2}+n+41 is prime for nn from 0 to 39 and stops being prime at n=40n=40, where you get 1681=4121681=41^{2}. There are properties whose first counterexample is beyond a billion. The role of the inductive step is precisely to replace infinitely many checks, which no finite number of trials can do.

3)

FALSE, and it is the most stubborn misunderstanding of the chapter. You do NOT assume that P(k)P(k) is true: you prove the IMPLICATION “if P(k)P(k) then P(k+1)P(k+1)”. An implication can be true while its hypothesis is false, so nothing has been asserted about P(k)P(k). What you prove is a transmission mechanism; the base case, for its part, supplies a true starting point, and the two together give the conclusion.

4)

FALSE. Recursion is not expensive in itself: it is BRANCHING WITH OVERLAP that is. Recursive binary search costs log⁡2n\log_{2}n comparisons, exactly like its iterative version; recursive merge sort, in nlog⁡2nn\log_{2}n, beats every quadratic iterative sort. What makes naive fib\mathrm{fib} disastrous is that it recomputes the same values, and memoization brings it down to a linear cost without taking away its recursion.

5)

FALSE. The relation Fn+2=Fn+1+FnF_{n+2}=F_{n+1}+F_{n} involves TWO earlier ranks, so TWO base cases are needed, at ranks 0 and 1. With only one, you cannot even compute F2F_{2}. General rule: the number of base cases must equal the number of earlier ranks the relation depends on.

Exercise 9: Problem: the Towers of Hanoi

The Towers of Hanoi are the perfect example of a recursive definition that gives both the algorithm and its cost. They also show that an exponential cost can be unavoidable rather than the sign of a bad program.

ABC
def hanoi(n, depart, arrivee, intermediaire):
    if n == 0:
        return 0
    coups = hanoi(n - 1, depart, intermediaire, arrivee)
    coups = coups + 1
    return coups + hanoi(n - 1, intermediaire, arrivee, depart)
  • a) Recall the rules of the game, then describe the recursive strategy for moving nn disks from peg A to peg C.
  • b) Let H(n)H(n) be the number of moves. Write the recurrence relation and compute H(1)H(1) to H(5)H(5).
  • c) Prove by induction that H(n)=2n−1H(n)=2^{n}-1.
  • d) The legend tells of 64 disks moved by monks at the rate of one per second. How long would it take?
  • e) Can you do better than 2n−12^{n}-1? What does this problem say about the relationship between recursion and efficiency?

Type your answers, the page tells you right or wrong 0/13

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) One disk at a time, never on a smaller one; move n−1n-1 disks to B, the large one to C, then n−1n-1 from B to C
  • b) H(n)=2H(n−1)+1H(n) = 2H(n-1)+1 with H(1)=1H(1)=1: 1,3,7,15,311, 3, 7, 15, 31
  • c) H(k+1)=2(2k−1)+1=2k+1−1H(k+1) = 2(2^{k}-1)+1 = 2^{k+1}-1
  • d) 264−1=18,446,744,073,709,551,6152^{64}-1 = 18{,}446{,}744{,}073{,}709{,}551{,}615 moves, that is, about 585 billion years
  • e) NO: 2n−12^{n}-1 is the minimum. The exponential cost lies in the problem, not in the algorithm
a)

RULES: you move one disk at a time, taking it from the top of a peg, and you never put a disk on a smaller one. STRATEGY: to move nn disks from A to C, first move the top n−1n-1 disks from A to B, using C as the intermediate peg; then move the large disk from A to C, the only direct move; finally move the n−1n-1 disks from B to C, using A. The base case is n=1n=1, where you move the single disk (the code below stops one step earlier, at n=0n=0, where there is nothing to move and the count is 0).

b)

The strategy gives directly H(n)=2H(n−1)+1H(n)=2H(n-1)+1 with H(1)=1H(1)=1. Computations: H(2)=3H(2)=3; H(3)=7H(3)=7; H(4)=15H(4)=15, the case of the figure; H(5)=31H(5)=31. You recognize the sequence 2n−12^{n}-1, which the next question proves.

c)

BASE CASE: H(1)=1=21−1H(1)=1=2^{1}-1 ✓. INDUCTIVE STEP: assume H(k)=2k−1H(k)=2^{k}-1. Then H(k+1)=2H(k)+1=2(2k−1)+1=2k+1−2+1=2k+1−1H(k+1)=2H(k)+1=2\left(2^{k}-1\right)+1=2^{k+1}-2+1=2^{k+1}-1 ✓. The formula is therefore established for every n≥1n\geq 1. Notice that the proof follows exactly the structure of the algorithm: this is typical of recursive problems, the proof and the program look alike.

d)

H(64)=264−1=18,446,744,073,709,551,615H(64)=2^{64}-1=18{,}446{,}744{,}073{,}709{,}551{,}615 moves. At one per second, that is about 5.85×10115.85\times 10^{11} years, that is, nearly 585 billion years. For comparison, the age of the Universe is estimated at about 13.8 billion years: the monks of the legend would therefore need a time forty times longer. It is the best teaching argument there is against the idea that a faster computer solves everything.

e)

NO, you cannot do better: one proves that 2n−12^{n}-1 is the MINIMUM number of moves, because the large disk must be moved at least once, and for that the n−1n-1 others must all be stacked somewhere else. The exponential cost is therefore not the fault of the recursive algorithm, it lies in the PROBLEM itself. This is the fundamental difference with naive Fibonacci, whose exponential cost came from the method and disappeared with memoization. Before optimizing a slow program, you must therefore ask yourself which of the two cases you are looking at.

Exercise 10: Problem: recognizing the right tool for a recursive algorithm

Five recursive algorithms, five radically different costs. Knowing which one you are looking at before writing a line of code is the skill this chapter should leave you with.

2468101214161820510152025303540number of callsn
  • a) Rank by increasing cost: binary search, memoized Fibonacci, the Towers of Hanoi, merge sort, naive Fibonacci.
  • b) For each one, write the recurrence relation for the cost.
  • c) On the figure, match each curve with one of these algorithms and say which one becomes unusable first.
  • d) A recursive algorithm is slow. Give the question to ask first, and the two possible answers with their remedy.
  • e) A student replaces a recursive function by a loop and finds that the program is just as slow. What should be concluded?

Type your answers, the page tells you right or wrong 0/13

a)
b)
c)
d)
e)
Show the solution

Answers

  • a) Binary search log⁡2n\log_{2}n, memoized Fibonacci nn, merge sort nlog⁡2nn\log_{2}n, naive Fibonacci φn\varphi^{n}, Hanoi 2n2^{n}
  • b) T(n/2)+1T(n/2)+1; T(n−1)+1T(n-1)+1; 2T(n/2)+n2T(n/2)+n; T(n−1)+T(n−2)+1T(n-1)+T(n-2)+1; 2T(n−1)+12T(n-1)+1
  • c) The flattest is binary search, the straight line is the memoized version, the vertical one is naive Fibonacci, unusable from n=5n=5
  • d) Do the subproblems overlap? Yes: memoization. No: the cost is intrinsic to the problem
  • e) That the problem did not come from recursion: with the same scheme, loop and recursion perform the same number of operations
a)

From least to most expensive: BINARY SEARCH in log⁡2n\log_{2}n; MEMOIZED FIBONACCI in nn; MERGE SORT in nlog⁡2nn\log_{2}n; then, far behind, NAIVE FIBONACCI in φn\varphi^{n} and HANOI in 2n2^{n}, both exponential, Hanoi being the most expensive since 2>φ2>\varphi.

b)

BINARY SEARCH: T(n)=T(n2)+1T(n)=T\left(\frac{n}{2}\right)+1. MEMOIZED FIBONACCI: T(n)=T(n−1)+1T(n)=T(n-1)+1, each value being computed only once. MERGE SORT: T(n)=2T(n2)+nT(n)=2T\left(\frac{n}{2}\right)+n. NAIVE FIBONACCI: T(n)=T(n−1)+T(n−2)+1T(n)=T(n-1)+T(n-2)+1. HANOI: T(n)=2T(n−1)+1T(n)=2T(n-1)+1. Two parameters decide everything, the number of recursive calls and the way the size decreases: divided by two, the cost stays modest; reduced by 1 with two calls, it explodes.

c)

The flattest curve is the logarithm, so BINARY SEARCH. The straight line is the linear cost, so MEMOIZED FIBONACCI. The curve that shoots up vertically is the power of the golden ratio, so NAIVE FIBONACCI. It is the one that becomes unusable first: on the figure, it already overtakes the other two from n=5n=5 on (φ5≈11.1\varphi^{5}\approx 11.1 against 1010 for the line), whereas the difference between logarithmic and linear stays negligible up to enormous sizes. Exponential growth catches up with any polynomial growth, and it always does so sooner than you think.

d)

The question to ask is: do the subproblems OVERLAP? FIRST ANSWER, yes, as in naive Fibonacci where fib(2)\mathrm{fib}(2) is recomputed three times for n=5n=5: the remedy is MEMOIZATION, which brings the cost down to linear without changing the structure of the program. SECOND ANSWER, no, as in the Towers of Hanoi where each call handles a new configuration: the cost is then intrinsic to the problem, no memory will improve it, and you must either change the problem or accept an approximate solution.

e)

That the problem did not come from recursion. A loop and a recursion that follow the SAME computation scheme perform the same number of operations: the rewrite only changes how the stack is managed, which saves memory and a little constant time, never an order of magnitude. If the program stays slow, the cost lies in the algorithm itself, and you should go back to question d rather than keep rewriting the same method in another form.

Something missing from this set? A type of exercise your teacher gives, a problem you are stuck on, a mistake you spotted: tell me, I read every request.

Sending goes through your free space: that is how I can answer you and tell you when the exercise is added. An email is enough, no password. Create your space. Rather not create an account? Write to me directly.

What is it about?
I read every request myself, nobody else sees it. No need to write your name or your school.
When your request is dealt with, you hear about it by email and in your space.

By sending, you agree that I read your request to answer it. It is kept with your account, erased with it, and never published: privacy policy.

Previous chapter Injective, surjective and bijective functions, 201-N11 Next chapter Introduction to graphs and matrices, 201-N11

© Ahmed Squalli Houssaini. Exercise set published at www.letuteurscientifique.ca/en/exercices/cegep-n11-induction-recursion. Free for personal and classroom use; republishing it elsewhere requires written permission (legal notice).

See also

Looking for a discrete mathematics tutor in Montreal?

Get in touch for a first session. We work on induction and recursion at the real level of the tests, from writing an inductive step to analyzing the cost of an algorithm.

Site by Studio Squalli