Dynamic programming (DP) solves problems with optimal substructure (optimal solution built from optimal sub-solutions) and overlapping subproblems (same subproblems recur) by storing results in a table instead of recomputing them.
Two approaches:
- Top-down (memoization) — recurse + cache. Easier to write, computes only needed subproblems
- Bottom-up (tabulation) — fill table iteratively from base cases. No recursion overhead, often more cache-friendly
The pattern:
- Define the state (what subproblem does represent?)
- Write the recurrence ( for )
- Identify base cases
- Determine computation order (dependencies must be solved first)
Critical ML applications:
- Viterbi algorithm — most probable sequence in HMMs / CRFs. instead of brute force
- CTC (Connectionist Temporal Classification) — DP over alignment paths for speech recognition
- Beam search — DP-like pruned search over sequences for Autoregressive Generation
- Sequence alignment (Needleman-Wunsch, Smith-Waterman) — foundational in bioinformatics, used in protein language models
- Dynamic Programming in RL — Bellman equations are DP recurrences:
- Optimal matrix chain multiplication — choosing the order of matmuls to minimize FLOPs, relevant when chaining large linear layers
Complexity: DP trades space for time. A DP solution is typically .
See also: Dynamic Programming in RL, Bellman Equations, Graphs and Traversals