0% found this document useful (0 votes)
11 views96 pages

Chapter 6 Recursion

Chapter 6 of the COMP1117A course covers recursion, including its definition and applications in computing factorials, palindromes, Pascal's triangle, Fibonacci numbers, and MergeSort. It contrasts iterative and recursive approaches, emphasizing the importance of base cases and the divide-and-conquer strategy in recursion. The chapter provides examples and code implementations to illustrate how recursive functions operate and how they can be used to solve problems.

Uploaded by

yangreiny
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views96 pages

Chapter 6 Recursion

Chapter 6 of the COMP1117A course covers recursion, including its definition and applications in computing factorials, palindromes, Pascal's triangle, Fibonacci numbers, and MergeSort. It contrasts iterative and recursive approaches, emphasizing the importance of base cases and the divide-and-conquer strategy in recursion. The chapter provides examples and code implementations to illustrate how recursive functions operate and how they can be used to solve problems.

Uploaded by

yangreiny
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 96

Chapter 6.

Recursion

2022-2023
COMP1117A Computer Programming
Dr. T.W. Chim ([email protected]) & Dr. H.F. Ting ([email protected])
Department of Computer Science, The University of Hong Kong
We are going to learn…
Recursion and memorization
Understand the recursive strategy to tackle the following problems
Factorial
Palindrome
Pascal triangle
Fibonacci number
MergeSort

2
Section 6.1

Recursion

2022-2023
COMP1117A Computer Programming
Dr. T.W. Chim ([email protected]) & Dr. H.F. Ting ([email protected])
Department of Computer Science, The University of Hong Kong
Recursion
A function definition may contain a call to the function being defined.
In such cases, the function is said to be recursive.

Please implement a program


that reads in an integer n, and
outputs n! (factorial)
e.g., 5! = 5*4*3*2*1 = 120
4
Iterative v.s. recursive
720
Option 1.
Use an iterative def factorialIterative(n):
approach to compute result = 1
the factorial of n, i.e. n! for i in range(2,n+1):
result *= i
return result

print(factorialIterative(6))

5
Iterative v.s. recursive
For recursive approach, we need the following: Option 2 :
Base case – the smallest problem with solution available. Another option is
E.g., 0! = 1. to use a recursive
Divide and conquer – breaking a larger problem into approach to
smaller problem(s). compute n!

E.g., n! = n * (n - 1)!
Larger problem Smaller problem
6
Iterative v.s. recursive
def f(n):
if n == 0: Base case
return 1
If n is 0, that means finding 0!,
else:
which is simply 1.
return n * f(n-1)

print(f(6))

7
Iterative v.s. recursive
def f(n):
if n == 0: Progress
return 1
If n is not 0, we need to obtain the
else:
result of (n-1)!, with that result,
return n * f(n-1)
we simply need to multiply that by
n to obtain n!
print(f(6))

Well, I kind of understand the


strategy, but how this program works
with the strategy? Can we try to
simulate the execution for, say, f(6)?
8
Iterative v.s. recursive
def f(n):
if n == 0: Calling f(6)
return 1 Calling f(6) is just like running
else:
normal functions, with local
return n * f(n-1)
variable n defined for the
execution of f(6).
print(f(6))
return 6 * f(5)
f(6)
Power of recursion : To find the
n 6 answer of f(6), we find f(5) first.
return 6 * f(6-1)
The execution of f(6) is
return 6 * f(5)
SUSPENDED at this stage.
9
Iterative v.s. recursive
def f(n): There is another
if n == 0:
workspace when we
return 1
else: execute f(5), with n equals
return n * f(n-1) 5 in that workspace.
The recursive call is
print(f(6)) reducing n towards 0.
f(6) f(5)

n 6 n 5

return 6 * f(6-1) return 5 * f(5-1)

return 6 * f(5) return 5 * f(4)

10
Iterative v.s. recursive
def f(n): Eventually, f(0) will be
if n == 0:
called. In that case, n equals
return 1
else: 0, and we will have no
return n * f(n-1) further recursive calls.
The value 1 is returned in
print(f(6)) f(0).
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

