Two dials
Time and space are separate choices. A faster program can use more memory, and a lean one can spend more steps.
Cache only what repeats
A cache pays off when the same work repeats. If nothing repeats, the cache only costs memory.
You can spend extra memory to save steps, or save memory to spend steps. The two dials are chosen on purpose, not by accident.
About 2 min of reading
Trade memory for steps and back.
Set membership loop.
Before you run it, say out loud what it will print. Then open the answer.
Fib 10 with cache.
Before you run it, say out loud what it will print. Then open the answer.
Fib 10 no cache.
Before you run it, say out loud what it will print. Then open the answer.
Sum and max one pass.
Before you run it, say out loud what it will print. Then open the answer.
Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.
Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.
Time and space are separate choices. A faster program can use more memory, and a lean one can spend more steps.
A cache pays off when the same work repeats. If nothing repeats, the cache only costs memory.
a = [3, 1, 4, 1, 5]
s = set(a)
steps = 0
for x in a:
steps += 1
if x in s:
steps += 1
print(steps)
Building the set costs n once. Each membership test is then one step.
a = [3, 1, 4, 1, 5]
steps = 0
for x in a:
steps += 1
if x in a:
steps += 1
print(steps)
Membership in a list scans it, so the loop pays n each turn. That is quadratic.
memo = {}
def fib(n):
if n in memo:
return memo[n]
if n < 2:
return n
v = fib(n - 1) + fib(n - 2)
memo[n] = v
return v
print(fib(10))
The cache turns repeated work into one lookup. Space n buys linear time.
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(10))
Without the cache the same calls repeat. The tree of calls doubles each level.
a = [5, 2, 9, 1]
a.sort()
lo, hi = 0, len(a) - 1
print(a, a[(lo + hi) // 2])
Sorting costs n log n once but then each search is log n. Worth it when you search many times.
a = [4, 7, 2]
s = 0
m = a[0]
for x in a:
s += x
if x > m:
m = x
print(s, m)
Sum and max in one pass keep only two numbers. No extra list is stored.
n = 1000000
a = [0] * n
print(len(a))
A cache of a million entries costs a million slots. Sometimes that memory is the real limit.
Each one is defined here on this site. Open the entry for the full definition and the other words it points at.