Chapter 6 Recursion
Chapter 6 Recursion
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.
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))
n 6 n 5
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)
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)
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)
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)
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
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
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
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
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
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
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
1 5 10 10 5 1 n=6
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
level. 1 5 10 10 5 1 n=6
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
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
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
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
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
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
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
f(5)
f(4) f(3)
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