11
Iterative v.s. recursive
def f(n): With f(0) return, the
if n == 0:
execution goes back to f(1)
return 1
else: and continue the suspended
return n * f(n-1) part with f(0) = 1 to the
return statement of f(1).
print(f(6))
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

return 1 * 1

return 1 12
Iterative v.s. recursive
def f(n): So this is why we need to have
if n == 0:
return n * f(n-1)
return 1
else: But not just
return n * f(n-1) n * f(n-1)

print(f(6))
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

return 2 * 1 return 1 * 1

return 2 return 1 13
Iterative v.s. recursive
def f(n): So this is why we need to have
if n == 0:
return n * f(n-1)
return 1
else: But not just
return n * f(n-1) n * f(n-1)

print(f(6))
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

return 3 * 2 return 2 * 1 return 1 * 1

return 6 return 2 return 1 14


Iterative v.s. recursive
def f(n): So this is why we need to have
if n == 0:
return n * f(n-1)
return 1
else: But not just
return n * f(n-1) n * f(n-1)

print(f(6))
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

return 4 * 6 return 3 * 2 return 2 * 1 return 1 * 1

return 24 return 6 return 2 return 1 15


Iterative v.s. recursive
def f(n): So this is why we need to have
if n == 0:
return n * f(n-1)
return 1
else: But not just
return n * f(n-1) n * f(n-1)

print(f(6))
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

return 5 * 24 return 4 * 6 return 3 * 2 return 2 * 1 return 1 * 1

return 120 return 24 return 6 return 2 return 1 16


Iterative v.s. recursive 720
720
def f(n): def factorialIterative(n):
if n == 0: result = 1
return 1 for i in range(2,n+1):
else:
return n * f(n-1) V.S. result *= i
return result

print(f(6)) print(factorialIterative(6))
f(6) f(5) f (4) f(3) f(2) f(1) f(0)

n 6 n 5 n 4 n 3 n 2 n 1 n 0
return 6 * f(6-1) return 5 * f(5-1) return 4 * f(4-1) return 3 * f(3-1) return 2 * f(2-1) return 1 * f(1-1) return 1

return 6 * f(5) return 5 * f(4) return 4 * f(3) return 3 * f(2) return 2 * f(1) return 1 * f(0)

return 6 * 120 return 5 * 24 return 4 * 6 return 3 * 2 return 2 * 1 return 1 * 1

return 720 return 120 return 24 return 6 return 2 return 1 17


Iterative v.s. recursive
If a recursive call is encountered:
Temporarily suspends its
computation in the current function.

Proceeds to evaluate the recursive call.

Returns to finish the outer computation


if the recursive call is completed.

18
Example 1 Recursive palindrome
0 1 2 3 4 5 0 1 2 3 4 5
b a c c a b b a c k a b

