DAA Module 4
DAA Module 4
Dynamic Programming
and
THE GREEDY METHOD
Dynamic Programming
▪ The Dynamic programming can also be used when the solution to a problem can be
viewed as the result of sequence of decisions.
▪ There is a row of n coins whose values are some positive integers c1, c2, ..., cn, not
necessarily distinct.
▪ The goal is to pick up the maximum amount of money subject to the constraint
that no two coins adjacent in the initial row can be picked up.
▪ Let F(n) be the maximum amount that can be picked up from the row of n coins.
▪ To derive a recurrence for F(n), we partition all the allowed coin selections into two
groups: those that include the last coin and those without it.
▪ The largest amount we can get from the first group is equal to cn +F(n−2)—the
value of the nth coin plus the maximum amount we can pick up from the first n−2
coins.
▪ The maximum amount we can get from the second group is equal to F(n−1) by the
definition of F(n).
▪ Thus, we have the following recurrence subject to the obvious initial conditions:
▪ Using the Coin-Row to find F(n), the largest amount of money that can be picked
up, as well as the coins composing an optimal set, clearly takes Ɵ(n) time and Ɵ(n)
space.
The time and space efficiencies of the algorithm are obviously O(nm) and Ɵ(n),
respectively.
▪ Several coins are placed in cells of an n × m board, no more than one coin per cell.
▪ A robot, located in the upper left cell of the board, needs to collect as many of
the coins as possible and bring them to the bottom right cell.
▪ On each step, the robot can move either one cell to the right or one cell down
from its current location.
▪ When the robot visits a cell with a coin, it always picks up that coin.
▪ Design an algorithm to find the maximum number of coins the robot can collect
and a path it needs to follow to do this.
▪ Let F(i,j) be the largest number of coins the robot can collect and bring to the cell
(i, j) in the ith row and jth column of the board.
▪ It can reach this cell either from the adjacent cell (i − 1, j) above it or from the
adjacent cell (i, j − 1) to the left of it.
▪ The largest numbers of coins that can be brought to these cells are F(i−1, j) and F(i,
j−1), respectively.
▪ Of course, there are no adjacent cells above the cells in the first row, and there
are no adjacent cells to the left of the cells in the first column.
▪ For those cells, we assume that F(i− 1,j) and F(i,j−1) are equal to 0 for their
nonexistent neighbors.
▪ Therefore, the largest number of coins the robot can bring to cell (i, j) is the
maximum of these two numbers plus one possible coin at cell (i, j) itself.
▪ Using these formulas, we can fill in the n×m table of F(i,j) values either row by row
or column by column, as is typical for dynamic programming algorithms involving
two-dimensional tables.
▪ This yields two optimal paths for the instance in Figure 8.3a, which are shown in
Figure 8.3c.
▪ If ties are ignored, one optimal path can be obtained in Ɵ(n+m) time.
▪ We start this section with designing a dynamic programming algorithm for the
knapsack problem: given n items of known weights w1, . . . , wn and values v1, . . . , vn
and a knapsack of capacity W, find the most valuable subset of the items that fit into
the knapsack.
▪ To design a dynamic programming algorithm, we need to derive a recurrence relation
that expresses a solution to an instance of the knapsack problem in terms of solutions
to its smaller subinstances.
▪ Let us consider an instance defined by the first i items, 1≤ i ≤ n, with weights w1, . . . ,
wi, values v1, . . . , vi , and knapsack capacity j, 1 ≤ j ≤ W.
▪ Let F(i, j) be the value of an optimal solution to this instance.
▪ We can divide all the subsets of the first i items that fit the knapsack of capacity j into
two categories: those that do not include the ith item and those include. Note the
following:
Among the subsets that do not include the ith item, the value of an optimal
subset is, by definition, F(i − 1, j).
Among the subsets that do include the ith item (hence, j − wi ≥ 0), an optimal
subset is made up of this item and an optimal subset of the first i−1 items that
fits into the knapsack of capacity j − wi . The value of such an optimal subset is vi
+ F(i − 1, j − wi).
▪ Our goal is to find F(n, W), the maximal value of a subset of the n given items that fit
into the knapsack of capacity W, and an optimal subset itself.
Figure: Example of solving an instance of the knapsack problem by the memory function algorithm.
In general, we cannot expect more than a constant-factor gain in using the memory
function method for the knapsack problem, because its time efficiency class is the same
as that of the bottom-up algorithm.
Definition:
The transitive closure of a directed graph with n vertices can be defined as the n×n
boolean matrix T = { tij }, in which the element in the ith row and the jth column is 1 if
there exists a nontrivial path (i.e., directed path of a positive length) from the ith vertex
to the jth vertex; otherwise, tij is 0.
Example:
An example of a digraph, its adjacency matrix, and its transitive closure is given below.
▪ Since this method traverses the same digraph several times, we can use a better
algorithm called Warshall’s algorithm.
▪ Warshall’s algorithm constructs the transitive closure through a series of n × n
boolean matrices:
▪ Each of these matrices provides certain information about directed paths in the
digraph.
▪ Specifically, the element rij(k) in the ith row and jth column of matrix R(k) (i, j = 1, 2, . . .
, n, k = 0, 1, . . . , n) is equal to 1 if and only if there exists a directed path of a positive
length from the ith vertex to the jth vertex with each intermediate vertex, if any,
numbered not higher than k.
--- (*)
Space efficiency: Although separate matrices for recording intermediate results of the
algorithm are used, that can be avoided.
Problem definition:
Given a weighted connected graph (undirected or directed), the all-pairs shortest
paths problem asks to find the distances—i.e., the lengths of the shortest paths -
from each vertex to all other vertices.
Applications:
▪ Solution to this problem finds applications in communications, transportation
networks, and operations research.
▪ Among recent applications of the all-pairs shortest-path problem is pre-computing
distances for motion planning in computer games.
▪ We store the lengths of shortest paths in an n x n matrix D called the distance matrix:
the element dij in the ith row and the jth column of this matrix indicates the length of
the shortest path from the ith vertex to the jth vertex.
▪ The element d(k) in the ith row and the jth column of matrix D(k) (i, j = 1, 2, . . . , n, k =
0, 1, . . . , n) is equal to the length of the shortest path among all paths from the ith
vertex to the jth vertex with each intermediate vertex, if any, numbered not higher
than k.
▪ As in Warshall’s algorithm, we can compute all the elements of each matrix D(k) from
its immediate predecessor D(k−1)
▪ We can partition all such paths into two disjoint subsets: those that do not use the
kth vertex vk as intermediate and those that do.
▪ Since the paths of the first subset have their intermediate vertices numbered not
higher than k − 1, the shortest of them is, by definition of our matrices, of length dij
(k–1)
In the second subset the paths are of the form.
vi , vertices numbered ≤ k − 1 , vk , vertices numbered ≤ k − 1, vj
DESIGN AND ANALYSIS OF ALGORITHMS Module-4
▪ The situation is depicted symbolically in Figure, which shows the underlying idea of
Floyd’s algorithm.
▪ Taking into account the lengths of the shortest paths in both subsets leads to the
following recurrence
▪ The initial sub-tree in such a sequence consists of a single vertex selected arbitrarily
from the set V of the graph's vertices.
▪ On each iteration it expands the current tree in the greedy manner by simply
attaching to it the nearest vertex not in that tree. (By the nearest vertex, we mean a
vertex not in the tree connected to a vertex in the tree by an edge of the smallest
weight. Ties can be broken arbitrarily.)
▪ The algorithm stops after all the graph's vertices have been included in the tree being
constructed.
▪ Since the algorithm expands a tree by exactly one vertex on each of its iterations, the
total number of such iterations is n-1, where n is the number of vertices in the graph.
The tree generated by the algorithm is obtained as the set of edges.
The efficiency of Prim’s algorithm depends on the data structures chosen for the graph
itself and for the priority queue of the set V − VT whose vertex priorities are the distances
to the nearest tree vertices.
1. If a graph is represented by its weight matrix and the priority queue is implemented
as an unordered array, the algorithm’s running time will be in Θ(|V|2). Indeed, on
each of the |V|−1 iterations, the array implementing the priority queue is traversed
to find and delete the minimum and then to update, if necessary, the priorities of the
remaining vertices.
Deletion of the smallest element from and insertion of a new element into a min-heap
of size n are O(log n) operations.
This is because the algorithm performs |V|−1 deletions of the smallest element and
makes |E| verifications and, possibly, changes of an element’s priority in a min-heap
of size not exceeding |V|.
Background
▪ Kruskal's algorithm is another greedy algorithm for the minimum spanning tree
problem that also always yields an optimal solution.
▪ It is named Kruskal's algorithm, after Joseph Kruskal.
▪ Kruskal's algorithm looks at a minimum spanning tree for a weighted connected
graph G = (V, E) as an acyclic sub graph with |V |-1 edges for which the sum of the
edge weights is the smallest.
▪ Consequently, the algorithm constructs a minimum spanning tree as an expanding
sequence of sub graphs, which are always acyclic but are not necessarily connected
on the intermediate stages of the algorithm.
Working
▪ The algorithm begins by sorting the graph's edges in non decreasing order of their
weights.
▪ Then, starting with the empty sub graph, it scans this sorted list adding the next edge
on the list to the current sub graph if such an inclusion does not create a cycle and
simply skipping the edge otherwise.
▪ Kruskal’s algorithm is not simpler because it has to check whether the addition of
the next edge to the edges already selected would create a cycle.
▪ The initial forest consists of |V| trivial trees, each comprising a single vertex of the
graph. The final forest consists of a single tree, which is a minimum spanning
tree of the graph.
▪ On each iteration, the algorithm takes the next edge (u, v) from the sorted list of
the graph's edges, finds the trees containing the vertices u and v, and, if these trees
are not the same, unites them in a larger tree by adding the edge (u, v).
▪ The crucial check whether two vertices belong to the same tree can be found out using
union- find algorithms.
▪ Efficiency of Kruskal’s algorithm is based on the time needed for sorting the edge
weights of a given graph.
▪ Hence, with an efficient sorting algorithm, the time efficiency of Kruskal's algorithm will
be in O (|E| log |E|).
▪ Definition: For a given vertex called the source in a weighted connected graph, the
problem is to find shortest paths to all its other vertices.
▪ The single-source shortest-paths problem asks for a family of paths, each leading from
the source to a different vertex in the graph, though some paths may, of course, have
edges in common.
Working :
Dijkstra's algorithm finds the shortest paths to a graph's vertices in order of their distance
from a given source.
▪ First, it finds the shortest path from the source to a
vertex nearest to it, then to a second nearest, and so
on.
▪ In general, before its ith iteration commences, the
algorithm has already identified the shortest paths to
i-1 other vertices nearest to the source.
▪ These vertices, the source, and the edges of the
shortest paths leading to them from the source form
a sub-tree Ti of the given graph shown in the figure.
▪ To identify the ith nearest vertex, the algorithm computes, for every fringe vertex u,
the sum of the distance to the nearest tree vertex v (given by the weight of the edge
(v, u)) and the length d, of the shortest path from the source to v (previously
determined by the algorithm) and then selects the vertex with the smallest such
sum.
▪ The fact that it suffices to compare the lengths of such special paths is the central
insight of Dijkstra's algorithm.
✔ The numeric label d indicates the length of the shortest path from the source
to this vertex found by the algorithm so far; when a vertex is added to the
tree, d indicates the length of the shortest path from the source to that vertex.
✔ The other label indicates the name of the next-to-last vertex on such a path,
i.e., the parent of the vertex in the tree being constructed. (It can be left
unspecified for the sources and vertices that are adjacent to none of the
current tree vertices.)
✔ With such labeling, finding the next nearest vertex u* becomes a simple task of
finding a fringe vertex with the smallest d value. Ties can be broken arbitrarily.
▪ After we have identified a vertex u* to be added to the tree, we need to perform two
operations:
✔ Move u* from the fringe to the set of tree vertices.
✔ For each remaining fringe vertex u that is connected to u* by an edge of weight
w (u*, u) such that d u*+ w(u*, u) <d u, update the labels of u by u* and du* +
w(u*, u), respectively.
Applications:
▪ Transportation planning and packet routing in communication networks, including
the Internet.
▪ Finding shortest paths in social networks, speech recognition, document formatting,
robotics, compilers, and airline crew scheduling.
Background
▪ Suppose we have to encode a text that comprises characters from some n-symbol
alphabet by assigning to each of the text's characters some sequence of bits called
the codeword.
▪ There are two types of encoding: Fixed-length encoding, Variable-length encoding
Fixed-length encoding:
This method assigns to each character a bit string of the same length m, (m>=
log2 n).
This is exactly what the standard ASCII code does.
One way of getting a coding scheme that yields a shorter bit string on the
average is based on the old idea of assigning shorter code-words to more
frequent characters and longer code-words to less frequent characters .
▪ Among the many trees that can be constructed in this manner for a given alphabet
with known frequencies of the character occurrences, construction of such a tree
that would assign shorter bit strings to high-frequency characters and longer ones to
low-frequency characters can be done by the following greedy algorithm, invented by
David Huffman.
Huffman's Algorithm:
Step 1:
Initialize n one-node trees and label them with the characters of the alphabet.
Record the frequency of each character in its tree's root to indicate the tree's
weight. (More generally, the weight of a tree will be equal to the sum of the
frequencies in the tree's leaves.)
Step 2:
Repeat the following operation until a single tree is obtained.
Find two trees with the smallest weight.
Make them the left and right sub-tree of a new tree and record the sum of their
weights in the root of the new tree as its weight.
▪ Had we used a fixed-length encoding for the same alphabet, we would have to use
at least 3 bits per each symbol.
▪ Thus, for this example, Huffman’s code achieves the compression ratio (a standard
measure of a compression algorithm’s effectiveness) of (3−2.25)/3*100%= 25%.
▪ In other words, Huffman’s encoding of the above text will use 25% less memory than
its fixed-length encoding