DAA Unit3
DAA Unit3
DAA Unit3
DYNAMIC PROGRAMMING
PRINCIPLE OF OPTIMALITY:
The principle of optimality states that, “In an optimal sequence of decisions each
subsequence must also be optimal”.
(OR)
The optimal solution to a problem is composed of optimal solutions to the
subproblems.
→ We are given a set of ‘n’ items (or, objects), such that each item i has a weight
‘wi’ and a profit ‘𝑝i’. We wish to pack a knapsack whose capacity is ‘M’ with a
subset of items such that total profit is maximized.
→ The solution to this problem is expressed as a vector (x1, x2, …, xn), where each
xi is 1 if object i has been placed in knapsack, otherwise xi is 0.
maximize
subject to the constraints:
and
i {0,1}, 1 ≤ i ≤ n
→ We need to make the decisions on the values of 𝑥1, 𝑥2, 𝑥3, …, 𝑥n.
Let f(i, y) denote the value (or profit), of an optimal solution to the knapsack
instance with remaining capacity y and remaining objects i, i+1,…, n for which
decisions have to be taken.
It follows that
𝑝 , 𝑖𝑓 𝑤 ≤ 𝑦
𝑓(𝑛, 𝑦) = -------(1)
0, 𝑖𝑓 𝑤 > 𝑦
and
𝑚𝑎𝑥 𝑓(𝑖 + 1, 𝑦), 𝑝𝑖 + 𝑓 (𝑖 + 1, 𝑦 − 𝑤𝑖 ) , 𝑖𝑓 𝑤 ≤ 𝑦
𝑓(𝑖, 𝑦) = --(2)
𝑓 (𝑖 + 1, 𝑦), 𝑖𝑓 𝑤 > 𝑦
NOTE: when 𝑤i > y, we cannot place the object i, and there is no choice to make,
but when 𝑤i ≤ 𝑦 we have two choices, viz., if object i can be placed or not. Here
we will consider the choice into account which results in maximum profit.
→ f(1, M) is the value (profit) of the optimal solution to the knapsack problem we
started with. Equation (2) may be used recursively to determine f(1, M).
-----------------------------------------------------------------------------------------------
Example 1: Let us determine f(1, M) recursively for the following 0/1 knapsack
instance: n=3 ; (𝑤1, 𝑤2, 𝑤3) = (100,14,10) ; (𝑝1, 𝑝2, 𝑝3) = (20,18,15) and
M=116.
Solution:
f(1,116) =max{f (2, 116), 20 + f(2, 116 − 100)} , since w1<116
=max{f (2, 116), 20 + f(2, 16)}
f(2,116) =max{f (3, 116), 18 + f(3, 116 − 14)} , since w2<116
=max{f (3, 116), 18 + f(3, 102)}
f(3,116) =15 (since w3<116)
f(3,102) =15 (since w3<102)
TIME COMPLEXITY:
→ let T(n) be the time this code takes to solve an instance with n
objects.
𝑐, 𝑖𝑓 𝑛 = 1
So, 𝑇(𝑛) =
2𝑇(𝑛 − 1) + 𝑐, 𝑖𝑓 𝑛 > 1
Solving this, we get time complexity equal to O(2𝑛).
→ In general, if there are d choices for each of the n decisions to be made, there
will be 𝑑𝑛 possible decision sequences.
Example: Consider the case n=5; M=10; (𝑤1, 𝑤2, 𝑤3, 𝑤4, 𝑤5) = (2,2,6,5,4); (𝑝1,
𝑝2, 𝑝3, 𝑝4, 𝑝5) = (6,3,5,4,6).
Solution: To determine f(1,10), function f is invoked as f(1,10). The recursive calls
made are shown by the following tree of recursive calls.
→ 27 invocations are done on f. We notice that several invocations redo the work
of previous invocation. For example: f(3,8), f(4,8), f(4,2), f(5,8), f(5,3), f(5,2) are
computed twice.
If we save the result of previous invocations, we can reduce the number of
invocations to 21.
We maintain a table to store the values, as and when the function call computes
there.
By using these table values, we can avoid re-computing the function, when it is
again invoked.
When the recursive program is designed to avoid the re-computation, the
complexity is drastically reduced from exponential to polynomial.
NOTE:
1. If 𝑠 contains two pairs (𝑝 , 𝑤 ) and (𝑝 , 𝑤 ) with the property that 𝑝 ≤
𝑝 and 𝑤 ≥ 𝑤 , we say that (𝑝 , 𝑤 ) dominates (𝑝 , 𝑤 ) and the
dominated tuple (𝑝 , 𝑤 ) can be discarded from 𝑠 . This rule is called
purging rule.
2. By this rule all duplicate tuples will also be purged.
3. We can also purge all pairs (𝑝 , 𝑤 ) with 𝑤 > 𝑀.
Finally profit for the optimal solution is given by p value of the last pair in 𝑠 set.
Tracing back values of 𝒙𝒊 ’s:
Suppose that (𝑝 , 𝑤 ) is the last tuple in 𝑠 , then a set of 0/1 values for the 𝑥 ’s
can be determined by carrying out a search through the 𝑠 sets.
if (𝑝 , 𝑤 ) ∈ 𝑠 , then we will set 𝑥 = 0.
If (𝑝 , 𝑤 )∉ 𝑠 , then (𝑝 − 𝑝 , 𝑤 − 𝑤 ) ∈ 𝑠 and we will set 𝑥 = 1. This
process can be done recursively to get remaining 𝑥 values.
Example: Consider the knapsack instance:
n = 3, (𝑤 , 𝑤 , 𝑤 ) = (2, 3, 4) and (𝑝 , 𝑝 , 𝑝 ) = (1,2,5), and M = 6. Generate the
sets 𝑠 and find the optimal solution.
Solution:
𝑠 = {(0,0)};
By including object 1,
𝑠 = {(1,2)};
By merging 𝑠 𝑎𝑛𝑑 𝑠 , we get
𝑠 = {(0,0), (1,2)};
By including object 2,
𝑠 ={(2,3), (3, 5)};
By merging 𝑠 𝑎𝑛𝑑 𝑠 , we get
𝑠 = {(0, 0), (1, 2), (2, 3), (3, 5)};
By including object 3,
𝑠 = {(5, 4), (6, 6), (7, 7), (8, 9)} = {(5, 4), (6, 6)};
By merging 𝑠 𝑎𝑛𝑑 𝑠 , we get
𝑠 = {(0,0), (1,2), (2, 3), (3, 5), (5, 4), (6, 6)};
By applying the purging rule, the tuple (3, 5) will get discarded.
𝑠 ={(0,0), (1,2), (2, 3), (5, 4), (6, 6)};
Tracing out the values of 𝑥 :
The last tuple in 𝑠 is (6,6) ∉ 𝑠 . So, 𝑥 =1.
The last tuple (6,6) of 𝑠 came from a tuple (6 − 𝑝 , 6 − 𝑤 ) = (6-5, 6-4) = (1, 2)
belonging to 𝑠 .
The tuple (1, 2) of 𝑠 , is also present in 𝑠 . So, 𝑥 = 0.
The tuple (1, 2) of 𝑠 , is not present in 𝑠 . So, 𝑥 = 1.
So, the optimal solution is: (𝑥 , 𝑥 , 𝑥 ) = (1,0,1)
2. Bottom-up method:
This approach typically depends on some natural notion of the “size” of a
subproblem, such that solving any particular subproblem depends only on solving
“smaller” subproblems. We sort the subproblems by size and solve them in size
order, smallest first. When solving a particular subproblem, we have already
solved all of the smaller subproblems its solution depends upon, and we have
saved their solutions. We solve each subproblem only once, and when we first see
it, we have already solved all of its prerequisite subproblems.
Initially this function is invoked as f(n, M). This problem’s size is n (i.e., the
number of objects on which decisions have to be taken). Next, it calls the
subproblem f(n-1, y) whose size is n-1, and so on. This recursion is repeated until a
problem of smallest size i.e., f(1, y) is called.
Algorithm f(i, y)
{
// Let T[0:n, 0:M] be a global two dimensional array whose elements are
// initialized with -1 except for row 0 and column 0 which are initialized with 0’s.
if (T[i, y] <0) then // if f(i, y) has not been computed previously
{
if(w[i]>y) then
{
T[i, y] := f(i-1, y);
}
else
{
T[i, y] := max(f(i-1, y), p[i] + f(i-1, y-w[i]));
}
}
return T[i, y];
}
Initially this function is invoked as f(n, M).
What is the time and space complexity of the above solution?
Since our memoization array T[0:n, 0:M] stores the results for all the subproblems,
we can conclude that we will not have more than (n+1)*(M+1) subproblems
(where ‘n’ is the number of items and ‘M’ is the knapsack capacity). This means
that the time complexity will be O(n*M).
The above algorithm will be using O(n*M) space for the memoization array T.
Other than that, we will use O(n) space for the recursion call-stack. So, the total
space complexity will be O(n*M+n), which is asymptotically equivalent
to O(n*M).
The optimal solution is: (x1, x2, x3, x4, x5) = (1, 1, 0, 0, 1).
Solution to 0/1 Knapsack problem using Bottom-up Dynamic Programming
approach:
Step-01:
Draw a table say ‘T’ with (n+1) number of rows and (M+1) number of
columns.
Fill all the boxes of 0th row and 0th column with zeroes as shown below:
0 1 2 3 … M
0 0 0 0 0 … 0
Row indices (i values)
1 0
2 0
3 0
… …
N 0
Step-02:
Start filling the table row wise top to bottom from left to right.
Use the following formula:
T[i , y] = max { T [ i-1 , y ] , pi + T[ i-1 , y – wi ] } , 𝑖𝑓 𝑦 ≥ 𝑤
= T [ i-1 , y ], 𝑖𝑓 𝑦 < 𝑤
Here, T[i , y] = maximum profit earned by taking decisions on items 1 to i with
remaining capacity y.
This step leads to completely filling the table.
Then, value of the last cell (i.e., intersection of last row and last column)
represents the maximum possible profit that can be earned.
Step-03:
To identify the items that must be put into the knapsack to obtain that maximum
profit (that means to trace back the values of xi),
Consider the last entry (i.e., cell) of the table.
Start scanning the entries from bottom to top.
On encountering an entry whose value is not same as the value stored in the
entry immediately above it, mark the row label of that entry.
After all the entries are scanned, the marked labels represent the items that
must be put into the knapsack.
Algorithm:
Algorithm KnapSack(n, M)
{
// Let T[0:n, 0:M] be a global two dimensional array whose elements in row 0 and
// column 0 are initialized with 0’s.
for i:=1 to n do
{
for y:=1 to M do
{
if(w[i]>y) then
{
T[i, y] := T[i-1, y];
}
else
{
T[i, y] := max(T[i-1, y], p[i] + T[i-1, y-w[i]]);
}
}
}
It takes θ(nM) time to fill (n+1)(M+1) table entries. This means that the time
complexity will be O(n*M). Even though it appears to be polynomial time but
actually it is called pseudo polynomial time because when M≥2n, the time
complexity is actually exponential but not polynomial.
p1=6
w2=2, 2 0 0 6 6 9 9 9 9 9 9 9
p2=3
w3=6, 3 0 0 6 6 9 9 9 9 11 11 14
p3=5
w4=5, 4 0 0 6 6 6 9 9 10 11 13 14
p4=4
W5=4, 5 0 0 6 6 6 9 12 12 12 15 15
p5=6
The optimal solution is: (x1, x2, x3, x4, x5) = (1, 1, 0, 0, 1).
---------------------------------------------------------------------------------------------------------------------
MATRIX CHAIN MULTIPLICATION:
→ Consider the case n=4. The matrix product M1*M2*M3*M4 may be computed
in any of the following 5 ways.
1. M1*((M2*M3)*M4)
2. M1*(M2*(M3*M4))
3. (M1*M2)*(M3*M4)
4. ((M1*M2)*M3)*M4
5. (M1*(M2*M3))*M4
Example:
Consider three matrices A2x3, B3x4, C4x5.
The product A*B*C can be computed in two ways: (AB)C and A(BC).
The cost of performing (AB)C is: 2*3*4+2*4*5 = 64.
The cost of performing A(BC) is: 3*4*5+2*3*5 = 90.
So, the optimal (i.e., best) order of multiplication is (AB)C.
(OR)
The best way of parenthesizing the given matrix chain multiplication ABC is,
(AB)C.
→ Let 𝑀i, j denote the result of the product chain 𝑀i∗ 𝑀i+1∗ …∗𝑀j, i<j.
Ex: M1*M2*M3 = 𝑀1,3.
→ In order to compute 𝑀i, j optimally, 𝑀i, k and 𝑀k+1, j should also be computed
optimally. Hence, the principle of optimality holds.
→ This suggests the following recurrence equation for computing c(i, j):
→ The above recurrence equation for c may be solved recursively. The k value
which results in c(i, j) is denoted by kay(i, j).
→ c(1, n) is the cost of the optimal way to compute the matrix product chain 𝑀1,𝑛.
And kay(1, n) defines the last product to be done or where the splitting is done.
→ The remaining products can be determined by using kay values.
The tree of recursive calls of c( ) function for the matrix product chain
M1*M2*M3*M4:
SOLUTION FOR MATRIX CHAIN MULTIPLICATION:
For j – i = 1:
For j – i = 2:
=min{0+20+5*1*2, 50+0+5*10*2}
=min{30, 150}
= 30 kay(2,4)=2
For j – i = 3:
kay(1,5)=2
Algorithm MATRIX-CHAIN-ORDER(r)
{
n := length(r) -1; // n denotes number of matrices
for i:= 1 to n do
c[i, i]:= 0;
for l := 2 to n do // l is the chain length
{
for i := 1 to n-l+1 do // n-l+1 gives number of cells in the current row
{
j := i+l-1;
c[i, j] := ∞;
for k := i to j-1 do
{
q := c[i, k] + c[k+1, j] + ri*rk+1*rj+1;
if q < c[i, j] then
{
c[i, j]:= q;
kay[i, j]:= k;
}
}
}
}
return c and kay;
}
Algorithm Allpaths(c, A, n)
{
//c[1:n,1:n] is the cost adjacency matrix of a graph with n vertices.
//A[i, j] is length of a shortest path from vertex i to vertex j.
//c[i, i]=0, for 1≤i≤n.
for i:=1 to n do
for j:=1 to n do
A[i, j]=c[i, j]; //copy c into A
for k:=1 to n do
for i=1 to n do
for j=1 to n do
A[i, j]:=min(A[i, j], A[i, k]+A[k, j]);
}
EXAMPLE: - Find out shortest paths between all pairs of vertices in the following
digraph.
Step-1: A0 ← c Step-2: Using, A1[i, j] = min{A0[i, j],
A0[i, 1]+A0[1, j]}
A1
0 4 11
0 4 11
6 0 2
6 0 2
3 ∞ 0
3 7 0
Step-3: Using, A2[i, j] = min{A1[i, j], Step-4: Using, A3[i, j] = min{A2[i, j],
A1[i, 2]+A1[2, j]} A2[i, 3]+A2[3, j]}
A2 A3
0 4 6 0 4 6
6 0 2 5 0 2
3 7 0
3 7 0
Exercises:
(1)
(2)
----------------------------------------------------------------------------------------------------
The Traveling Salesperson problem: -
Given a set of cities and distances between every pair of cities, the
problem is to find the shortest possible route that visits every city
exactly once and returns to the starting point.
Let the given set of vertices be {1, 2, …., n}. In our discussion, we
shall consider a tour be a simple path that starts at vertex 1 and
terminates at 1.
Every possible tour can be viewed as consisting of an edge <1, k> for
some k∈ V-{1} and a path from vertex k to vertex 1.
The path from vertex k to vertex 1 goes through each vertex in the set V-
{1, k} exactly once.
Suppose the tour consisting of the edge <1, k> (for some k∈ V-{1})
followed by a path from k to 1 is an optimal tour. Then the path from k
to 1 should also be optimal. Thus, the principle of optimality holds.
Let g(i, S) denote the length of the shortest path starting at vertex i,
going through all vertices in set S, and terminating at vertex 1. Thus g(1,
V-{1}) gives the length of an optimal tour.
If the optimal tour consists of the edge <1, k>, then from the principle
of optimality, it follows that g(1, V-{1})= C1k+g(k, V-{1,k}).
But we don’t know for which value of k the tour will be optimal. We
know that k take can any value from the set {2, 3, …, n}.
The RHS of the above recurrence can be evaluated for each value of k
and the minimum of those results should be assigned to g(1, V-{1})
resulting in the following recurrence equation:
g(1, V-{1}) = min2≤k≤n {C1k+g(k, V-{1,k})} ------- (1)
Generalizing equation (1) for i not in S, we obtain
g(i, S)=min∀ j∈S {Cij+g(j, S-{j})} ------ (2)
Equation (1) can be solved for g(1, V-{1}) if we know g(k, V-{1,k})
for all choices of k which can be obtained by using equation (2).
Clearly, g(i, Ø)=Ci1, 1≤i≤n.
The optimal tour can be found by noting the vertex which resulted in
minimum cost at each stage.
An algorithm that proceeds to find an optimal tour by making use of (1)
and (2) will require
O(n 22n) time.