Iterative:
def isPalindrome(s):
If the first slot is NOT the for i in range(0, len(s)//2):
equal to the last slot, it is not if s[i] != s[-(i+1)]:
a palindrome. return False
Then, if the 2nd slot is NOT return True
Iterative approach
equal to the last slot, it is not
palindrome, …etc
19
Example 1 Recursive palindrome
0 1 2 3 4 5
b a c c a b
len(s)=6 Larger problem
0 1 2 3
a c c a
len(s)=4 Smaller problem

Question 1. The length of the string is the


What is the parameter that determines
parameter that the problem size. (i.e., the
determines the problem is smaller when
problem size? len(s) is smaller)
20
Example 1 Recursive palindrome
0 1 2 3 4 5 0 1 2 3 4 5 6
b a c c a b b a c d c a b

0 1 2 3 0 1 2 3 4
a c c a a c d c a

0 1 0 1 2
c c c d c

0
Empty string Base cases d

Question 2. What
is/are the base case(s) When s is empty
that the solution is string or len(s)==1,
readily available? s is a palindrome.
21
Example 1 Recursive palindrome
0 1 2 3 4 5 6
b a c d c a b

Question 3. What is the progress to


break down the problem and reuse
the solution of the smaller problem?

22
Example 1 Recursive palindrome
Ok, the first and the last slots are the same. 0 1 2 3 4 5 6
I want to ask if acdca is a palindrome? b a c d c a b

Ok, the first and the last slots are the same. 0 1 2 3 4
I want to ask if cdc is a palindrome? Progress a c d c a
towards
the base
Ok, the first and the last slots are the same.
I want to ask if d is a palindrome?
case 0 1 2
c d c

Yes! d is a 0
palindrome! d

base case
23
Example 1 Recursive palindrome
Yes! bacdcab is 0 1 2 3 4 5 6
a palindrome! b a c d c a b

Yes! acdca is a 0 1 2 3 4
palindrome!
a c d c a
Reuse of
answer of
smaller Yes! cdc is a 0 1 2
palindrome!
problem c d c

Yes! d is a 0
palindrome! d

base case
24
Example 1 Recursive palindrome
Ok, the first and the last slots are the same. No! bacdkab is 0 1 2 3 4 5 6
I want to ask if acdka is a palindrome? NOT a
palindrome! b a c d k a b

Ok, the first and the last slots are the same. No! acdka is NOT 0 1 2 3 4
I want to ask if cdk is a palindrome? a palindrome!
a c d k a

The first and the last slots are NOT the No! cdk is NOT a 0 1 2
same. palindrome!
c d k

25
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case
return True
else: # Progress
if s[0] == s[-1]:
return isPalindrome(s[1:-2])
else:
return False
Recursive approach Base case
If length of the string is less than
or equal to 1, that must be a
palindrome.
26
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case
return True
else: # Progress
if s[0] == s[-1]:
return isPalindrome(s[1:-2])
else:
return False
Recursive approach Not a palindrome
If the string’s first character is NOT
equal to the last one, immediately
conclude that it is NOT a
palindrome. 27
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case
return True
else: # Progress
if s[0] == s[-1]:
return isPalindrome(s[1:-1])
else:
return False
Recursive approach Progress towards base case
If the first and last slots of the
string are the same, then whether
the current string is a palindrome
depends on whether the internal
string is a palindrome. 28
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case
Now, let’s try to
return True
else: # Progress simulate what will
if s[0] == s[-1]: happen when we
return isPalindrome(s[1:-1])
run this program.
else:
return False

isPalindrome('backab') isPalindrome('acka') isPalindrome('ck')

s='backab' s='acka' s='ck'


return isPalindrome(s[1:-1]) return isPalindrome(s[1:-1])
return isPalindrome('acka') return isPalindrome('ck')

29
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case More importantly,
return True understand how return
else: # Progress isPalindrome(s[1:-2])
if s[0] == s[-1]: works in propagating
return isPalindrome(s[1:-1]) the result back the
else: recursive calls.
return False

isPalindrome('backab') isPalindrome('acka') isPalindrome('ck')

s='backab' s='acka' s='ck'


return isPalindrome(s[1:-1]) return isPalindrome(s[1:-1]) return False
return isPalindrome('acka') return isPalindrome('ck')
return False return False 30
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case
return True
else: # Progress
if s[0] == s[-1]:
return isPalindrome(s[1:-1])
else:
return False

isPalindrome('baccab') isPalindrome('acca') isPalindrome('cc') isPalindrome('')

s='baccab' s='acca' s='cc' s=''


return isPalindrome(s[1:-1]) return isPalindrome(s[1:-1]) return isPalindrome(s[1:-1]) return True
return isPalindrome('acca') return isPalindrome('cc') return isPalindrome('')

31
Example 1 Recursive palindrome
def isPalindrome(s):
if len(s)<=1: # Base case
return True
else: # Progress
if s[0] == s[-1]:
return isPalindrome(s[1:-1])
else:
return False

isPalindrome('baccab') isPalindrome('acca') isPalindrome('cc') isPalindrome('')

s='baccab' s='acca' s='cc' s=''


return isPalindrome(s[1:-1]) return isPalindrome(s[1:-1]) return isPalindrome(s[1:-1]) return True
return isPalindrome('acca') return isPalindrome('cc') return isPalindrome('')

return True return True return True 32


Example 2. Pascal triangle
In Pascal's triangle, each number is the
sum of the two numbers directly above it.
1 n=1

33
Example 2. Pascal triangle
In Pascal's triangle, each number is the
sum of the two numbers directly above it.
1 n=1

1 1 n=2

When n=2, the first slot is


1 because the number
directly above it is 1.
The same for the 2nd slot.

34
Example 2. Pascal triangle
In Pascal's triangle, each number is the
sum of the two numbers directly above it.
1 n=1

1 1 n=2

1 2 1 n=3

When n=3,
The first and last slots are 1 because
the number directly above them is 1.
The second number is 2 because
there are two “1” above it.
35
Example 2. Pascal triangle
In Pascal's triangle, each number is the
sum of the two numbers directly above it.
1 n=1

1 1 n=2

Please write a 1 2 1 n=3

Python program to 1 3 3 1 n=4

generate the n-th 1 4 6 4 1 n=5

1 5 10 10 5 1 n=6

row of the Pascal 1 6 15 20 15 6 1 n=7

triangle. 1 7 21 35 35 21 7 1 n=8


36
Example 2. Pascal triangle
Question 1. What is the parameter
that determines the problem size? 1 n=1

1 1 n=2

1 2 1 n=3
It is obviously the
1 3 3 1 n=4
parameter n, because
1 4 6 4 1 n=5
when n is larger, there are
1 5 10 10 5 1 n=6
more numbers to be
1 6 15 20 15 6 1 n=7
calculated in the output.
1 7 21 35 35 21 7 1 n=8


37
Example 2. Pascal triangle
Question 2. What is/are the base case(s)
that the solution is readily available? 1 n=1

1 1 n=2

1 2 1 n=3

Base case is obviously when 1 3 3 1 n=4

n is equal to 1 i.e., the first 1 4 6 4 1 n=5

level. 1 5 10 10 5 1 n=6

The list only contains [1]. 1 6 15 20 15 6 1 n=7

1 7 21 35 35 21 7 1 n=8


38
Example 2. Pascal triangle
previous 1 3 3 1 n=4 Question 3. What is
the progress to
current ? ? ? ? ? n=5
break down the
problem and reuse
the solution of the
Think in this way: smaller problem?
If we already have the solution
of n-1, can we construct the
solution for n?
39
Example 2. Pascal triangle
previous 1 3 3 1 n=4
current 1 ? ? ? 1 n=5

Observation 1:
The first and the last
slots must be 1.
40
Example 2. Pascal triangle
previous 1 3 3 1 n=4
current 1 4 6 4 1 n=5

Observation 2:
Then the internal values are sum of
consecutive pairs of previous level, from
slot 0 to slot len(previous)-2.
41
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
else: #Progress 1 n=1
current = []
current.append(1) 1 1 n=2

previous = pascal(n-1) n=3


1 2 1
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1]) 1 3 3 1 n=4

