LightMySky
En anglais

Dynamic Programming: Optimal Substructure and Overlapping Subproblems

When a problem's best answer is built from best answers to smaller versions, and the same smaller versions keep reappearing, storing each answer once turns an exponential recursion into a polynomial one. The two conditions are what decide whether the method applies.

No account needed. Progress saves in this browser.

What a learner can do afterwards

  • Turn a naive recursive Fibonacci into a memoised one and count the calls saved
  • State the subproblem and the recurrence before writing any code
  • Say which of the two conditions fails for a problem where the method does not apply

1 · Read

Some problems hide smaller copies of themselves inside. When the best answer to the whole problem is built from best answers to those smaller versions, the problem has optimal substructure. Your first move is always to name the smaller version, called the subproblem, and the rule that combines them, called the recurrence. The smallest version you answer directly is the base case. For Fibonacci the subproblem is fib(k) for k below n, with fib(k) = fib(k-1) + fib(k-2).

Try it together

Watch naive fib(20) run and it makes over 20,000 calls, because it recomputes the same small values again and again. The memoised version computes each of fib(0) through fib(20) exactly once, which is 21 values, each in constant extra work. Same recurrence, same answers, hugely less work, because no value is ever computed twice.

The second condition is overlap: the same smaller versions must keep reappearing. When they do, you store each answer the first time you compute it and reuse it later, which is called memoising. Moving a stack of rings never repeats a smaller move, since each move happens once, so storing answers there saves nothing.

Good to know

Before any code, ask two questions. One, is the best answer built from best answers to smaller versions. Two, do those smaller versions repeat. Mergesort fails the second, since its pieces are all distinct. The longest simple path fails the first, since reusing vertices breaks the buildup. For rod-cutting, state the subproblem and recurrence first, then memoise.

Name the subproblem and recurrence, check both conditions, and store each repeated answer once.

More to read

Chapter 12: Recursion basics

Recursion means a function solves a big problem by calling itself on a smaller version of the same problem. Picture moving a stack of rings from one peg to another. To move three rings, you first move the top two rings out of the way, move the bottom ring, then move the two rings back on top. Moving the two rings is itself done by moving one ring out of the way, moving the next, then moving the one ring back. The same small job shows up again and again inside the big job. Every recursive solution needs two parts. First, a base case: the smallest problem you can answer directly, like moving a single ring. Second, a recursive case: how a bigger problem breaks into smaller ones plus a little extra work. Without a base case the function calls itself forever. Note the contrast that decides everything: moving rings never repeats the same subproblem, each smaller move happens once, so storing answers saves nothing there. Fibonacci is the opposite. Its recurrence fib(n) = fib(n-1) + fib(n-2) recomputes the same values exponentially many times, so when the same smaller problem keeps reappearing, storing each answer once saves huge amounts of work.

12.3 Recursion with strings and lists

This section shows recursion working on everyday data like words and lists. A word that reads the same forwards and backwards, like racecar, is a palindrome. You can check one recursively. Strip the first and last letters. If they do not match, the answer is no. If they do match, ask the same question about the word left in the middle, which is shorter. Keep going until the word has one or no letters left, which is the base case and means yes. Notice what happens: the same check runs on smaller and smaller pieces, and each piece is solved the same way as the original. That idea, a problem built from smaller versions of itself, leads to the two questions you should always ask about a problem: can its best answer be built from best answers to smaller versions, and do those smaller versions keep showing up more than once. When both answers are yes, remembering each answer once instead of recomputing it turns slow code into fast code.

Think Python 3e

Think Python is a friendly introduction to programming that builds one idea at a time and defines every term when it first appears. It matters for this topic because it introduces recursion as a sequence of small, careful steps rather than one big leap. You first learn that a function can call itself, then you see how a problem must shrink toward a base case each time, and only then do you meet harder uses like Fibonacci, where a simple recursive version recomputes the same values over and over and becomes terribly slow. That slow version is the perfect before picture. Once you can watch a recursive function redo the same smaller problem many times, you are ready to ask the two key questions: is the best answer to the big problem built from best answers to smaller versions, and do those smaller versions repeat. When both are true, saving each answer the first time you compute it turns an exponential tangle into a fast, polynomial one. Concretely, naive fib(20) makes over 20,000 calls because it recomputes the same small values again and again, while the memoised version computes each of fib(0) through fib(20) once, about 21 values, each in constant extra work. State the subproblem and the recurrence before any code, for example subproblem fib(k) for k below n with fib(k) = fib(k-1) + fib(k-2). When the method does not apply, name the failing condition: mergesort pieces are all distinct, so overlapping subproblems fails; the longest simple path reuses vertices, so optimal substructure fails.

2 · Watch

En anglais

Take it off screen

Print a worksheetA4 with an answer key page for grown-ups. No screen, no internet.

Where it sits

Then practise

8 questions wait behind this lesson, each with its answer explained. Every answer feeds the sky: stars light as they are learned, and dim when it is time to come back.

Spotted a problem on this page? Tell us
Dynamic Programming: Optimal Substructure and Overlapping Subproblems · Computing, ages 20 to 21 · LightMySky