First Ingredient of Dynamic Programming 1. Optimal substructure The optimal solution to the problem contains optimal solutions to the subproblems. Example A A A A A A )( i i+1 k k+1 k+2 j ( ) . … . … Optimal parenthesization for A A i j … Optimal parenthesization for A A i k … Optimal parenthesization for A A k+1 j … Proof by contradiction (cut-and-paste).
Second Ingredient of DP 2. Overlapping subproblems. Few subproblems but many recurring instances. 1..4 1..1 2..4 1..2 3..4 1..3 4..4 2..2 3..4 2..3 4..4 1..1 2..2 3..3 4..4 1..1 2..3 1..2 3..3 3..3 4..4 2..2 3..3 2..2 3..3 1..1 2..2 Example Matrix-Chain Multiplication recurrences Exponential number of nodes but only (n ) subproblems! 2
Memoization 1. Still recursive 2. After computing the solution to a subproblem, store it in table. 3. Subsequent calls do table lookup.
Matrix-Chain Recursion Tree without Memoization 1..4 already solved 1..1 2..4 1..2 3..4 1..3 4..4 2..2 3..4 2..3 4..4 1..1 2..2 3..3 4..4 1..1 2..3 1..2 3..3 Can be pruned! 3..3 4..4 2..2 3..3 2..2 3..3 1..1 2..2
Matrix-Chain Recursion Tree with Memoization 1..4 1..1 2..4 1..2 3..4 1..3 4..4 Table lookup for solutions 2..2 3..4 2..3 4..4 1..1 2..2 1..1 2..3 1..2 3..3 3..3 4..4 2..2 3..3
Memoized-Matrix-Chain Lookup-Chain(p, i, j) // chain product A … A ; dimensions in p[ ] if m[i, j] < // if cost already computed then return m[i, j] // simply return the cost // otherwise, it’s the first call; compute the cost recursively if i = j then m[i, j] = 0 else for k = i to j – 1 do q = Lookup-Chain(p, i, k) + Lookup-Chain(p, k+1, j) + p p p if q < m[i, j] then m[i, j] = q return m[i, j] i j i-1 k j Invoke Lookup-Chain(p, 1, n) to compute the chain product cost.
Analysis of Memoization Lookup-Chain(p, 1, n) ... . LC(p, 1, j) … LC(p, i – 1, j) LC(p, i, j+1) … LC(p, i, n) ... LC(p, i, j) Each LC(p, i, j), i = 1, …, n and j = i, …, n, is called by i – 1 + n – j = O(n) parents
Analysis (cont’d) (O(n) + (n+ij2) O(1)) The first call to Lookup-Chain(p, i, j) requires computation. // (j – i) = O(n) time // (excluding time spent on recursively computing other entries). The rest n + i j 2 calls result in table lookups // O(1) each call of this kind Total running time: n n i = 1 j = i (O(n) + (n+ij2) O(1)) 3 = O(n )
Memoization vs DP Top-down vs bottom-up. Asymptotically as fast as DP. Prefered to DP if not all subproblems need to be solved. Otherwise slower than DP by a constant factor because of overhead for recursion and table maintenance.