The factorial of n is n * (n-1) * (n-2) * ... * 1. By definition, factorial(0) = 1.
Before coding:
factorial(4) as 4 * factorial(3). What is factorial(3) in terms of factorial(2)? Keep going until you reach a number whose factorial you already know without any multiplication.factorial(n)?" without calling factorial again? This is called the base case.n, how do you express factorial(n) using factorial(n-1)? This is called the recursive case.Your derivation:
Write a function factorial_recursive(n) that:
1 if n is 0 (the base case),n * factorial_recursive(n - 1) (the recursive case).Example:
factorial_recursive(5) # Output: 120
factorial_recursive(3) # Output: 6
factorial_recursive(0) # Output: 1
factorial_recursive(1) # Output: 1
Before running anything: what do you think happens if you call factorial_recursive(-1)? Walk through it: does n ever hit 0?
Now try it in the cell below --- wrapped in a try/except so it doesn't crash your whole notebook. What error do you get?
In Exercise 1.3 you saw what happens without a base case. What is the role of the base case in a recursive function?
A. It makes the function run faster
B. It tells Python to use a loop instead of recursion
C. It provides the stopping condition --- the simplest case that can be answered directly without another recursive call
D. It handles invalid inputs like negative numbers
4 * 3 means "add 4 to itself 3 times": 4 + 4 + 4 = 12.
Before coding (assume both a and b are non-negative for now):
b for which a * b is obviously 0 without doing any addition?b, how can you write a * b in terms of a * (b - 1)?Your derivation:
Write multiply_recursive(a, b) that works for b >= 0, without using the * operator.
Example:
multiply_recursive(4, 3) # Output: 12
multiply_recursive(5, 0) # Output: 0
Now suppose b can be negative --- like multiply_recursive(7, -2), which should be -14.
Before coding:
b is negative, how is a * b related to a * (-b) (where -b is now positive)?b to positive, calling your existing recursive logic, and then fixing the sign of the result?Your derivation:
if b < 0: multiply_recursive(a, b) = ...
Update multiply_recursive(a, b) to handle negative b too.
Example:
multiply_recursive(4, 3) # Output: 12
multiply_recursive(5, 0) # Output: 0
multiply_recursive(7, -2) # Output: -14
multiply_recursive(-3, -3) # Output: 9
x ** n (for a positive integer n) means multiplying x by itself n times. For example, 2 ** 3 = 2 * 2 * 2 = 8.
Before coding:
n (a positive integer) for which x ** n is just x, with no multiplication needed?n, how do you write x ** n using x ** (n - 1)?Your derivation:
Write power(x, n) that returns x ** n using recursion, where n is a positive integer (n >= 1).
Example:
power(2, 3) # Output: 8
power(5, 2) # Output: 25
power from Part 3 only handles n >= 1. Now extend it to handle n == 0 and negative n too.
Before coding:
x ** 0 be, for any non-zero x? (No recursion needed --- this becomes a new base case.)n is negative, x ** n equals 1 divided by what?Your derivation:
Write compute_power(x, n) that returns x ** n using recursion, for any integer n --- positive, negative, or zero.
Example:
compute_power(2, 3) # Output: 8
compute_power(2, -3) # Output: 0.125
compute_power(5, 0) # Output: 1
compute_power(-2, 3) # Output: -8
Your compute_power probably handles a negative n by computing the positive power first, then taking 1 / the result. There's a route that never needs that flip: make the recursion itself climb toward zero.
For negative n, notice that $x^n = x^{n+1} / x$ --- the exponent moves up by one each call, so -3 -> -2 -> -1 -> 0 reaches the same base case that positive n reaches by counting down.
Write power_climb(x, n) with one base case (n == 0) and two recursive cases: for positive n recurse on n - 1 (as before), for negative n recurse on n + 1 using the identity above.
The lesson: a recursion just needs each call to step closer to the base case --- from below works as well as from above.
Tests:
power_climb(4, -1) # 0.25
power_climb(2, -3) # 0.125
power_climb(2, 3) # 8
power_climb(5, 0) # 1
17 / 5: how many times does 5 fit into 17, and what's left over?
Before coding:
5 from 17 repeatedly, counting how many subtractions you do, until what's left is less than 5. How many subtractions did it take, and what's the leftover amount?(dividend, divisor) relate to the answer for (dividend - divisor, divisor)?Your derivation:
Write recursive_divide(dividend, divisor) that returns a tuple (quotient, remainder), using recursion. You must not use // or %. Assume dividend >= 0 and divisor > 0.
Example:
recursive_divide(17, 5) # Output: (3, 2)
recursive_divide(20, 4) # Output: (5, 0)
recursive_divide(7, 3) # Output: (2, 1)
recursive_divide(0, 1) # Output: (0, 0)
The HCF (Highest Common Factor, a.k.a. GCD) of two numbers can be found with a remarkably short recursive rule:
b == 0, the HCF is a.a and b is the same as the HCF of b and a % b.Before coding: you already have a way to compute a remainder --- recursive_divide(a, b) from Part 5 returns (quotient, remainder). Instead of writing a % b again, how would you get that same remainder by calling recursive_divide?
Your plan:
remainder of a divided by b, using Part 5's function = ...
Write compute_hcf(a, b) using Euclid's method, reusing recursive_divide from Part 5 to get the remainder instead of using %.
Example:
compute_hcf(12, 18) # Output: 6
compute_hcf(100, 25) # Output: 25
compute_hcf(17, 13) # Output: 1
compute_hcf(0, 5) # Output: 5
You have 3 pegs: A (source), B (auxiliary), C (target), and a stack of discs on A, smallest on top. Move the whole stack to C, one disc at a time, never placing a larger disc on a smaller one.
The general rule for n discs, moving from source to target using auxiliary as spare space:
n - 1 discs from source to auxiliary (using target as spare).n from source to target.n - 1 discs from auxiliary to target (using source as spare).Before coding:
n you can solve directly, with no sub-problem?n = 2: write out the 3 moves yourself, in order, using the rule above (you have 1 disc to move in steps 1 and 3, and disc 2 in step 2).Your derivation:
Write solve_hanoi(n, source='A', auxiliary='B', target='C') that:
Moving <disc> from <source> to <target>.Example:
solve_hanoi(1)
# Moving 1 from A to C.
# Returns: 1
Tasks:
solve_hanoi(2). You should see exactly 3 moves, matching what you traced by hand in Exercise 7.1.solve_hanoi(3). You should see exactly 7 moves.solve_hanoi(4) and just look at the returned total (don't worry about reading all the printed moves). What's the pattern in the totals for n = 1, 2, 3, 4? Can you guess the formula for the total number of moves as a function of n?
power/compute_power function might help you verify your guess.The Tower of Hanoi for n discs requires $2^n - 1$ moves. For n = 4 (four discs), how many moves are needed?
A. 8
B. 15
C. 16
D. 31
The Fibonacci sequence is defined recursively: fib(0) = 0, fib(1) = 1, and fib(n) = fib(n-1) + fib(n-2) for n >= 2.
Tasks:
fib(n) using plain recursion (two recursive calls, just like Tower of Hanoi).fib(10) (expected: 55).fib(30) takes. Then try fib(35). What's happening? (Hint: how many times does fib end up computing fib(2), for instance?)fib(n) the first time you compute it, and check the cache before recursing. How much faster is fib(35) now?In Exercise 7.3, you may have guessed that solve_hanoi(n) always returns $2^n - 1$ total moves.
Write a small check that uses your compute_power function from Part 4 to verify this formula against the actual return value of solve_hanoi, for n from 1 to 10 (call solve_hanoi in a way that doesn't print anything, or just ignore the printed output).
Take a moment to answer these questions in your own words.
b show you a pattern: solve the "hard" case by converting it to a case you already know how to solve?%?n increases?You've just learned recursion's core move: trust that a smaller call to the same function already works, and build the answer to the bigger problem on top of it. Every algorithm you meet from here on --- searching, sorting, tree traversal --- leans on exactly this idea.
Source on GitHub · Back to all chapters
© 2026 CloudxLab. All rights reserved.