MCS 275 Spring 2021
Emily Dumas
Course bulletins:
Focuses on recursion. Based on special classes of strings that have the "pattern" ABB
:
A
and B
are single characters, e.g. egg
, off
, aaa
A
is a superegg or single character and B
is a single character, e.g. add
, addee
A
and B
are each hypereggs or single characters, e.g. anoonoo
, offeggegg
, gooss
You'll write functions to test whether a string belongs to these classes.
Details in the project description.
Later I will provide test data, but you'll need to write your own test code.
n=35 | |
---|---|
recursive | 1.9s |
iterative | <0.001s |
Measured on a 4.00Ghz Intel i7-6700K CPU (2015 release date) with Python 3.8.5
Most Fibonacci numbers are computed many times!
Most Fibonacci numbers are computed many times!
fib
computes the same terms over and over again.
Instead, let's store all previously computed results, and use the stored ones whenever possible.
This is called memoization. It only works for pure functions, i.e. those which always produce the same return value for any given argument values.
math.sin(...)
is pure; time.time()
is not.
Let's add a simple memoization feature to our recursive fib
function.
n=35 | n=450 | |
---|---|---|
recursive | 1.9s | > age of universe |
memoized recursive | <0.001s | 0.003s |
iterative | <0.001s | 0.001s |
Measured on a 4.00Ghz Intel i7-6700K CPU (2015 release date) with Python 3.8.5
Recursive functions with multiple self-calls often benefit from memoization.
Memoized version is conceptually similar to an iterative solution.
Memoization does not alleviate recursion depth limits.
One way to measure the expense of a recursive function is to count how many times the function is called.
Let's do this for recursive fib
.
$n$ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|---|
calls | 1 | 1 | 3 | 5 | 9 | 15 | 25 | |
$F_n$ | 0 | 1 | 1 | 2 | 3 | 5 | 8 | 13 |
fib
is called to compute fib(n)
. Then
$$T(0)=T(1)=1$$ and $$T(n) = T(n-1) + T(n-2) + 1.$$
Corollary: $T(n) = 2F_{n+1}-1$.
Proof of corollary: Let $S(n) = 2F_{n+1}-1$. Then $S(0)=S(1)=1$, and $$\begin{split}S(n) &= 2F_{n+1}-1 = 2(F_{n} + F_{n-1}) - 1\\ &= (2 F_n - 1) + (2 F_{n-1}-1) + 1\\ & = S(n-1) + S(n-2) + 1\end{split}$$ Therefore $S$ and $T$ have the same first two terms, and follow the same recursive definition based on the two previous terms.
Corollary: Every time we increase $n$ by 1, the naive recursive fib
does $\approx61.8\%$ more work.
(The ratio $F_{n+1}/F_n$ approaches $\frac{1 + \sqrt{5}}{2} \approx 1.61803$.)
How do you solve a maze?
How do you solve a maze?
No changes to the references from Lecture 13