Recursion - Università di Trento

Module 2: Algorithm and Data Structures
Recursion
Alberto Montresor
Università di Trento
2016/11/03
This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License.
Table of contents
1
2
3
Introduction
Hanoi’s Tower
An example problem
Introduction
Goal of this lecture
To understand that complex problems that may otherwise be
difficult to solve, may be solved by splitting them in sub-problems
To learn how to formulate programs recursively
To understand and apply the three laws of recursion
To implement the recursive formulation of a problem
To understand how recursion is implemented by a computer system
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
1 / 21
Introduction
Recursion
Definition
Recursion is a method of solving problems that involves reducing a
problem into a smaller one, until you get a problem small enough that
it can be solved trivially.
Recursion often involves a function that invokes itself
Several mathematical functions are defined recursively
Several problems can be defined recursively
Some problems may be solved more efficiently with a recursive
approach
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
2 / 21
Introduction
Example: Factorial numbers
(
1
n≤1
n! =
n · (n − 1)! n > 1
int factorial(int n)
if n ≤ 1 then
return 1
else
return n · factorial(n − 1)
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
3 / 21
Introduction
Example: Factorial numbers
n=1
n=2
n=3
n=4
n=5
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
4 / 21
Introduction
Example: Factorial numbers
def factorial(n):
print("Start ", n)
if n < 2:
print("End ", 1)
return 1
else:
res = n*factorial(n-1)
print("End ", n)
return res
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
5 / 21
Introduction
The three laws of recursion
All recursive algorithms must obey three important laws
A recursive algorithm must have a base case
A recursive algorithm must call itself, recursively
A recursive algorithm must move toward the base case
What happens with this code?
factorial(int n)
return n · factorial(n − 1)
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
6 / 21
Introduction
Minimum – Recursive version 1
This function returns the minimum of the elements included between i
and j.
minrec(int[ ] A, int i, int j)
if i = j then
return A[i]
else
m ← b(i + j)/2c
return min(minrec(A, i, m), minrec(A, m + 1, j))
This function is a wrapper for the first function:
int min(int[ ] A)
return minrec(A, 0, len(A) − 1)
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
7 / 21
Introduction
Minimum – Recursive version 2
This function returns the minimum of the elements included in the slice
A[0 : i].
minrec(int[ ] A, int i)
if i = 1 then
return A[0]
else
return min(minrec(A, i − 1), A[i − 1])
This function is a wrapper for the first function:
int min(int[ ] A)
return minrec(A, len(A))
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
8 / 21
Introduction
Binary search – Recursive version
int binarySearch(int[ ] L, int v, int i, int j)
if i > j then
return −1
else
int m = b(i + j)/2c
if L[m] = v then
return m
else if L[m] < v then
return binarySearch(L, v, m + 1, j)
else
return binarySearch(L, v, i, m − 1)
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
9 / 21
Introduction
Why recursion?
So far, you probably don’t see the advantages of recursion...
Factorial can be defined recursively, but also "iteratively": n! =
Qn
i=1 i
int factorial(int n)
int fact = 1
for i = 2 to n do
fact = fact · i
return fact
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
10 / 21
Introduction
Why recursion?
So far, you probably don’t see the advantages of recursion...
Sometime there is no real advantage in execution time for the recursive
versions
Both the recursive and iterative versions of mimimum() are O(n)
Both the recursive and iterative versions of lookup() are O(n)
Executing the recursive invocations is more costly than executing
the iterations
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
11 / 21
Introduction
Why recursion?
So far, you probably don’t see the advantages of recursion...
Recursion may even be dangerous
def minrec(L):
if len(L) == 1:
return L[0]
else:
return min(L[0], minrec(L[1:])
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
12 / 21
Introduction
Divide-et-impera
Three phases
Divide: Break the problem in smaller and independent
sub-problems
Impera: Solve the sub-problems recursively
Combine: "merge" the solutions of subproblems
There is not a unique recipe for divide-et-impera
Mergesort: "divide" trivial, "combine" complex
Quicksort: "divide" complex, no "combine"
A creative effort is required
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
13 / 21
Hanoi’s Tower
Hanoi’s tower
Mathematical game
Three pins
n disks with different sizes
Initially, all the disks are stacked in
decreasing size order (from bottom to
top) on the left pin
https://it.wikipedia.org/wiki/File:
Tower_of_Hanoi.jpeg
Goal of the game
Stack all the disks on the right pin in decreasing size order (from
bottom to top)
Never put a larger disk on top of a smaller disk
You can move one disk at each step
You can use the middle pin to as support
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
14 / 21
Hanoi’s Tower
Hanoi’s tower
hanoi(int n, int src, int dest, int middle)
if n = 1 then
print src → dest
else
hanoi(n − 1, src, middle, dest)
print src → dest
hanoi(n − 1, middle, dest, src)
Divide-et-impera
n − 1 disks from src to middle
1 disks from src to dest
n − 1 disks from middle to
dest
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
15 / 21
Hanoi’s Tower
Hanoi’s tower
hanoi(int n, int src, int dest, int middle)
if n = 1 then
print src → dest
else
hanoi(n − 1, src, middle, dest)
print src → dest
hanoi(n − 1, middle, dest, src)
This solution is the best one (can be proven)
Number of steps: T (n) = O(2n )
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
16 / 21
An example problem
Gap
Gap
In a list L containing n ≥ 2 integers, a gap is an index i, 0 < i < n,
such that L[i − 1] < L[i].
Prove that if n ≥ 2 and L[0] < L[n − 1], L contains at least one gap
Design an algorithm that, given a list L containing n ≥ 2 integers
such that L[0] < L[n − 1], finds a gap in the list.
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
17 / 21
An example problem
Gap
By contradiction:
Suppose there is no gap in the list.
Then L[0] ≥ L[1] ≥ L[2] ≥ . . . L[n − 1], which contradicts the fact
that L[0] < L[n − 1].
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
18 / 21
An example problem
Gap
gap(int[ ] L, int n)
for i = 1 to n − 1 do
if L[i − 1] < L[i] then
return i
return −1
def gap(L):
for i in range(1,len(L)):
if L[i-1] < L[i]:
return i
return -1
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
19 / 21
An example problem
Gap
Consider two indexes i < j, such that L[i] < L[j]. Let’s consider the
index m in the middle between i and j. Consider the following cases:
If i = j − 1, then the gap is in j
If L[m] ≤ L[i] < L[j], then there is a gap between m and j;
If L[i] < L[m], then there is a gap between i and m;
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
20 / 21
An example problem
Gap
gap(int[ ] L, int i, int j)
if j == i + 1 then
return j
m = b(i + j)/2c
if L[m] ≤ L[i] then
return gap(L, m, j)
else
return gap(L, i, m)
Alberto Montresor (UniTN)
ASD - Algorithm analysis
2016/11/03
21 / 21