2.1 Recursion
A recursive function is a function that calls itself in its body, either directly or indirectly. Recursive functions have three important components:
- Base case(s), the simplest possible form of the problem you're trying to solve.
- Recursive case(s), where the function calls itself with a simpler argument as part of the computation.
- Combining results from recursive calls to solve the full problem.
Q: What does "simpler" mean?
A: Well, for integer
i
andj
,i
is simpler thanj
iffi < j
Q: So, "simpler" means less than?
A: "Generalized" less than. This can be tricky for some problems, see Problem 1.
Let's look at the canonical example, factorial
.
Factorial, denoted with the ! operator, is defined as: $$ n! = \prod_{i=1}^n i = n \times (n-1) \times \dots \times 1 $$ Or, equivalently, in a recursive way: $$ n! = \begin{cases} 1 & n=0 \\ n\times (n-1)! & \text{otherwise} \end{cases} $$ For example: $$ 5! = 5 \times 4 \times 3 \times 2 \times 1 = 120 $$
An iterative implementation for factorial might look like this:
def factorial(n):
result, i = 1, 1
while i <= n:
result = result * i
i += 1
return result
In contrast, a recursive implementation for factorial is as follows, note how it resembles the recursive definition of factorial:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
- Base case: by the definition of factorial, the simplest possible form would be
0!
sincen == 0
is the smallest number we can compute the factorial of. - Recursive case: follows immediately from the definition of factorial, i.e.,
factorial(n) = n * factorial(n - 1)
, wherefactorial(n - 1)
is a recursive call tofactorial
with a simpler argumentn - 1
. - Combining results: as for factorial, simply multiply
factorial(n - 1)
byn
and return the result.
In this lab you will be implementing recursive functions to solve some problems. Here are some general tips:
- Paradoxically, to write a recursive function, you must assume that the function is fully functional before you finish writing it; this is called the recursive leap of faith.
- Consider how you can solve the current problem using the solution to a simpler version of the problem. The amount of work done in a recursive function can be deceptively little: remember to take the leap of faith and trust the recursion to solve the slightly smaller problem without worrying about how.
- Think about what the answer would be in the simplest possible case(s). These will be your base cases - the stopping points for your recursive calls. Make sure to consider the possibility that you're missing base cases (this is a common way recursive solutions fail).
- It may help to write an iterative version first.