Skip to content

state 30 of 32 · 6 min

The errors that do not crash

Dynamic programming fails quietly. The table fills, numbers appear, and the answer is wrong. These are the specific ways, collected.

Wrong loop direction

python
for c in range(w, C + 1):        # upward: items reused
for c in range(C, w - 1, -1):    # downward: each item once

Solves a different problem. Symptom: answers too high on the bounded version.

Base cases left at zero

A minimum over a table containing false zeroes selects them. Symptom: answers far too low, and plausible.

For a maximum, zero claims unreachable states are achievable with value zero. Use negative infinity for unreachable, infinity for an unfilled minimum.

The answer read from the wrong cell

"Best up to i" is monotone, so the answer is the last cell. "Best ending exactly at i" is not, so it is the maximum anywhere. Returning the last cell from an "ending exactly here" table passes small tests, because on short inputs the best chain often does end last.

The memo key is not the whole state

python
@lru_cache(maxsize=None)
def go(i):                # but the answer also depends on remaining capacity

Returns a value computed for a different situation, silently. Test: can two different situations produce the same key?

Recurrence permits what the rule forbids

python
best[i] = max(best[i-1], value[i] + best[i-1])   # allows two adjacent
best[i] = max(best[i-1], value[i] + best[i-2])   # correct

Symptom: the answer is larger than any legal selection. Check the take branch excludes what it should.

Counting duplicates

Loops in the wrong nesting counts permutations where combinations were asked for. Test on a tiny input where you can enumerate by hand: coins 1 and 2, amount 3, is the answer 2 or 3?

Modifying while iterating, or sharing a row

python
table = [[0] * m] * n      # n references to ONE list
table = [[0] * m for _ in range(n)]   # n separate lists

The first is a Python specific trap and every row changes together.

The checklist

CheckQuestion
Statecould two situations share this key?
Base casesis each one a sentence I can defend?
Directionis every cell filled after what it reads?
Combinecounting or optimising?
Answer celldoes my definition put it last, or anywhere?
Reachabilityis unreachable distinguishable from zero?

The habit that catches all of them

Write a brute force version, then compare both on a few hundred random small inputs.

python
for _ in range(500):
    case = random_small_case()
    assert brute(case) == fast(case), case

It takes ten minutes, it finds every error above, and it prints the smallest failing case for you. This is worth more than any amount of staring at the table.