You are on page 1of 30

Recursion and induction

Rahul Narain

COL100: Introduction to Computer Science


I Semester, 2021-22
Assignment 1 part 1

Digital electronic devices (computers, phones, calculators, . . . ) are just big


circuits that manipulate voltages. What is special is that the voltages can
only take two values: high and low.
In this assignment we will model these two states using Boolean values:
True and False.
The goal is for you to get a sense of
I how digital electronics can perform more complex tasks such as
arithmetic, and
I how complex programs can be designed by composing (putting
together) simpler operations (e.g. the logical operators and, or, not).
Binary numerals
0 0
1 1
2 10
3 11

...
....
....
4
5
100
101
... 6
7
110
111
8 1000
In decimal, ten digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 9 1001
#dots = “14” = 1 ten + 4 ones 10 1010
11 1011
In binary, only two digits: 0, 1 12 1100
#dots = “1110” = 1 eight + 1 four + 1 two + 0 ones 13 1101
14 1110
15 1111
16 10000
.. ..
. .
1 1 1 0 1 1
9 1 0 0 1
1 10 1
0 0 1 1
0
2 0 1 0 1 0 0

What you will actually implement:

>>> add4(True,True,False,True, True,False,False,True, False)


(False,False,True,False,True)
Recursion

Recall: recursion is when we define a function in terms of itself.


Base case(s): do not need any recursive calls
Recursive case(s): defined in terms of arguments that are “smaller” in
some sense (i.e. closer to base case)

