In this lesson, you'll explore recursive algorithms and functions, a key concept in advanced algorithms. Recursion allows functions to solve problems by breaking them into smaller, similar subproblems.
By the end of this lesson, you will be able to:
Recursion might seem complex at first, but itβs a clever way for a function to tackle a problem by calling itself with a simpler version of that problem. Picture yourself organizing a stack of nested boxes: you open one box, then deal with a smaller set of boxes inside it, repeating the process until you find an empty box. Thatβs recursion.
Every recursive function has two main parts:
When a function calls itself, it uses something called the 'call stack' β think of it as a stack of plates. Each call adds a plate to the stack. When it reaches the base case, it starts removing plates (returning values) from the bottom up, building the final answer.
Let's take a look at the key differences between iterative and recursive approaches, building on the examples you've seen in this lesson, like factorial and Fibonacci.
Recursive Approach: A recursive function solves a problem by calling itself with a smaller version of the same problem. It relies on a base case to stop the recursion and recursive cases to break down the problem. For example, in the recursive factorial, each call computes
n * factorial(n-1)
until reaching the base case (n=0 or 1). This uses the call stack, which can lead to stack overflow for large inputs, and may be less efficient due to repeated calculations.
Iterative Approach: An iterative function uses loops (like for or while) to repeat steps without calling itself. It builds the solution step by step, often using variables to track progress. For instance, the iterative factorial multiplies numbers in a loop from 1 to n, using constant space (O(1)) and linear time (O(n)). It's generally more efficient, avoids recursion depth issues, and is better for large inputs.
Key Differences:
In VS Code, create a new folder called RecursionLesson
and a file named recursion.py
inside it.
Calculating the factorial of a number involves multiplying the number by the factorial of the number one less than it. For example, the factorial of 5 (written 5!) is 5 * 4 * 3 * 2 * 1 = 120. Recursion works here because each factorial can be broken down into a smaller factorial problem.
We'll write a recursive function to compute the factorial of a number, where the base case is 0! or 1! (both equal 1), and the recursive case multiplies the number by the factorial of the number minus one.
Add the following complete code to your file:
def factorial(n):
# Base case: if n is 0 or 1, the factorial is 1
if n == 0 or n == 1:
return 1
# Recursive case: calculate factorial by multiplying n with factorial of (n-1)
previous_factorial = factorial(n - 1)
current_factorial = n * previous_factorial
return current_factorial
# Test the function
print("Factorial of 5:", factorial(5)) # Output: 120
print("Factorial of 3:", factorial(3)) # Output: 6
print("Factorial of 0:", factorial(0)) # Output: 1
Save and run the file in VS Code terminal with python factorial.py
. You should see 120, 6, and 1 printed.
Try testing with other inputs like print(factorial(4))
(should print 24) or print(factorial(1))
(should print 1) to verify.
Now, let's implement an iterative version of factorial using a loop, and compare it to the recursive one. Iteration means using a loop to repeat steps, which can often be more efficient than recursion for certain problems.
In the iterative approach, we'll start with a result of 1 and then multiply it by each integer from 1 up to n in a loop. This builds up the factorial step by step without calling the function repeatedly.
For example, for n=5, it starts with result=1, then multiplies by 1 (still 1), by 2 (becomes 2), by 3 (becomes 6), by 4 (becomes 24), by 5 (becomes 120).
Add the following complete code below the recursive factorial in 'recursion.py
':
def iterative_factorial(n):
# Base cases: if n is 0 or 1, return n directly
if n == 0:
return 0
if n == 1:
return 1
# Recursive case: calculate Fibonacci by adding the two previous numbers
# Call the function for (n-1) and (n-2) and add their results
previous_one = iterative_factorial(n - 1)
previous_two = iterative_factorial(n - 2)
current_fibonacci = previous_one + previous_two
return current_fibonacci
# Test the function
print("Recursive:", factorial(5)) # 120
print("Iterative:", iterative_factorial(5)) # 120
Comparison:
Recursion is great for clarity and problems that naturally fit a 'divide and conquer' approach, but iteration often wins for performance in simple cases like this, especially to avoid running out of memory with deep recursion.