current.append(1)
return current 1 4 6 4 1 n=5

1 5 10 10 5 1 n=6

1 6 15 20 15 6 1 n=7

1 7 21 35 35 21 7 1 n=8


42
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
else: #Progress 1 n=1
current = []
current.append(1) 1 1 n=2

previous = pascal(n-1) n=3


1 2 1
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1]) 1 3 3 1 n=4

current.append(1)
return current 1 4 6 4 1 n=5

1 5 10 10 5 1 n=6

1 6 15 20 15 6 1 n=7

1 7 21 35 35 21 7 1 n=8


43
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
else: #Progress 1 n=1
current = []
current.append(1) 1 1 n=2

previous = pascal(n-1) n=3


1 2 1
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1]) 1 3 3 1 n=4

current.append(1)
return current 1 4 6 4 1 n=5

1 5 10 10 5 1 n=6

1 6 15 20 15 6 1 n=7

1 7 21 35 35 21 7 1 n=8


44
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
else: #Progress 1 n=1
current = []
current.append(1) 1 1 n=2

previous = pascal(n-1) n=3


1 2 1
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1]) 1 3 3 1 n=4

current.append(1)
return current 1 4 6 4 1 n=5

1 5 10 10 5 1 n=6

1 6 15 20 15 6 1 n=7

