12.04.dynamic Programming
12.04.dynamic Programming
Dynamic programming
ece.uwaterloo.ca
[email protected]
Dynamic programming
Outline
Fibonacci numbers
(1) n 1
T n
T n 1 T n 2 (1) n 1
Dynamic programming
5
Fibonacci numbers
Fibonacci numbers
To demonstrate, consider:
int main() {
cout.precision( 16 ); // print 16 decimal digits of precision for
doubles
// 53/lg(10) = 15.95458977...
return 0;
}
Dynamic programming
7
Fibonacci numbers
The output:
F(33) = 5702887 1206474355
F(34) = 9227465 1206474355 F(34), and F(35) in 1 s
F(33),
F(35) = 14930352 1206474355
F(36) = 24157817 1206474356
F(37) = 39088169 1206474358
F(38) = 63245986 1206474360
F(39) = 102334155 1206474363
F(40) = 165580141 1206474368
F(41) = 267914296 1206474376
F(42) = 433494437 1206474389
F(43) = 701408733 1206474411
F(44) = 1134903170 1206474469 ~1 min to calculate F(44)
Dynamic programming
8
Fibonacci numbers
Problem:
– To calculate F(44), it is necessary to calculate F(43) and F(42)
– However, to calculate F(43), it is also necessary to calculate F(42)
– It gets worse, for example
• F(40) is called 5 times
• F(30) is called 620 times
• F(20) is called 75 025 times
• F(10) is called 9 227 465 times
• F(0) is called 433 494 437 times
Fibonacci numbers
Fibonacci numbers
Once we have calculated a value, can we not store that value and
return it if a function is called a second time?
– One solution: use an array
– Another: use an associative hash table
Dynamic programming
11
Fibonacci numbers
array[0] = 1.0;
array[1] = 1.0;
ray?
t
// use 0.0 to indicate we have not yet calculated
of gers?
he ar inatevalue
for ( int i = 2; i < ARRAY_SIZE;t he end) { exed by
++i
array[i] = 0.0; bey ond no t ind
f you go
oblem is
} ti
Wha at if our pr
s:
lem Whn ) {
Probdouble F( int
if ( array[n] == 0.0 ) {
array[n] = F( n – 1 ) + F( n – 2 );
}
return array[n];
}
Dynamic programming
12
Fibonacci numbers
Fibonacci numbers
if ( n <= 1 ) {
return 1.0;
} else {
if ( memo[n] == 0.0 ) {
memo[n] = F(n - 1) + F(n - 2);
}
return memo[n];
}
}
Dynamic programming
14
Fibonacci numbers
return 0;
}
double F( int n ) {
if ( n <= 1 ) {
return 1.0;
}
return a;
}
Dynamic programming
17
Newton polynomials
yk ,...,2 yk 1,...,1
yk ,...,1
xk x1
where k = 1, …, n and the coefficients are defined recursively:
yi ,..., j 1 yi 1,..., j
yi ,..., j
x j xk
y j 1 y j
y j , j 1
x j 1 x j
Dynamic programming
18
Newton polynomials
Newton polynomials
n n 1
Note, however, that there are only coefficients
2
– This shouldn’t require such recursion
x0 y0
y1 y0
y1, 0
x1 x0 y2 ,1 y1, 0
x1 y1 y2 ,1, 0
y2 y1 x2 x0 y3, 2 ,1 y2 ,1, 0
y2 ,1 y3, 2 ,1, 0
x2 x1 y3 , 2 y2 ,1 x3 x0
x2 y2 y3, 2 ,1
y3 y 2 x3 x1
y3 , 2
x3 x2
x3 y3
Dynamic programming
20
Newton polynomials
return array[i][j];
}
Dynamic programming
21
Newton polynomials
double Newton_polynomials( int i, int j, double *xs, double *ys, bool clear = true
) {
static std::map< std::pair< int, int >, double > memo;
if ( clear ) {
memo.clear();
}
if ( i == j ) {
return ys[i];
} else if ( i + 1 == j ) {
return ( ys[j] - ys[i] )/( xs[j] - xs[i] );
} else {
if ( memo.find( std::pair<int, int>( i, j ) ) == memo.end() ) {
memo[std::pair<int, int>( i, j )] = (
Newton_polynomials( i + 1, j, xs, ys, false )
- Newton_polynomials( i, j - 1, xs, ys, false )
) / (xs[j] - xs[i]);
}
Newton polynomials
Note that the memory required is now Q(n) and not Q(n2)
Dynamic programming
23
Connected
return false;
}
Dynamic programming
Dynamic programming
Suppose A is k × m and B is m × n
– Then AB is k × n and calculating AB is Q(kmn)
– The number of multiplications is given exactly kmn
Matrix Dimensions
A 20 × 5
B 5 × 40
C 40 × 50
D 50 × 10
Dynamic programming
29
Order Multiplications
((AB)C)D 54000
(AB)(CD) 32000
(A(BC))D 25000
A((BC)D) 13500
A(B(CD)) 23000
Thus, the optimal run time may be found by calculating the product
in the order
A((BC)D)
3 2 7 9 4
6 4
2
0 8 1 5
Dynamic programming
36
return minimum;
Dynamic programming
39
if ( clear ) {
memo.clear();
} Associate a pair (i, j) with an integer
if ( i + 1 == j ) {
return 0;
} else if ( memo[std::pair<int, int>(i, j)] == 0 ) {
if ( i + 2 == j ) {
memo[std::pair<int, int>(i, j)] = ms[i].rows() * ms[i].columns() * ms[i + 1].columns();
} else {
int minimum = matrix_chain_memo( ms, i + 1, j, false )
+ ms[i].rows() * ms[i].columns() * ms[j - 1].columns();
n 1n 1 n 2
j=1 2 3 4 ··· n
i=1 0
2 0 ···
3 0 ···
4 0 ···
·
··
n 0
Dynamic programming
45
j=1 2 3 4 ··· n
i=1 0
2 0 ···
3 0 ···
4 0 ···
·
··
n 0
Dynamic programming
46
This table has n2 entries, and therefore our run time must be at least
W(n2)
Matrix Dimensions
A1 20 × 5
A2 5 × 40
A3 40 × 50
A4 50 × 10
we can calculate the table as follows...
Dynamic programming
48
1 2 3 4
Matrix Dimensions 1 0 4000
A1 20 × 5 20×5 20×40
A2 5 × 40 2 0 10000
5×40 5×50
A3 40 × 50
3 0 20000
A4 50 × 10 40×50 40×10
4 0
50×10
Dynamic programming
49
A2 5 × 40 2 0 10000
5×40 5×50
A3 40 × 50
3 0 24000
A4 50 × 10 40×50 40×10
4 0
50×10
Dynamic programming
50
We continue:
A2(A3A4) 5 × 40 × 10 + 20000 =
22000 (A2A3)A4 10000 + 5 × 50 × 10 =
12500
1 2 3 4
Matrix Dimensions 1 0 4000 15000
A1 20 × 5 20×5 20×40 20×50
A2 5 × 40 2 0 10000 12500
5×40 5×50 5×10
A3 40 × 50
3 0 20000
A4 50 × 10 40×50 40×10
4 0
50×10
Dynamic programming
51
Finally we calculate:
A1((A2A3)A4) 20 × 5 × 10 + 12500 =
13500
(A1A2)(A3A4) 4000 + 20 × 40 × 10 + 20000 =
32000
1 2
(A1(A2A3))A4 15000 + 20 ×3 50 × 104 =
Matrix Dimensions 1 0 4000 15000 13500
25000
A 20 × 5 20×5 20×40 20×50 20×10
1
A2 5 × 40 2 0 10000 12500
5×40 5×50 5×10
A3 40 × 50
3 0 20000
A4 50 × 10 40×50 40×10
4 0
50×10
Dynamic programming
52
b≈2 b≈3
Dynamic programming
56
Or 3 and 4
Dynamic programming
67
Interval scheduling
Process Interval
A problem we saw in the topic on
greedy algorithms was interval A 5 –
8
scheduling
B 10 –
13
C 6 –
9
D 12 –
15
E 3 –
7
F 8 –
11
G 1 –
6
H 8 –
12
J 3 –
Dynamic programming
71
Interval scheduling
Process Interval
The earliest-deadline-first greedy
algorithm will maximize the number A 5 –
8
of processes that can be scheduled
B 10 –
13
C 6 –
9
D 12 –
15
E 3 –
7
F 8 –
11
G 1 –
6
H 8 –
12
J 3 –
Dynamic programming
72
Interval scheduling
Interval scheduling
Interval scheduling
Interval scheduling
Interval scheduling
Interval scheduling
Interval scheduling
Interval scheduling
return std::max(
weight[n] + optimal_weight( previous[n] ),
optimal_weight( n - 1 )
);
}
Dynamic programming
80
Interval scheduling
Interval scheduling
In the optimal case where we can run every process, the run time is
exponential:
1 n 0
T n 2
n
2T n 1 1 n 0
Dynamic programming
82
Interval scheduling
if ( clear ) {
memo.clear();
}
if ( n == 0 ) {
return 0;
} else if ( memo[n] == 0 ) {
memo[n] = std::max(
weight[n] + optimal_weight_memo( previous[n], false ),
optimal_weight_memo( n – 1, false )
);
}
return memo[n];
}
Dynamic programming
83
Interval scheduling
return optimal[n];
}
Dynamic programming
84
Interval scheduling
return optimal[n];
}
Dynamic programming
85
Project management
0/1 knapsack problem
Recall our problem of project management or 0/1 knapsack problem
– The constraint W is an integer value
– There are n items we could include:
• The nth item has an integral weight wk
• Its value is vk
– The optimal value of objects we can place in the knapsack is:
0 k 0
knapsack k , w knapsack k 1, w wk w
max knapsack k 1, w , knapsack k 1, w w v w w
k k k
double knapsack( int *weight, double *value, int k, int W, bool clear = true ) {
static std::map< std::pair< int, int >, double > memo;
if ( clear ) memo.clear();
if ( k == 0 ) {
return 0;
} else {
if ( memo.find( std::pair<int, int>( k, W ) ) == memo.end() ) {
if ( weight[k - 1] > W ) {
memo[std::pair<int, int>( k, W )] = knapsack( weight, value, k - 1, W,
false );
} else {
memo[std::pair<int, int>( k, W )] = std::max(
knapsack( weight, value, k - 1, W, false ),
knapsack( weight, value, k - 1, W - weight[k - 1], false ) + value[k
- 1]
);
}
}
is 520, return
which is0;the optimal result
}
Dynamic programming
88
As we saw before, this does not significantly affect the run time
> F := proc(n)
option remember; Maple’s version of
printf( "Calling F(%d)\n", n ); “this” function
procname( n - 1 ) + procname( n - 2 );
end proc:
F( 0 ) := 1: # assign F(0) = 1 to remember table
F( 1 ) := 1: # assign F(1) = 1 to remember table
Dynamic programming
91
Recall however, that remember tables are hash tables (with 1024
bins)
Therefore, they can fill up, and therefore accessing previous results
can become expensive
Dynamic programming
93
Applications
Applications
And finally:
– the Duckworth-Lewis method for calculating the target score in an
interrupted cricket match:
• On the 2006 One-Day International between India and Pakistan, India batted first and
was all out in the 49th over for 328
• Pakistan was 7 wickets down for 311 when bad light stopped play after the 47th over
• With three full overs left to play and three wickets in hand, the Duckworth-Lewis method
calculated the target to be 304, so the result of the match is officially listed as
“Pakistan won by 7 runs (D/L Method)”
Jono4174
http://en.wikipedia.org/wiki/Duckworth-Lewis_method
Dynamic programming
96
Summary
References
Wikipedia, http://en.wikipedia.org/wiki/Topological_sorting
[1] Cormen, Leiserson, and Rivest, Introduction to Algorithms, McGraw Hill, 1990, §11.1, p.200.
[2] Weiss, Data Structures and Algorithm Analysis in C++, 3 rd Ed., Addison Wesley, §9.2, p.342-5.
These slides are provided for the ECE 250 Algorithms and Data Structures course. The
material in it reflects Douglas W. Harder’s best judgment in light of the information available to
him at the time of preparation. Any reliance on these course slides by any party for any other
purpose are the responsibility of such parties. Douglas W. Harder accepts no responsibility for
damages, if any, suffered by any party as a result of decisions made or actions based on these
course slides for any other purpose than that for which it was intended.