(
0 if b = 0,
a×b =
a + a × (b − 1) otherwise.

(
1 if n = 0,
n! =
(n − 1)! × n otherwise.
Last exercise: computing x n
(
1 if n = 0,
power(x; n) =
x · power(x; n − 1) otherwise.

def power(x,n):
# Raises a number to a nonnegative integer power.
if n == 0:
return 1
else:
return x * power(x, n-1)
Bisection

What’s the key idea?


I We know that the solution we are looking for is in a particular range.
I We can check a point inside that range to determine whether the
solution is to the left or right of it.
I So, we can make our range smaller, and repeat the process. . .
recursively!

Let’s apply this idea to a different problem.


Integer square root


Problem: Given a natural number n, find the natural number k = b nc,
i.e. the largest natural number such that k 2 ≤ n.


b 0c = 0
√ √ √
b 1c = b 2c = b 3c = 1
√ √ √
b 4c = b 5c = · · · = b 8c = 2

Can we solve this using bisection?



Find b nc, i.e. the largest k ∈ N such that k 2 ≤ n.
I We need a range that k must be in, . . .
We know k ∈ {a; a + 1; : : : ; b} with a = 0; b = n.

I . . . a way to choose a point within the range, . . .


We can always pick the midpoint, m = b(a + b)=2c.

I . . . a way to tell if the desired k is above or below that point, . . .


If m2 < n, then k ∈ {m; m + 1; : : : ; b}. If m2 > n, then
k ∈ {a; a + 1; : : : ; m − 1}.

I . . . and a way to know when to stop.


If m2 = n, or a = b, then k = m.
isqrt(n) = bisect(0; n; n);
8
<m
> if m2 = n or a = b,
bisect(a; b; n) = bisect(m; b; n) if m2 < n,
>
bisect(a; m − 1; n) if m2 > n,
:

where m = b(a + b)=2c.


def bisect(a, b, n):
# The largest natural number k between natural
# numbers a and b (inclusive) such that k^2 <= n.
m = (a + b)//2
if m*m == n or a == b:
return m
elif m*m < n:
return bisect(m, b, n)
else:
return bisect(a, m-1, n)

def isqrt(n):
# The integer square root of a given natural number.
return bisect(0, n, n)
Actually, this algorithm doesn’t work!! Try evaluating isqrt(1).

What is going wrong?


Can you fix it?


Hint: k is exactly equal to b nc if and only if k 2 ≤ n and √ ...?
Otherwise, it is either strictly less or strictly greater than b nc.
Practice exercises

I Design a corrected version of the bisection algorithm for the integer


square root, implement it in Python, and test it on several inputs.
I Consider a number-guessing game: I think of a number between 1
and 100, and you try to guess it. After each guess I tell you whether
your guess is greater than, less than, or equal to my number. Your
goal is to find my number using as few guesses as possible.
Use the input function to make a program that plays this game for
you (as the guesser!). After each guess your program prints, you
could ask the user to type G, L, or E, and then check what the user
has entered using ==.
Correctness of algorithms

As we have seen, sometimes we try to design a plausible-seeming algorithm


and it doesn’t even terminate.
Even if it terminates, it might not give the right answer.
Only after we ensure termination and correctness can we worry about
efficiency!
How to prove these things?
(
1 if n = 0,
power(x; n) =
x · power(x; n − 1) otherwise.

Does the recursive evaluation of power(x; n) terminate for every nonzero


x ∈ R and every n ∈ N?
Does it always produce the correct result, x n ?

OK, maybe this one seems obvious, but. . .


8
<1
> if n = 0,
fastPower(x; n) = fastPower(x · x; bn=2c) if n is even,
>
x · fastPower(x · x; bn=2c) if n is odd.
:

Does fastPower(x; n) always terminate for every nonzero x and every n?


Does it always produce x n ?
How can we prove these claims?

(P.S. Also, why do I call it “fastPower”? That’s for a later lecture.)


The principle of mathematical induction
How to prove a statement about infinitely many natural numbers?
e.g. “Prove that n3 + 2n is divisible by 3 for all n ∈ N.”

Maybe I can write an infinitely long proof? 03 + 2 × 0 = 0 which is


divisible by 3, and 13 + 2 × 1 = 3 which is divisible by 3, and
23 + 2 × 2 = 12 which is divisible by 3, and . . .
No good, it will take too long.
The principle of mathematical induction

A property P holds for all natural numbers if


I (base case:) P holds for 0, and
I (induction step:) if P holds for an arbitrary n ∈ N, then it also holds
for n + 1.
Example: Prove that 1 + 2 + · · · + n = 12 n(n + 1).

1
Base case: For n = 0, LHS is 0 and RHS is 2
· 0 · 1 = 0.
Induction step: Assume 1 + 2 + · · · + n = 12 n(n + 1) (induction
hypothesis).
We have to show that 1 + 2 + · · · + n + (n + 1) = 12 (n + 1)(n + 2).

1 + 2 + · · · + n +(n + 1) = 12 n(n + 1) + (n + 1)
| {z }
= 12 (n + 1)(n + 2):

By induction, the result follows.


Why is this valid?

I The base case proves it for 0.


I Because it’s true for 0, the
induction step proves it for 1.
I Because it’s true for 1, the
induction step proves it for 2.
I Because it’s true for 2, the
induction step proves it for 3.
I ...

So for any particular n, I can write an (n + 1)-line proof proving it for n.

Induction lets us conclude the property is true for all n.


Example: Prove that n3 + 2n is divisible by 3 for all n in N.
Base case: Prove that 03 + 2 × 0 is divisible by 3. (trivial)
Induction step: Assume that n3 + 2n is divisible by 3. Prove that
(n + 1)3 + 2(n + 1) is divisible by 3.

Complete this proof yourself.


Strong induction

A property P holds for all natural numbers if


I (base case:) P holds for 0, and
I (induction step:) if P holds for all m ≤ n for an arbitrary n ∈ N, then
it also holds for n + 1.

Equivalent version of induction step: if P holds for all m < n for an


arbitrary n ≥ 1, then it also holds for n.

“Strong” doesn’t mean it can prove more things than “weak” induction,
only that we are allowed to use a stronger hypothesis in the induction step.
(Actually both versions of induction are equivalent!)
Example: Recall the Fibonacci numbers, defined by

F0 = 0;
F1 = 1;
Fn = Fn−1 + Fn−2 for all n ≥ 2:


1+ 5
Let ffi = 2
≈ 1:618 : : : , which has the property that ffi2 = ffi + 1.
Show that Fn ≤ ffin−1 for all n ≥ 0.
Base case: F0 = 0 ≤ ffi−1 .
Induction hypothesis: For some n ≥ 1, Fm ≤ ffim−1 for all 0 ≤ m < n.
Induction step: We need to show that Fn ≤ ffin−1 .

Fn = Fn−1 + Fn−2
≤ ffin−2 + ffin−3
= ffin−3 (ffi + 1)
= ffin−3 ffi2
= ffin−1 :

Is this valid? (. . . What if n = 1?)


Correctness of algorithms

Integer division is usually defined in terms of multiplication:


If n ≥ 0 and d > 0 are two natural numbers, then there is a unique pair of
natural numbers q ≥ 0 and 0 ≤ r < b such that n = q × d + r . These are
called the quotient and remainder respectively.
This is a definition, but not an algorithm: it does not specify the steps to
find q and r .
We will have to design an algorithm ourselves . . . and then prove that it
matches the definition!
(
0 if n < d,
div(n; d) =
div(n − d; d) + 1 otherwise.

mod(a; b) = . . . do it yourself ^
¨

Claim: For all natural numbers n ≥ 0 and d > 0, div(n; d) computes the
quotient when n is divided by d. That is, if q = div(n; d), then
n = q × d + r for some 0 ≤ r < b.
We will prove this by induction. . . on which variable?
Base case: If n = 0, then q = 0, so n = q × d + r for r = 0.
Induction hypothesis: For all 0 ≤ m < n, if q = div(m; d) then
m = q × d + r for some 0 ≤ r < d.
Induction step:
If n < d, then q = div(n; d) = 0, so n = q × d + r for r = n.
If n ≥ d, then q = div(n; d) = div(n − d; d) + 1.
Let div(n − d; d) = q 0 , so q = q 0 + 1. By the induction hypothesis,
n − d = q 0 × d + r for some 0 ≤ r < d. So

n = (q 0 × d + r ) + d
= (q 0 + 1) × d + r
=q×d +r

with 0 ≤ r < d as desired.


Practice exercises

I Prove the correctness of your modified bisection algorithm from slide


14.
I Analogous to how fastPower works, design an algorithm fastMul(a; b)
for integer multiplication using repeated addition, in which the
recursive call involves bb=2c instead of b − 1. Implement your
algorithm in Python and test it out.
(Use b%2 to check if b is even, and b//2 to compute bb=2c. Do you
see why these operations are extremely easy if b is represented in
binary?)

You might also like