1 7 21 35 35 21 7 1 n=8


45
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case Now, let’s try to
return [1]
else: #Progress simulate what will
current = [] happen when we
current.append(1)
previous = pascal(n-1) run pascal(4).
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1])
current.append(1)
return current

pascal(4) pascal(3) pascal(2) pascal(1)

current = [1] current = [1] current = [1] return [1]


previous = pascal(3) previous = pascal(2) previous = pascal(1)
46
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
else: #Progress
current = []
current.append(1)
previous = pascal(n-1)
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1])
current.append(1)
return current

pascal(4) pascal(3) pascal(2) pascal(1)

current = [1] current = [1] current = [1,1] return [1]


previous = pascal(3) previous = pascal(2) previous = [1]
47
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
else: #Progress
current = []
current.append(1)
previous = pascal(n-1)
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1])
current.append(1)
return current

pascal(4) pascal(3) pascal(2) pascal(1)

current = [1] current = [1,2,1] current = [1,1] return [1]


previous = pascal(3) previous = [1,1] previous = [1]
48
Example 2. Pascal triangle
def pascal(n):
if n == 1: #Base case
return [1]
Success
else: #Progress
current = []
current.append(1)
previous = pascal(n-1)
for i in range(len(previous)-1):
current.append(previous[i] + previous[i+1])
current.append(1)
return current

pascal(4) pascal(3) pascal(2) pascal(1)

current = [1,3,3,1] current = [1,2,1] current = [1,1] return [1]


previous = [1,2,1] previous = [1,1] previous = [1]
49
Example 3. Greatest Common Divisor
Find the greatest common divisor (GCD) of two numbers, which is the
largest number that divides both of them without leaving a remainder.
GCD (504,105) = GCD (504%105, 105)
= GCD (84,105)

If x can divide 105 and 504


x can also divide 84 because:

84+(105*4)=504
x can divide
Do you remember how
3
Therefore x can also x can divide
divide this part
2
this part
1
this part to solve GCD(a,b)?
50
Example 3. Greatest Common Divisor
Find the greatest common divisor (GCD) of two numbers, which is the
largest number that divides both of them without leaving a remainder.
GCD (504,105) = GCD (504%105, 105)
= GCD (84,105)
Let’s always rearrange the smaller
= GCD (105,84) value to be the 2nd parameter

= GCD (84,105%84)
= GCD (84,21)
= GCD (21,84%21)
= GCD (21,0) GCD(x,0) = x because
only x can divide x and 0.
= 21 51
Example 3. Greatest Common Divisor

GCD (504,105) = GCD (504%105, 105) def gcd(a, b):


= GCD (84,105) if (b == 0): #Base case
= GCD (105,84) return a
else: #Progress
= GCD (84,105%84) return gcd(b , a % b)
= GCD (84,21)
= GCD (21,84%21)
= GCD (21,0)
= 21 52
Example 3. Greatest Common Divisor

GCD (504,105) = GCD (504%105, 105) def gcd(a, b):


= GCD (84,105) if (b == 0): #Base case
= GCD (105,84) return a
else: #Progress
= GCD (84,105%84) return gcd(b , a % b)
= GCD (84,21)
= GCD (21,84%21) Note that a % b must be smaller than b.
= GCD (21,0)
= 21 53
Example 4. Fibonacci number
The Fibonacci numbers are the numbers in an integer sequence, called
the Fibonacci sequence, and characterized by the fact that every number
after the first two is the sum of the two preceding ones.
0 1 2 3 4 5 6 7 8 9 10 11 12
Fibonacci sequence …
0 1 1 2 3 5 8 13 21 34 55 89 144

Please write a Question 1. What


Python program to is the parameter
return the n-th that determines
Fibonacci number. the problem size?
54
Example 4. Fibonacci number
The Fibonacci numbers are the numbers in an integer sequence, called
the Fibonacci sequence, and characterized by the fact that every number
after the first two is the sum of the two preceding ones.
0 1 2 3 4 5 6 7 8 9 10 11 12
Fibonacci sequence …
0 1 1 2 3 5 8 13 21 34 55 89 144

