Dynamic Programming and Memoization: Maximizing Sums and Longest Common Subsequences, Slides of Data Structures and Algorithms

Dynamic programming and memoization techniques used to maximize the sum in a choosing game and find the length of the longest common subsequence between two strings. Lecture notes, examples, and code snippets in java. Dynamic programming is a method for solving complex problems by breaking them down into simpler subproblems and solving each subproblem only once, while memoization is a technique for storing and reusing the results of expensive function calls to avoid redundant computation.

Typology: Slides

2012/2013

Uploaded on 04/27/2013

netii
netii 🇮🇳

4.4

(7)

91 documents

1 / 8

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Lecture #39
Today: Dynamic programming and memoization.
Last modified: Wed Apr 26 14:28:36 2006 CS61B: Lecture #39 1
pf3
pf4
pf5
pf8

Partial preview of the text

Download Dynamic Programming and Memoization: Maximizing Sums and Longest Common Subsequences and more Slides Data Structures and Algorithms in PDF only on Docsity!

Lecture

• Today: Dynamic programming and memoization.

Dynamic Programming

• A puzzle (D. Garcia):

– Start with a list with an even number of non-negative integers.

– Each player in turn takes either the leftmost number or the

rightmost.

– Idea is to get the largest possible sum.

• Example: starting with (6, 12, 0, 8), you (as first player) should take

the 8. Whatever the second player takes, you also get the 12, for a

total of 20.

• Assuming your opponent plays perfectly (i.e., to get as much as pos-

sible), how can you maximize your sum?

• Can solve this with exhaustive game-tree search.

Still Another Idea from CS61A

• The problem is that we are recomputing intermediate results many

times.

• Solution: memoize the intermediate results. Here, we pass in an

N × N array (N = V.length) of memoized results, initialized to -1.

int bestSum (int[] V, int left, int right, int total, int[][] memo) { if (left > right) return 0; else if (memo[left][right] == -1) { int L = V[left] + total - bestSum (V, left+1, right, total-V[left],memo); int R = V[right] + total - bestSum (V, left, right-1, total-V[right],memo); memo[left][right] = Math.max (L, R); } return memo[left][right]; } }

• Now the number of recursive calls to bestSum must be O(N 2 ), for

N = the length of V , an enormous improvement from Θ(2N^ )!

Iterative Version

• I prefer the recursive version, but the usual presentation of this

idea—known as dynamic programming—is iterative:

int bestSum (int[] V) { int[][] memo = new int[V.length][V.length]; int[][] total = new int[V.length][V.length]; for (int i = 0; i < V.length; i += 1) memo[i][i] = total[i][i] = V[i]; for (int k = 1; k < V.length; k += 1) for (int i = 0; i < V.length-k-1; i += 1) { total[i][i+k] = V[i] + total[i+1][i+k]; int L = V[i] + total[i+1][i+k] - memo[i+1][i+k]; int R = V[i+k] + total[i][i+k-1] - memo[i][i+k-1]; memo[i][i+k] = Math.max (L, R); } return memo[0][V.length-1]; }

• That is, we figure out ahead of time the order in which the memo-

ized version will fill in memo, and write an explicit loop.

• Save the time needed to check whether result exists.

• But I say, why bother?

Memoized Longest Common Subsequence

/** Length of longest common subsequence of S0[0..k0-1]

  • and S1[0..k1-1] (pseudo Java) */ static int lls (String S0, int k0, String S1, int k1) { int[][] memo = new int[k0+1][k1+1]; for (int[] row : memo) Arrays.fill (row, -1); return lls (S0, k0, S1, k1, memo); }

private static int lls (String S0, int k0, String S1, int k1, int[][] memo) { if (k0 == 0 || k1 == 0) return 0; if (memo[k0][k1] == -1) { if (S0[k0-1] == S1[k1-1]) memo[k0][k1] = 1 + lls (S0, k0-1, S1, k1-1, memo); else memo[k0][k1] = Math.max (lls (S0, k0-1, S1, k1, memo), lls (S0, k0, S1, k1-1, memo)); } return memo[k0][k1]; }

Q: How fast will the memoized version be?

Memoized Longest Common Subsequence

/** Length of longest common subsequence of S0[0..k0-1]

  • and S1[0..k1-1] (pseudo Java) */ static int lls (String S0, int k0, String S1, int k1) { int[][] memo = new int[k0+1][k1+1]; for (int[] row : memo) Arrays.fill (row, -1); return lls (S0, k0, S1, k1, memo); }

private static int lls (String S0, int k0, String S1, int k1, int[][] memo) { if (k0 == 0 || k1 == 0) return 0; if (memo[k0][k1] == -1) { if (S0[k0-1] == S1[k1-1]) memo[k0][k1] = 1 + lls (S0, k0-1, S1, k1-1, memo); else memo[k0][k1] = Math.max (lls (S0, k0-1, S1, k1, memo), lls (S0, k0, S1, k1-1, memo)); } return memo[k0][k1]; }

Q: How fast will the memoized version be? Θ(k 0 · k 1 )