doc

advertisement
AT70.02 - Data Structures and Algorithms
Lecturer: Jittat Fakcharoenphol
Nov 10, 2004
Scribe: Long X. Nguyen
Lecture 13A: Dynamic Programming
This lecture introduces the dynamic programming. We examine three problems with
dynamic programming: Fibonacci numbers, 0-1 Knapsack, and longest common
subsequence.
We have learnt about divide-and-conquer approach to design the algorithm. In this
approach, the function is called recursively to compute the solution.
Unlike the divide-and-conquer approach, the dynamic programming proposes the bottomup approach. Dynamic programming is an optimization procedure that is particularly
applicable to problems requiring a sequence of interrelated decisions [1].
The art of dynamic programming is how to discover the recursive relationship and define
the sub-problem. There are two key points of dynamic programming:
(1) Optimal substructure: An optimal solution to a problem (instance) contains optimal
solutions to sub-problem.
(2) Overlapping subproblems: A recursive solution contains a “small” number of
distinct subproblems repeated many times.
1. Fibonacci Numbers
The Fibonacci numbers are defined by the following recurrence:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2),
where n > 1
Each Fibonacci number is the sum of the two previous ones, yielding the sequence
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …
Problem: given n, compute the Fibonacci number F(n)?
1.A. Algorithm by Divide-and-Conquer approach
The algorithm is designed by divide-and-conquer approach as the following:
Fibonacci(n)
if n = 0
return 0
if n = 1
return 1
else
return (Fibonacci(n-1) + Fibonacci(n-2))
The behavior of this algorithm is illustrated in the figure
Recursive
calls
F(n)
F(n-1)
F(n-2)
Re-use
computed
results
F(n)
F(n-2)
F(n-1)
F(n-2)
F(4)
F(3)
Computed
Repeatly
F(4)
F(3)
F(2)
F(2)
F(2)
1
0
F(1)
1
1
0
F(0)
(1a)
(1b)
Figure 1 – (1a) Divide-and-Conquer and (1b) Dynamic Programming
This algorithm computes some results that it has in different branches. Hence, it save a
lot of resource if it could re-use the computed results of some other branches.
The running time of algorithm is O(2n).
1.B. Algorithm by Dynamic Programming
Dynamic Programming provides another solution that utilizes the computed result. To
compute F(n), it computes F(0), F(1), F(2), …, F(n-1), to F(n). Hence, the computed
results F(n-2), F(n-1) is used to compute F(n).
Fibonacci(n)
f[0]  0
f[1]  1
for i = 2 to n do
f[i] = f[i-1] + f[i-2]
return f(n)
The behavior of the algorithm is illustrated in the figure (1b).
Another example of dynamic programming:
Given n, m, computer P(n,m) according to the formula
P(n, m) = P(n-1, m) + P(n, m-1),
P(0, 0) = 1
P(n, m) = 0 if n, m < 0
if n, m ≥ 0
The behavior of dynamic programming on this problem is illustrated in the following
figure:
1
4
(0, 3)
1
(1, 3)
3
(0, 2)
1
6
2
1
1
(0, 0)
(2, 2)
(3, 2)
4
(2, 1)
1
(1, 0)
(3, 3)
10
3
(1, 1)
20
(2, 3)
(1, 2)
(0, 1)
Bottom-Up
approach
10
(3, 1)
1
(2, 0)
(3, 0)
Figure 2 – Illustrate of Bottom-Up approach
2. 0-1 Knapsack
The 0-1 Knapsack problem is posed as follows [2]: A thief robbing a store finds n items;
the i-th item is worth ci dollars and weights wi pounds (or kilograms), where ci and wi are
integers. He wants to take as valuable a load as possible, but he can carry at most W
pounds in his knapsack for some integer W. Which item he should take?
The problem is re-stated formally: given some set N of n items, where the i-th item is
worth ci dollars and weights wi pounds. Find the subset S of n items to maximize
c
iS
i such that
w W .
iS
i
We have to make decision that pick an item or drop it. Suppose that W is a small integer.
The 0-1 Knapsack problem could be decomposed into O(NW) number of subproblems.
({wi, ci}i=1n, W)
= cn + MAX({wi, ci}i=1n-1, W-wn)
= ({wi, ci}i=1n-1, W)
if pick (wn, cn),
otherwise
Example of 0-1 Knapsack problem: W = 4
6
4
ci
3
2
wi
4
2
Compute the table bottom-up
4
3
2
1
0
Weight/
{N items}
0
0
0
0
0
6
6
4
0
0
6
6
4
0
0
8
6
4
0
0
0
1
2
3
Each cell in the above table represent the value V(i, j) that is the maximum total value if
we can pick i objects from the set and put them in the knapsack of weight j.
V(i, j) = MAX {ci + V(i -1, j – wi) if wi ≤ j; V(i-1, j)}
V(i, j) = 0 if i = 0 or j = 0.
Example of 0-1 Knapsack problem with W = 10
3
4
2
ci
2
3
5
wi
The best value is 20, computed in the following figure:
10
1
3
4
Total
weight
10
0
10
14
17
20
20
9
0
10
14
17
17
17
8
0
10
14
17
17
17
7
0
10
14
17
17
17
6
0
10
14
17
17
17
5
0
10
14
14
14
14
4
0
10
14
14
14
14
3
0
10
13
13
13
13
2
0
10
10
10
10
10
1
0
10
10
10
10
10
0
0
0
0
0
0
0
0
1
2
3
4
5
Bag of
size i
Figure 3 – 0-1 Knapsack computation
3. Longest Common Subsequence
A subsequence of a string S is a string that can be obtained by deleting some character
from S.
Given two strings x[1…m] and y[1…n], the problem is to find a string u which is a
subsequence of both x and y of longest length.
For example:
x=
A
G
A
T
C
A
G
G
y=
G
C
A
T
G
A
G
LCS(x,y) =
G-A-T-A-G
Simplification
(1) Look at the length of a longest-common subsequence
(2) Extend the algorithm to find the LCS itself.
Consider the prefixes of x and y.
Define c[i, j] = |LCS(x[1..i], y[1..j])|
Then c[m, n] = |LCS(x, y)|
The idea of the algorithm is to match the subsequence of the prefix of x with that of y.
Hence, there are two cases.
c[i  1, j  1]  1, ifx[i ]  y[ j ]
c[i, j ]  
max{c[i  1, j ], c[i, j  1]}, otherwise
Procedure LCS-LENGTH takes two sequences X = x1, x2, ..., xm and Y = y1, y2, ..., yn as
inputs. It stores the c[i, j] values in a table c[0… m, 0…n] whose entries are computed in
row-major order. (That is, the first row of c is filled in from left to right, then the second
row, and so on.) It also maintains the table b[1…m, 1…n] to simplify construction of an
optimal solution. Intuitively, b[i, j] points to the table entry corresponding to the optimal
subproblem solution chosen when computing c[i, j]. The procedure returns the b and c
tables; c[m, n] contains the length of an LCS of X and Y.
LCS-LENGTH(x, y)
m  length[x]
n  length[y]
for i  1 to m do
c[i, 0]  0
for j  1 to n do
c[0, j]  0
for i  1 to m do
for j  1 to n do
if (x[i] = y[j])
then
c[i, j]  c[i-1, j-1] + 1
b[i, j]  “*”
else if c[i-1, j] ≥ c[i, j-1]
then c[i, j]  c[i-1, j]
b[i, j]  “”
else c[i, j]  c[i, j-1]
b[i, j]  “”
return c and b
The figure shows how the algorithm compute the table by dynamic programming.
G
1
2
3
3
4
4
5
G
1
2
3
3
4
4
5
A
1
2
3
3
3
4
4
C
1
2
2
3
3
3
3
T
1
1
2
3
3
3
3
A
1
1
2
2
2
3
3
G
1
1
1
1
2
2
2
A
0
0
1
1
1
1
1
G
C
A
T
G
A
G
Figure 4 – Longest Common Subsequence
References
[1]
[2]
S. E. Dreyfus and A. M. Law, The art and theory of dynamic programming:
Academic Press, 1977.
T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to
Algorithms, 2nd edition ed: MIT Press, 2001.
Download