Question 2. What
Base cases: is/are the base
If n<2, the case(s) that the
answer is n solution is readily
available? 55
Example 4. Fibonacci number
The Fibonacci numbers are the numbers in an integer sequence, called
the Fibonacci sequence, and characterized by the fact that every number
after the first two is the sum of the two preceding ones.
0 1 2 3 4 5 6 7 8 9 10 11 12
Fibonacci sequence …
0 1 1 2 3 5 8 13 21 34 55 89 144

Question 3. What is
Progress:
the progress to
To find the nth Fibonacci number,
break down the
we need to have the n-1th and
problem and reuse
the n-2th Fibonacci number first,
the solution of the
and sum them up.
smaller problem?
56
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

57
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)
Now, let’s try to
f(4) f(3)
simulate what will
f(3) f(2) f(2) f(1)
happen when we
f(2) f(1) f(1) f(0) f(1) f(0) run f(5).
f(1) f(0)

58
Recursive calls
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)
This is how the
f(4) f(3)
return from base
f(3) f(2) f(2) f(1)
case propagate back
1 f(1) f(1) f(0) f(1) f(0) to the parent calls.
1 0

Return values from base case 59


Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)

f(4) f(3)

2 f(2) f(2) f(1)

1 1 f(1) f(0) f(1) f(0)

60
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)

f(4) f(3)

2 1 f(2) f(1)

1 0 f(1) f(0)

61
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)

3 f(3)

2 1 f(2) f(1)

f(1) f(0)

62
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)

3 f(3)

1 f(1)

1 0

63
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

f(5)

3 2

1 1

64
Example 4. Fibonacci number
def f(n):
if n<2: #Base case
return n
else: #Progress
return f(n-1)+f(n-2)

5 And finally, we
3 2 found that f(5) =
5, do you observe
Observation: There are any efficiency
many repeat function problem in the
calls! I try to call f(100), computation?
it is extremely slow! 65
Section 6.2

Memorization

2022-2023
COMP1117A Computer Programming
Dr. T.W. Chim ([email protected]) & Dr. H.F. Ting ([email protected])
Department of Computer Science, The University of Hong Kong
Example 4. Fibonacci number
Implement a map that caches the computed Fibonacci numbers and avoid
repeat function calls.
cache = {} # Create a global map

def f(n):
if n in cache:
return cache[n] A global map object
if n<2: cache is a variable referencing a map
value = n object, which is initially empty.
else: The map will have key as the value of n,
value = f(n-1)+f(n-2) value storing the nth Fibonacci number.
cache[n] = value
return value 67
Example 4. Fibonacci number
Implement a map that caches the computed Fibonacci numbers and avoid
repeat function calls.
cache = {} # Create a global map

def f(n):
if n in cache:
return cache[n] Check if f(n) has been computed
if n<2: If n is in cache, then it has been
value = n computed before and we can directly
else: return its cached value without need of
value = f(n-1)+f(n-2)
recursive calls.
cache[n] = value
return value 68
Example 4. Fibonacci number
Implement a map that caches the computed Fibonacci numbers and avoid
repeat function calls.
cache = {} # Create a global map

def f(n):
if n in cache:
return cache[n] If not cached, compute recursively
if n<2: If n is NOT in cache, then we need to
value = n recursively compute its value.
else:
value = f(n-1)+f(n-2)

cache[n] = value
return value 69
Example 4. Fibonacci number
Implement a map that caches the computed Fibonacci numbers and avoid
repeat function calls.
cache = {} # Create a global map

def f(n):
if n in cache:
return cache[n] Update the cache
if n<2: Up to this point the value of the nth
value = n Fibonacci number has been computed,
else: we update the cache to store the newly
value = f(n-1)+f(n-2)
computed number.
cache[n] = value
return value 70
Example 4. Fibonacci number
Now, let’s try to simulate what
will happen when we run f(5).

cache = {} # Create a global map Key


Cache Empty
value
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n f(3) f(2)
else:
value = f(n-1)+f(n-2) f(2) f(1)

cache[n] = value f(1) f(0)


