Process shapes, call trees, and growth rates $\Theta(n)$ vs $\Theta(\varphi^n)$
$$ n! = \begin{cases}1 & n=0\ \,(n-1)! & n>0 \end{cases} $$
def fact(n):
"""Return n! for n >= 0."""
if n == 0:
return 1
return n * fact(n - 1)
# quick sanity checks
print(fact(0)) # 1
print(fact(5)) # 120
def fact_debug(n):
print("enter", n)
if n == 0:
print("base")
return 1
result = n * fact_debug(n - 1)
print("exit", n, "->", result)
return result
fact_debug(4)
def fib(n):
"""Return Fib(n) for n >= 0 (naive tree recursion)."""
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
print(fib(6)) # 8
fib(5) expands into many repeated subcallsfib(5) → fib(4) + fib(3), and fib(4) → fib(3) + fib(2)T(n) be the number of calls: T(n) = T(n-1) + T(n-2) + O(1)T(n) grows like Fibonacci numbers, giving time $\Theta(\varphi^n)$ and space $\Theta(n)$fib(6), how many times is the base case reached (calls with n <= 1)? (Expected: 21; fib(0) occurs 8 times and fib(1) occurs 13 times)def fib_memo(n, memo=None):
"""Return Fib(n) using recursion + memoization."""
if memo is None:
memo = {}
if n in memo:
return memo[n]
if n <= 1:
memo[n] = n
else:
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)
return memo[n]
print(fib_memo(30)) # fast
def fib_iter(n):
"""Return Fib(n) in O(n) time and O(1) extra space."""
a, b = 1, 0 # a = Fib(1), b = Fib(0)
count = n
while count != 0:
a, b = a + b, a
count -= 1
return b
print(fib_iter(6)) # 8
def count_change(amount, coins=(50, 25, 10, 5, 1)):
"""Number of ways to make 'amount' using the given coin denominations."""
coins = tuple(coins)
def cc(a, i):
# a = remaining amount, i = index into coins
if a == 0:
return 1
if a < 0 or i == len(coins):
return 0
# skip coin i OR use coin i
return cc(a, i + 1) + cc(a - coins[i], i)
return cc(amount, 0)
print(count_change(10, coins=(10, 5, 1))) # 4

Powered by SciMigo AI Tutor - https://scimigo.com