Common Algorithms
Recursion Introduction
Many programming problems involve repeating the same operation on smaller pieces of data, like calculating compound interest or navigating folder structures. Recursion provides an elegant way to solve these problems by having a function call itself with progressively simpler inputs until reaching a trivial case.
Recursion is when a function calls itself to solve a problem by breaking it into smaller versions of the same problem.
Key Components
- Base case: condition where recursion stops
- Recursive case: function calls itself with simpler input
- Progress: each call moves toward base case
base_case.py
Replay: real traced execution (multi-file project)
# Importance of base case
import sys
def infinite_recursion(n):
"""Missing base case (don't run!)"""
# No base case - will cause RecursionError
return infinite_recursion(n - 1)
def countdown(n):
"""Correct with base case"""
# Base case
if n <= 0:
print("Liftoff!")
return 0
# Recursive case
print(n)
return countdown(n - 1)
def bad_base_case(n):
"""Wrong base case"""
# Base case never reached for negative input
if n == 0:
return 0
return bad_base_case(n - 1)
# Test base cases
print("Countdown with proper base case:")
countdown(5)
print("\nTrying infinite recursion with small depth:")
# Set a smaller recursion limit for demonstration
old_limit = sys.getrecursionlimit()
sys.setrecursionlimit(20)
try:
# This will overflow quickly
infinite_recursion(10)
except RecursionError:
print("RecursionError caught!")
# Restore original limit
sys.setrecursionlimit(old_limit)
print("\nWrong base case with positive input (safe):")
bad_base_case(3)
print("Completed successfully")
# Uncommenting this will cause RecursionError
# print("\nWrong base case with negative input:")
# bad_base_case(-5)
print("Countdown with proper base case:")
32# Test base cases33print("Countdown with proper base case:")34countdown(5)outputCountdown with proper base case:def countdown(n):
pass 1 of 612def countdown(n5):13 """Correct with base case"""14 # Base case15 if n <= 0:16 print("Liftoff!")17 return 01819 # Recursive case20 print(n5)21 return countdown(n5 - 1)output5All 6 passes — pass 1 is the card above pass n1 5 2 4 3 3 4 2 5 1 6 0 if n <= 0:
14# Base case15if n0 <= 0:16 print("Liftoff!")17 return 0outputLiftoff!old_limit ← 1000
33print("Countdown with proper base case:")34countdown(5)3536print("\nTrying infinite recursion with small depth:")37# Set a smaller recursion limit for demonstration38old_limit→ 1000 = sys<module 'sys' (built-in)>.getrecursionlimit()39sys<module 'sys' (built-in)>.setrecursionlimit(20)output Trying infinite recursion with small depth:def infinite_recursion(n):
pass 1 of 186def infinite_recursion(n10):7 """Missing base case (don't run!)"""8 # No base case - will cause RecursionError9 return infinite_recursion(n10 - 1)18 passes — pass 1 is the card above pass n1 10 2 9 3 8 4 7 5 6 6 5 7 4 8 3 9 2 ⋯ 7 more passes ⋯ 17 -6 18 (empty) except RecursionError:
43 infinite_recursion(10)44except RecursionError:45 print("RecursionError caught!")outputRecursionError caught!sys.setrecursionlimit(old_limit)
47# Restore original limit48sys<module 'sys' (built-in)>.setrecursionlimit(old_limit1000)4950print("\nWrong base case with positive input (safe):")51bad_base_case(3)52print("Completed successfully")output Wrong base case with positive input (safe):def bad_base_case(n):
pass 1 of 424def bad_base_case(n3):25 """Wrong base case"""26 # Base case never reached for negative input27 if n == 0:28 return 029 return bad_base_case(n3 - 1)All 4 passes — pass 1 is the card above pass n1 3 2 2 3 1 4 0 if n == 0:
26# Base case never reached for negative input27if n0 == 0:28 return 029return bad_base_case(n - 1)bad_base_case(3)
50print("\nWrong base case with positive input (safe):")51bad_base_case(3)52print("Completed successfully")outputCompleted successfully
base_case
The condition where recursion stops and returns a direct value without further recursive calls
Classic Example: Factorial
factorial.py
Replay: real traced execution (multi-file project)
# Basic factorial recursion
def factorial(n):
"""Recursive factorial"""
# Base case
if n <= 1:
return 1
# Recursive case
# n! = n × (n-1)!
return n * factorial(n - 1)
# Test factorial
limit = 7
for i in range(limit):
print(f"{i}! = {factorial(i)}")
print(f"\n10! = {factorial(10)}")
# Basic factorial recursion
def factorial(n):
"""Recursive factorial"""
# Base case
if n <= 1:
return 1
# Recursive case
# n! = n × (n-1)!
return n * factorial(n - 1)
# Test factorial
limit = 5
for i in range(limit):
print(f"{i}! = {factorial(i)}")
print(f"\n10! = {factorial(10)}")
# Basic factorial recursion
def factorial(n):
"""Recursive factorial"""
# Base case
if n <= 1:
return 1
# Recursive case
# n! = n × (n-1)!
return n * factorial(n - 1)
# Test factorial
limit = 9
for i in range(limit):
print(f"{i}! = {factorial(i)}")
print(f"\n10! = {factorial(10)}")
limit ← 7
15# Test factorial16limit→ 7 = 717#@limit=5, 9for i in range(limit):
pass 1 of 717#@limit=5, 918for i0 in range(limit7):19 print(f"{i0}! = {factorial(i)}")All 7 passes — pass 1 is the card above pass i1 0 2 1 3 2 4 3 5 4 6 5 7 6 def factorial(n):
pass 1 of 324def factorial(n0):5 """Recursive factorial"""6 # Base case32 passes — pass 1 is the card above pass n1 0 2 1 3 2 4 1 5 3 6 2 7 1 8 4 9 3 ⋯ 21 more passes ⋯ 31 2 32 1 if n <= 1:
pass 1 of 86# Base case7if n0 <= 1:8 return 1All 8 passes — pass 1 is the card above pass n1 0 2 1 3 1 4 1 5 1 6 1 7 1 8 1 print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i0}! = {factorial(i)}")output0! = 1print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i1}! = {factorial(i)}")output1! = 1print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i2}! = {factorial(i)}")output2! = 2print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i3}! = {factorial(i)}")output3! = 6print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i4}! = {factorial(i)}")output4! = 24print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i5}! = {factorial(i)}")output5! = 120print(f"{i}! = {factorial(i)}")
18for i in range(limit):19 print(f"{i6}! = {factorial(i)}")output6! = 720print(f" 10! = {factorial(10)}")
21print(f"\n10! = {factorial(10)}")print(f" 10! = {factorial(10)}")
21print(f"\n10! = {factorial(10)}")output 10! = 3628800
limit ← 5
15# Test factorial16limit→ 5 = 517for i in range(limit):for i in range(limit):
pass 1 of 516limit = 517for i0 in range(limit5):18 print(f"{i0}! = {factorial(i)}")All 5 passes — pass 1 is the card above pass i1 0 2 1 3 2 4 3 5 4 def factorial(n):
pass 1 of 214def factorial(n0):5 """Recursive factorial"""6 # Base case21 passes — pass 1 is the card above pass n1 0 2 1 3 2 4 1 5 3 6 2 7 1 8 4 9 3 ⋯ 10 more passes ⋯ 20 2 21 1 if n <= 1:
pass 1 of 66# Base case7if n0 <= 1:8 return 1All 6 passes — pass 1 is the card above pass n1 0 2 1 3 1 4 1 5 1 6 1 print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i0}! = {factorial(i)}")output0! = 1print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i1}! = {factorial(i)}")output1! = 1print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i2}! = {factorial(i)}")output2! = 2print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i3}! = {factorial(i)}")output3! = 6print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i4}! = {factorial(i)}")output4! = 24print(f" 10! = {factorial(10)}")
20print(f"\n10! = {factorial(10)}")print(f" 10! = {factorial(10)}")
20print(f"\n10! = {factorial(10)}")output 10! = 3628800
limit ← 9
15# Test factorial16limit→ 9 = 917for i in range(limit):for i in range(limit):
pass 1 of 916limit = 917for i0 in range(limit9):18 print(f"{i0}! = {factorial(i)}")All 9 passes — pass 1 is the card above pass i1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 def factorial(n):
pass 1 of 474def factorial(n0):5 """Recursive factorial"""6 # Base case47 passes — pass 1 is the card above pass n1 0 2 1 3 2 4 1 5 3 6 2 7 1 8 4 9 3 ⋯ 36 more passes ⋯ 46 2 47 1 if n <= 1:
pass 1 of 106# Base case7if n0 <= 1:8 return 1All 10 passes — pass 1 is the card above pass n1 0 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 10 1 print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i0}! = {factorial(i)}")output0! = 1print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i1}! = {factorial(i)}")output1! = 1print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i2}! = {factorial(i)}")output2! = 2print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i3}! = {factorial(i)}")output3! = 6print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i4}! = {factorial(i)}")output4! = 24print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i5}! = {factorial(i)}")output5! = 120print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i6}! = {factorial(i)}")output6! = 720print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i7}! = {factorial(i)}")output7! = 5040print(f"{i}! = {factorial(i)}")
17for i in range(limit):18 print(f"{i8}! = {factorial(i)}")output8! = 40320print(f" 10! = {factorial(10)}")
20print(f"\n10! = {factorial(10)}")print(f" 10! = {factorial(10)}")
20print(f"\n10! = {factorial(10)}")output 10! = 3628800
factorial
A mathematical function where n! = n * (n-1) * ... * 1, naturally expressed recursively
How It Works
trace.py
Replay: real traced execution (multi-file project)
# Factorial with trace
depth = 0
def factorial(n):
"""Factorial with trace"""
global depth
# Indent based on depth
indent = " " * depth
print(f"{indent}→ factorial({n})")
depth += 1
# Base and recursive cases
if n <= 1:
result = 1
print(f"{indent} Base case: return 1")
else:
result = n * factorial(n - 1)
print(f"{indent} Return {n} * factorial({n-1}) = {result}")
depth -= 1
print(f"{indent}← returning {result}")
return result
# Test with trace
print("Computing factorial(5):\n")
result = factorial(5)
print(f"\nFinal result: {result}")
depth ← 0
4depth→ 0 = 0567def factorial(n):8 """Factorial with trace"""9 global depth1011 # Indent based on depth12 indent = " " * depth1314 print(f"{indent}→ factorial({n})")15 depth += 11617 # Base and recursive cases18 if n <= 1:19 result = 120 print(f"{indent} Base case: return 1")21 else:22 result = n * factorial(n - 1)23 print(f"{indent} Return {n} * factorial({n-1}) = {result}")2425 depth -= 126 print(f"{indent}← returning {result}")2728 return result293031# Test with trace32print("Computing factorial(5):\n")33result = factorial(5)34print(f"\nFinal result: {result}")outputComputing factorial(5):indent ← (empty), depth ← 1
pass 1 of 57def factorial(n5):8 """Factorial with trace"""9 global depth1011 # Indent based on depth12 indent→ (empty) = " " * depth01314 print(f"{indent(empty)}→ factorial({n5})")15 depth→ 1 += 1output→ factorial(5)All 5 passes — pass 1 is the card above pass nindentdepthresult1 5 (empty) 0 → 1 — 2 4 1 → 2 — 3 3 2 → 3 — 4 2 3 → 4 — 5 1 4 → 5 1 else:
pass 1 of 419 result = 120 print(f"{indent} Base case: return 1")21else:22 result = n5 * factorial(n - 1)23 print(f"{indent} Return {n} * factorial({n-1}) = {result}")All 4 passes — pass 1 is the card above pass nindentresult1 5 — — 2 4 — — 3 3 — — 4 2 1 result ← 1
17# Base and recursive cases18if n1 <= 1:19 result→ 1 = 120 print(f"{indent } Base case: return 1")21else:output Base case: return 1depth ← 4
25depth→ 4 -= 126print(f"{indent }← returning {result1}")2728return result1output ← returning 1result ← 2
21else:22 result→ 2 = n2 * factorial(n - 1)23 print(f"{indent } Return {n2} * factorial({n-1}) = {result2}")output Return 2 * factorial(1) = 2depth ← 3
25depth→ 3 -= 126print(f"{indent }← returning {result2}")2728return result2output ← returning 2n ← 3, result ← 6
21else:22 result→ 6 = n→ 3 * factorial(n - 1)23 print(f"{indent } Return {n3} * factorial({n-1}) = {result6}")output Return 3 * factorial(2) = 6depth ← 2
25depth→ 2 -= 126print(f"{indent }← returning {result6}")2728return result6output ← returning 6n ← 4, result ← 24
21else:22 result→ 24 = n→ 4 * factorial(n - 1)23 print(f"{indent } Return {n4} * factorial({n-1}) = {result24}")output Return 4 * factorial(3) = 24depth ← 1
25depth→ 1 -= 126print(f"{indent }← returning {result24}")2728return result24output ← returning 24n ← 5, result ← 120
21else:22 result→ 120 = n→ 5 * factorial(n - 1)23 print(f"{indent(empty)} Return {n5} * factorial({n-1}) = {result120}")output Return 5 * factorial(4) = 120depth ← 0
25depth→ 0 -= 126print(f"{indent(empty)}← returning {result120}")2728return result120output← returning 120result ← 120
32print("Computing factorial(5):\n")33result→ 120 = factorial(5)34print(f"\nFinal result: {result120}")output Final result: 120
See the Call Stack
The trace above uses indentation to show which calls are waiting. These diagrams pin the same factorial(5) run: five frames open, factorial(1) returns 1, then the waiting multiplications finish in reverse order.
Recursion vs Iteration
vs_iteration.py
Replay: real traced execution (multi-file project)
# Recursion vs iteration
import time
def sum_recursive(n):
"""Recursive sum"""
# Base case
if n <= 0:
return 0
# Recursive case
return n + sum_recursive(n - 1)
def sum_iterative(n):
"""Iterative sum"""
# Loop approach
total = 0
for i in range(1, n + 1):
total += i
return total
def power_recursive(base, exp):
"""Recursive power"""
# Base case
if exp == 0:
return 1
# Recursive case
return base * power_recursive(base, exp - 1)
def power_iterative(base, exp):
"""Iterative power"""
# Loop approach
result = 1
for _ in range(exp):
result *= base
return result
# Compare both approaches
print("Sum 1 to 10:")
print(f" Recursive: {sum_recursive(10)}")
print(f" Iterative: {sum_iterative(10)}")
print("\n2^8:")
print(f" Recursive: {power_recursive(2, 8)}")
print(f" Iterative: {power_iterative(2, 8)}")
# Performance comparison kept small so trace replay stays readable.
n = 12
start = time.perf_counter()
r1 = sum_recursive(n)
time_recursive = time.perf_counter() - start
start = time.perf_counter()
r2 = sum_iterative(n)
time_iterative = time.perf_counter() - start
print(f"\nPerformance (sum to {n}):")
print(f" Recursive: {time_recursive * 1000:.4f} ms")
print(f" Iterative: {time_iterative * 1000:.4f} ms")
print(f" Speedup: {time_recursive / time_iterative:.2f}x")
print("Sum 1 to 10:")
42# Compare both approaches43print("Sum 1 to 10:")44print(f" Recursive: {sum_recursive(10)}")45print(f" Iterative: {sum_iterative(10)}")outputSum 1 to 10:def sum_recursive(n):
pass 1 of 246def sum_recursive(n10):7 """Recursive sum"""8 # Base case9 if n <= 0:10 return 011 # Recursive case12 return n10 + sum_recursive(n - 1)24 passes — pass 1 is the card above pass n1 10 2 9 3 8 4 7 5 6 6 5 7 4 8 3 9 2 ⋯ 13 more passes ⋯ 23 1 24 0 if n <= 0:
pass 1 of 28# Base case9if n0 <= 0:10 return 011# Recursive caseprint(f" Recursive: {sum_recursive(10)}")
43print("Sum 1 to 10:")44print(f" Recursive: {sum_recursive(10)}")45print(f" Iterative: {sum_iterative(10)}")output Recursive: 55total ← 0
pass 1 of 215def sum_iterative(n10):16 """Iterative sum"""17 # Loop approach18 total→ 0 = 019 for i in range(1, n + 1):total ← 1
pass 1 of 2218total = 019for i1 in range(1, n10 + 1):20 total→ 1 += i121return total22 passes — pass 1 is the card above pass intotal1 1 10 0 → 1 2 2 10 1 → 3 3 3 10 3 → 6 4 4 10 6 → 10 5 5 10 10 → 15 6 6 10 15 → 21 7 7 10 21 → 28 8 8 10 28 → 36 9 9 10 36 → 45 ⋯ 11 more passes ⋯ 21 11 12 55 → 66 22 12 12 66 → 78 return total
20 total += i21return total55print(f" Iterative: {sum_iterative(10)}")
44print(f" Recursive: {sum_recursive(10)}")45print(f" Iterative: {sum_iterative(10)}")4647print("\n2^8:")48print(f" Recursive: {power_recursive(2, 8)}")49print(f" Iterative: {power_iterative(2, 8)}")output Iterative: 55 2^8:def power_recursive(base, exp):
pass 1 of 924def power_recursive(base2, exp8):25 """Recursive power"""26 # Base case27 if exp == 0:28 return 129 # Recursive case30 return base2 * power_recursive(base, exp8 - 1)All 9 passes — pass 1 is the card above pass exp1 8 2 7 3 6 4 5 5 4 6 3 7 2 8 1 9 0 if exp == 0:
26# Base case27if exp0 == 0:28 return 129# Recursive caseprint(f" Recursive: {power_recursive(2, 8)}")
47print("\n2^8:")48print(f" Recursive: {power_recursive(2, 8)}")49print(f" Iterative: {power_iterative(2, 8)}")output Recursive: 256result ← 1
33def power_iterative(base2, exp8):34 """Iterative power"""35 # Loop approach36 result→ 1 = 137 for _ in range(exp):result ← 2
pass 1 of 836result = 137for _0 in range(exp8):38 result→ 2 *= base239return resultAll 8 passes — pass 1 is the card above pass _result1 0 1 → 2 2 1 2 → 4 3 2 4 → 8 4 3 8 → 16 5 4 16 → 32 6 5 32 → 64 7 6 64 → 128 8 7 128 → 256 return result
38 result *= base39return result256n ← 12, start ← 1010621.691784896
48print(f" Recursive: {power_recursive(2, 8)}")49print(f" Iterative: {power_iterative(2, 8)}")5051# Performance comparison kept small so trace replay stays readable.52n→ 12 = 125354start→ 1010621.691784896 = time<module 'time' (built-in)>.perf_counter()55r1 = sum_recursive(n12)56time_recursive = time.perf_counter() - startoutput Iterative: 256if n <= 0:
pass 2 of 28# Base case9if n0 <= 0:10 return 011# Recursive caser1 ← 78, time_recursive ← 6.685801781713963e-05, start ← 1010621.691859894
54start = time.perf_counter()55r1→ 78 = sum_recursive(n12)56time_recursive→ 6.685801781713963e-05 = time<module 'time' (built-in)>.perf_counter() - start1010621.6917848965758start→ 1010621.691859894 = time<module 'time' (built-in)>.perf_counter()59r2 = sum_iterative(n12)60time_iterative = time.perf_counter() - starttotal ← 0
pass 2 of 215def sum_iterative(n12):16 """Iterative sum"""17 # Loop approach18 total→ 0 = 019 for i in range(1, n + 1):return total
20 total += i21return total78r2 ← 78, time_iterative ← 6.138707976788282e-05
58start = time.perf_counter()59r2→ 78 = sum_iterative(n12)60time_iterative→ 6.138707976788282e-05 = time<module 'time' (built-in)>.perf_counter() - start1010621.6918598946162print(f"\nPerformance (sum to {n12}):")63print(f" Recursive: {time_recursive6.685801781713963e-05 * 1000:.4f} ms")64print(f" Iterative: {time_iterative6.138707976788282e-05 * 1000:.4f} ms")65print(f" Speedup: {time_recursive6.685801781713963e-05 / time_iterative6.138707976788282e-05:.2f}x")output Performance (sum to 12): Recursive: 0.0669 ms Iterative: 0.0614 ms Speedup: 1.09x
Characteristics
- Elegant: solves complex problems with simple code
- Memory: uses call stack
- Performance: often slower than iteration
- Natural fit: trees, graphs, divide-and-conquer
When to Use
- Problem naturally recursive
- Divide and conquer algorithms
- Backtracking problems
- Mathematical sequences
When to Avoid
- Simple loops work better
- Very deep recursion
- Performance critical code
- Python recursion limit concerns
Fibonacci Example
fibonacci.py
Replay: real traced execution (multi-file project)
# Fibonacci sequence
def fibonacci(n):
"""Recursive fibonacci"""
# Base cases
if n <= 1:
return n
# Recursive case
# fib(n) = fib(n-1) + fib(n-2)
return fibonacci(n - 1) + fibonacci(n - 2)
# Fibonacci with call counting
call_count = 0
def fibonacci_counted(n):
"""Fibonacci with call counting"""
global call_count
call_count += 1
if n <= 1:
return n
return fibonacci_counted(n - 1) + fibonacci_counted(n - 2)
# Test fibonacci
print("Fibonacci sequence:")
for i in range(7):
print(f"fib({i}) = {fibonacci(i)}")
# Count calls for fib(6)
call_count = 0
result = fibonacci_counted(6)
print(f"\nfib(6) = {result}, required {call_count} calls")
# Count calls for fib(8)
call_count = 0
result = fibonacci_counted(8)
print(f"fib(8) = {result}, required {call_count} calls")
call_count ← 0
15# Fibonacci with call counting16call_count→ 0 = 0171819def fibonacci_counted(n):20 """Fibonacci with call counting"""21 global call_count22 call_count += 12324 if n <= 1:25 return n2627 return fibonacci_counted(n - 1) + fibonacci_counted(n - 2)282930# Test fibonacci31print("Fibonacci sequence:")32for i in range(7):outputFibonacci sequence:for i in range(7):
pass 1 of 731print("Fibonacci sequence:")32for i0 in range(7):33 print(f"fib({i0}) = {fibonacci(i)}")All 7 passes — pass 1 is the card above pass i1 0 2 1 3 2 4 3 5 4 6 5 7 6 def fibonacci(n):
pass 1 of 594def fibonacci(n0):5 """Recursive fibonacci"""6 # Base cases59 passes — pass 1 is the card above pass n1 0 2 1 3 2 4 1 5 0 6 3 7 2 8 1 9 0 ⋯ 48 more passes ⋯ 58 1 59 0 if n <= 1:
pass 1 of 336# Base cases7if n0 <= 1:8 return n033 passes — pass 1 is the card above pass n1 0 2 1 3 1 4 0 5 1 6 0 7 1 8 1 9 0 ⋯ 22 more passes ⋯ 32 1 33 0 print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i0}) = {fibonacci(i)}")outputfib(0) = 0print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i1}) = {fibonacci(i)}")outputfib(1) = 1print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i2}) = {fibonacci(i)}")outputfib(2) = 1print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i3}) = {fibonacci(i)}")outputfib(3) = 2print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i4}) = {fibonacci(i)}")outputfib(4) = 3print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i5}) = {fibonacci(i)}")outputfib(5) = 5print(f"fib({i}) = {fibonacci(i)}")
32for i in range(7):33 print(f"fib({i6}) = {fibonacci(i)}")outputfib(6) = 8call_count ← 0
35# Count calls for fib(6)36call_count→ 0 = 037result = fibonacci_counted(6)38print(f"\nfib(6) = {result}, required {call_count} calls")call_count ← 1
pass 1 of 9219def fibonacci_counted(n6):20 """Fibonacci with call counting"""21 global call_count22 call_count→ 1 += 12324 if n <= 1:25 return n2627 return fibonacci_counted(n6 - 1) + fibonacci_counted(n - 2)92 passes — pass 1 is the card above pass ncall_count1 6 0 → 1 2 5 1 → 2 3 4 2 → 3 4 3 3 → 4 5 2 4 → 5 6 1 5 → 6 7 0 6 → 7 8 1 7 → 8 9 2 8 → 9 ⋯ 81 more passes ⋯ 91 1 65 → 66 92 0 66 → 67 if n <= 1:
pass 1 of 4724if n1 <= 1:25 return n147 passes — pass 1 is the card above pass n1 1 2 0 3 1 4 1 5 0 6 1 7 0 8 1 9 1 ⋯ 36 more passes ⋯ 46 1 47 0 result ← 8, call_count ← 0
36call_count = 037result→ 8 = fibonacci_counted(6)38print(f"\nfib(6) = {result8}, required {call_count25} calls")3940# Count calls for fib(8)41call_count→ 0 = 042result = fibonacci_counted(8)43print(f"fib(8) = {result}, required {call_count} calls")output fib(6) = 8, required 25 callsresult ← 21
41call_count = 042result→ 21 = fibonacci_counted(8)43print(f"fib(8) = {result21}, required {call_count67} calls")outputfib(8) = 21, required 67 calls
Exercise: practical.py
Implement a recursive countdown function and a recursive sum of digits function