return value 71
Example 4. Fibonacci number
f(0) = 0 and f(1) = 1 are computed
and stored in the cache.

cache = {} # Create a global map Key 0 1


Cache
value 0 1
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n f(3) f(2)
else:
value = f(n-1)+f(n-2) f(2) f(1)

cache[n] = value 1 0
return value 72
Example 4. Fibonacci number
f(2) = 1 is computed and
stored in the cache.
cache = {} # Create a global map Key 0 1 2
Cache
value 0 1 1
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n f(3) f(2)
else:
value = f(n-1)+f(n-2) f(2) f(1)

cache[n] = value 1 0
return value 73
Example 4. Fibonacci number
Cache hit! f(1) has computed before, so
its value, i.e.,1, is returned right away.

cache = {} # Create a global map Key 0 1 2


Cache
value 0 1 1
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n f(3) f(2)
else:
value = f(n-1)+f(n-2) 1 f(1)

cache[n] = value
return value 74
Example 4. Fibonacci number
f(3) = 2 is computed and
stored in the cache.
cache = {} # Create a global map Key 0 1 2 3
Cache
value 0 1 1 2
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n f(3) f(2)
else:
value = f(n-1)+f(n-2) 1 1

cache[n] = value
return value 75
Example 4. Fibonacci number
Cache hit! f(2) has computed before, so
its value, i.e.,1, is returned right away.

cache = {} # Create a global map Key 0 1 2 3


Cache
value 0 1 1 2
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n 2 f(2)
else:
value = f(n-1)+f(n-2)

cache[n] = value
return value 76
Example 4. Fibonacci number
f(4) = 3 is computed and
stored in the cache.
cache = {} # Create a global map Key 0 1 2 3 4
Cache
value 0 1 1 2 3
def f(n):
if n in cache: f(5)
return cache[n]
f(4) f(3)
if n<2:
value = n 2 1
else:
value = f(n-1)+f(n-2)

cache[n] = value
return value 77
Example 4. Fibonacci number
Cache hit! f(3) has computed before, so its
value, i.e.,2, is returned right away.

cache = {} # Create a global map Key 0 1 2 3 4


Cache
value 0 1 1 2 3
def f(n):
if n in cache: f(5)
return cache[n]
3 f(3)
if n<2:
value = n
else:
value = f(n-1)+f(n-2)

cache[n] = value
return value 78
Example 4. Fibonacci number

cache = {} # Create a global map Key 0 1 2 3 4 5


Cache
value 0 1 1 2 3 5
def f(n):
if n in cache: f(5)
return cache[n]
3 2
if n<2:
value = n
else:
Finally, f(5) is computed.
value = f(n-1)+f(n-2) Please try to run f(100) and
compare its performance with
cache[n] = value
return value the no cache version.
79
Example 5. MergeSort
54 26 93 17 77 31 44 55
Sorting a list recursively:
Question 1. What is the
parameter that determines
the problem size?

Obviously it is the
number of items in
the list, say n.
E.g., n=8 in this
17 26 31 44 54 55 77 93 example.
80
Example 5. MergeSort
54 26 93 17 77 31 44 55

54 26 93 17 77 31 44 55 Divide
Divide the list with n items to two
54 26 93 17 77 31 44 55 lists of n/2 items and sort them

54 26 93 17 77 31 44 55

Base cases: Question 2. What


If n==1, the list is is/are the base case(s)
already sorted. that the solution is
readily available?
81
Example 5. MergeSort
54 26 93 17 77 31 44 55
Question 3. How to
reuse the solution of
54 26 93 17 77 31 44 55 the smaller problem?
54 26 93 17 77 31 44 55

54 26 93 17 77 31 44 55

26 54 17 93
Given two sorted lists of size n/2,
merging the two lists to create a
sorted list of size n requires
scanning the two lists once only.
82
Example 5. MergeSort
54 26 93 17 77 31 44 55

54 26 93 17 77 31 44 55 Divide
Divide the list with n items to two
54 26 93 17 77 31 44 55 lists of n/2 items and sort them

54 26 93 17 77 31 44 55

26 54 17 93 31 77 44 55
Merge
17 26 54 93 31 44 55 77 Merge two sorted lists of size n/2 to
create a sorted list of n items

17 26 31 44 54 55 77 93

83
Example 5. MergeSort def mergeSort(A,left,right):
mergeSort(A,0,8)
if right-left>1:
54 26 93 17 77 31 44 55 mid = (left+right)//2
mergeSort(A, left, mid)
mergeSort(A, mid, right)
mergeSort(A,0,4) mergeSort(A,4,8) merge(A, left, mid, right)
54 26 93 17 77 31 44 55
A = [54,26,93,17,77,31,44,55]
mergeSort(A,0,len(A))
mergeSort(A,0,2) mergeSort(A,2,4) mergeSort(A,4,6) mergeSort(A,6,8)

54 26 93 17 77 31 44 55 right-left > 1 is a
condition to detect
mergeSort mergeSort mergeSort mergeSort mergeSort mergeSort mergeSort mergeSort
(A,0,1) (A,1,2) (A,2,3) (A,3,4) (A,4,5) (A,5,6) (A,6,7) (A,7,8) progress cases.
54 26 93 17 77 31 44 55

84
def merge(A,left,mid,right):
L = A[left:mid]
R = A[mid:right]
i=j=0
k=left def mergeSort(A,left,right):
while i < len(L) and j < len(R): if right-left>1:
if L[i] < R[j]: mid = (left+right)//2
A[k]=L[i] mergeSort(A, left, mid)
i=i+1 mergeSort(A, mid, right)
k=k+1 merge(A, left, mid, right)
else:
A[k]=R[j] A = [54,26,93,17,77,31,44,55]
j=j+1 mergeSort(A,0,len(A))
k=k+1

while i < len(L):


A[k]=L[i]
We call the merge()
i=i+1 function to combine
k=k+1
two sorted lists of size
while j < len(R): n/2 to a sorted list of
A[k]=R[j]
j=j+1 size n.
k=k+1
85
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 54 93 31 44 55 77 k=0
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=0

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=0
while j < len(R):
A[k]=R[j]
j=j+1
k=k+1
86
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 54 93 31 44 55 77 k=0
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=0

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=0
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
87
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 54 93 31 44 55 77 k=1
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=1

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=0
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
88
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 31 93 31 44 55 77 k=2
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=2

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=0
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
89
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 31 44 31 44 55 77 k=3
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=2

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=1
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
90
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 31 44 54 44 55 77 k=4
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=2

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=2
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
91
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 31 44 54 55 55 77 k=5
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=3

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=2
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
92
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 31 44 54 55 77 77 k=6
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=3

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=3
while j < len(R):
A[k]=R[j]
j=j+1 This part just put the smallest value
k=k+1 among L and R to A, one at a time.
93
def merge(A,left,mid,right):
L = A[left:mid] merge(A,0,4,8)
R = A[mid:right]
i=j=0
k=left k
while i < len(L) and j < len(R):
if L[i] < R[j]: A 17 26 31 44 54 55 77 93 k=7
A[k]=L[i]
i=i+1
k=k+1 Sorted Sorted

else:
A[k]=R[j] i
j=j+1
k=k+1 L 17 26 54 93 i=3

while i < len(L):


A[k]=L[i] j

i=i+1
k=k+1
R 31 44 55 77 j=4
while j < len(R):
A[k]=R[j]
Since all values in R are scanned,
j=j+1
we put the remaining values in L to A.
k=k+1
94
Summary
To understand recursive strategy, you need to know…
The parameter that determines the problem size.
The base case(s) where the solution is readily available.
The progress to break down a bigger problem to smaller problem(s).
The strategy to reuse the solutions of the smaller problem(s) to solve the
bigger problem.

Learning objective: To understand the Python code and recursive


strategies presented in this chapter first.

95
Chapter 6.

END
Acknowledgement:
This set of slides is partially adopted from
ENGG1330. Thanks to Dr. C.K. Chui!

2022-2023
COMP1117A Computer Programming
Dr. T.W. Chim ([email protected]) & Dr. H.F. Ting ([email protected])
Department of Computer Science, The University of Hong Kong

You might also like