Ada Module 4 Notes
Ada Module 4 Notes
Prepared by
Dr.A.Kannagi
UNIT 4: Greedy Algorithms -I
2
approach. This property is called optimal substructure.
3
return solution;
}
}
The above is the greedy algorithm. Initially, the solution is assigned with zero
value. We pass the array and number of elements in the greedy algorithm. Inside
the for loop, we select the element one by one and checks whether the solution is
feasible or not. If the solution is feasible, then we perform the union.
P:A→B
The problem is that we have to travel this journey from A to B. There are
various solutions to go from A to B. We can go from A to B by walk, car, bike,
train, aeroplane, etc. There is a constraint in the journey that we have to travel this
journey within 12 hrs. If I go by train or aeroplane then only, I can cover this
distance within 12 hrs. There are many solutions to this problem but there are only
two solutions that satisfy the constraint.
If we say that we have to cover the journey at the minimum cost. This means
that we have to travel this distance as minimum as possible, so this problem is
known as a minimization problem. Till now, we have two feasible solutions, i.e.,
one by train and another one by air. Since travelling by train will lead to the
minimum cost so it is an optimal solution. An optimal solution is also the feasible
solution, but providing the best result so that solution is the optimal solution with
the minimum cost. There would be only one optimal solution.
The problem that requires either minimum or maximum result then that
problem is known as an optimization problem. Greedy method is one of the
strategies used for solving the optimization problems.
4
Disadvantages of using Greedy algorithm
5
2) Assign a key value to all vertices in the input graph. Initialize all key values as INFINITE.
Assign key value as 0 for the first vertex so that it is picked first.
3) While mstSet doesn’t include all vertices
Pick a vertex u which is not there in mstSet and has minimum key value.
Include u to mstSets
Update key value of all adjacent vertices of u. To update the key values, iterate through
all adjacent vertices. For every adjacent vertex v, if weight of edge u-v is less than the
previous key value of v, update the key value as weight of u-v
Let us understand with the following example:
Consider the following graph as an example for which we need to find the
Minimum Spanning Tree (MST).
Example of a graph
Step 1: Firstly, we select an arbitrary vertex that acts as the starting vertex of the
Minimum Spanning Tree. Here we have selected vertex 0 as the starting vertex.
6
0 is selected as starting vertex
Step 2: All the edges connecting the incomplete MST and other vertices are the
edges {0, 1} and {0, 7}. Between these two the edge with minimum weight is {0,
1}. So include the edge and vertex 1 in the MST.
Step 3: The edges connecting the incomplete MST to other vertices are {0, 7}, {1,
7} and {1, 2}. Among these edges the minimum weight is 8 which is of the edges
7
{0, 7} and {1, 2}. Let us here include the edge {0, 7} and the vertex 7 in the MST.
[We could have also included edge {1, 2} and vertex 2 in the MST].
Step 4: The edges that connect the incomplete MST with the fringe vertices are
{1, 2}, {7, 6} and {7, 8}. Add the edge {7, 6} and the vertex 6 in the MST as it has
the least weight (i.e., 1).
8
6 is added in the MST
Step 5: The connecting edges now are {7, 8}, {1, 2}, {6, 8} and {6, 5}. Include
edge {6, 5} and vertex 5 in the MST as the edge has the minimum weight (i.e., 2)
among them.
Step 6: Among the current connecting edges, the edge {5, 2} has the minimum
weight. So include that edge and the vertex 2 in the MST.
9
Include vertex 2 in the MST
Step 7: The connecting edges between the incomplete MST and the other edges
are {2, 8}, {2, 3}, {5, 3} and {5, 4}. The edge with minimum weight is edge {2, 8}
which has weight 2. So include this edge and the vertex 8 in the MST.
Step 8: See here that the edges {7, 8} and {2, 3} both have same weight which
are minimum. But 7 is already part of MST. So we will consider the edge {2, 3}
and include that edge and vertex 3 in the MST.
10
Include vertex 3 in MST
Step 9: Only the vertex 4 remains to be included. The minimum weighted edge
from the incomplete MST to 4 is {3, 4}.
The final structure of the MST is as follows and the weight of the edges of the
MST is (4 + 8 + 1 + 2 + 4 + 2 + 7 + 9) = 37.
11
4. Minimum Cost Spanning Trees - Kruskal’s algorithm
A minimum spanning tree (MST) or minimum weight spanning tree for a weighted,
connected, undirected graph is a spanning tree with a weight less than or equal to the
weight of every other spanning tree.
Introduction to Kruskal’s Algorithm:
In Kruskal’s algorithm, sort all edges of the given graph in increasing order. Then it
keeps on adding new edges and nodes in the MST if the newly added edge does not
form a cycle. It picks the minimum weighted edge at first and the maximum weighted
edge at last. Thus we can say that it makes a locally optimal choice in each step in order
to find the optimal solution. Hence this is a Greedy Algorithm.
Below are the steps for finding MST using Kruskal’s algorithm:
12
Kruskal’s algorithm to find the minimum cost spanning tree uses the greedy
approach. The Greedy Choice is to pick the smallest weight edge that does not cause a
cycle in the MST constructed so far. Let us understand it with an example:
Illustration:
Input Graph:
The graph contains 9 vertices and 14 edges. So, the minimum spanning tree formed
will be having (9 – 1) = 8 edges.
After sorting:
1 7 6
2 8 2
2 6 5
4 0 1
4 2 5
13
Weight Source Destination
6 8 6
7 2 3
7 7 8
8 0 7
8 1 2
9 3 4
10 5 4
11 1 7
14 3 5
Now pick all edges one by one from the sorted list of edges
Step 1: Pick edge 7-6. No cycle is formed, include it.
14
Add edge 7-6 in the MST
15
Add edge 6-5 in the MST
Step 4: Pick edge 0-1. No cycle is formed, include it.
16
Add edge 2-5 in the MST
Step 6: Pick edge 8-6. Since including this edge results in the cycle, discard it. Pick edge 2-
3: No cycle is formed, include it.
17
Add edge 0-7 in MST
Step 8: Pick edge 1-2. Since including this edge results in the cycle, discard it. Pick edge 3-
4. No cycle is formed, include it.
5. Huffman codes
Huffman Coding is a technique of compressing data to reduce its size without
losing any of the details. It was first developed by David Huffman.
Huffman Coding is generally useful to compress the data in which there are
frequently occurring characters.
How Huffman Coding works?
Suppose the string below is to be sent over a network.
18
Initial string
Each character occupies 8 bits. There are a total of 15 characters in the above string.
Thus, a total of 8 * 15 = 120 bits are required to send this string.
Using the Huffman Coding technique, we can compress the string to a smaller size.
Huffman coding first using the frequencies of the character and then generates
code for each character.
Once the data is encoded, it has to be decoded. Decoding is done using the same
tree.
Huffman Coding prevents any ambiguity in the decoding process using the concept
of prefix code ie. a code associated with a character should not be present in the prefix of
any other code. The tree created above helps in maintaining the property.
Huffman coding is done with the help of the following steps.
1. Calculate the frequency of each character in the string.
Frequency of string
2. Sort the characters in increasing order of the frequency. These are stored in a priority
queue Q.
19
Getting the sum of the least numbers
5. Remove these two minimum frequencies from Q and add the sum into the list of
frequencies (* denote the internal nodes in the figure above).
6. Insert node z into the tree.
7. Repeat steps 3 to 5 for all the characters.
20
Freque
Character Code Size
ncy
A 5 11 5*2 = 10
Assign 0
to the B 1 100 1*3 = 3 left edge
and 1 to the
C 6 0 6*1 = 6
right edge
For D 3 101 3*3 = 9 sending
the above
4 * 8 = 32 bits 15 bits 28 bits
string over a
network, we have to send the tree as well as the above compressed-code. The total size
is given by the table below.
Without encoding, the total size of the string was 120 bits. After encoding the size is
reduced to 32 + 15 + 28 = 75.
Decoding the code
21
For decoding the code, we can take the code and traverse through the tree to find
the character.
Let 101 is to be decoded, we can traverse from the root as in the figure below.
Decoding
Huffman Coding Algorithm
create a priority queue Q consisting of each unique character.
sort then in ascending order of their frequencies.
for all the unique characters:
create a newNode
extract minimum value from Q and assign it to leftChild of newNode
extract minimum value from Q and assign it to rightChild of newNode
calculate the sum of these two minimum values and assign it to the value of newNode
insert this newNode into the tree
return rootNode
22
Fundamentals of Dijkstra's Algorithm
Dijkstra's Algorithm was conceived by computer scientist Edsger W. Dijkstra in 1956.
1. Dijkstra's Algorithm begins at the node we select (the source node), and it examines
the graph to find the shortest path between that node and all the other nodes in
the graph.
2. The Algorithm keeps records of the presently acknowledged shortest distance from
each node to the source node, and it updates these values if it finds any shorter
path.
3. Once the Algorithm has retrieved the shortest path between the source and another
node, that node is marked as 'visited' and included in the path.
4. The procedure continues until all the nodes in the graph have been included in the
path. In this manner, we have a path connecting the source node to all other nodes,
following the shortest possible path to reach each node.
23
2. We are using this property so that we do not revisit any node.
3. A node is marked visited only when the shortest path has been found.
Path Property:
1. The 'path' property stores the value of the current minimum path to the node.
2. The current minimum path implies the shortest way we have reached this node till
now.
3. This property is revised when any neighbor of the node is visited.
4. This property is significant because it will store the final answer for each node.
Initially, we mark all the vertices, or nodes, unvisited as they have yet to be
visited. The path to all the nodes is also set to infinity apart from the source node.
Moreover, the path to the source node is set to zero (0).
We then select the source node and mark it as visited. After that, we access all
the neighboring nodes of the source node and perform relaxation on every node.
Relaxation is the process of lowering the cost of reaching a node with the
help of another node.
In the process of relaxation, the path of each node is revised to the minimum value
amongst the node's current path, the sum of the path to the previous node, and the path
from the previous node to the current node.
Let us suppose that p[n] is the value of the current path for node n, p[m] is the value
of the path up to the previously visited node m, and w is the weight of the edge between
the current node and previously visited one (edge weight between n and m).
We then mark an unvisited node with the least path as visited in every subsequent
step and update its neighbor's paths.
24
We repeat this procedure until all the nodes in the graph are marked visited.
Whenever we add a node to the visited set, the path to all its neighboring nodes
also changes accordingly.
If any node is left unreachable (disconnected component), its path remains 'infinity'.
In case the source itself is a separate component, then the path to all other nodes remains
'infinity'.
The following is the step that we will follow to implement Dijkstra's Algorithm:
Step 1: First, we will mark the source node with a current distance of 0 and set the rest of
the nodes to INFINITY.
Step 2: We will then set the unvisited node with the smallest current distance as the
current node, suppose X.
Step 3: For each neighbor N of the current node X: We will then add the current distance
of X with the weight of the edge joining X-N. If it is smaller than the current distance of N,
set it as the new current distance of N.
Step 5: We will repeat the process from 'Step 2' if there is any node unvisited left in the
graph.
25
Let us now understand the implementation of the algorithm with the help of an
example:
1. We will use the above graph as the input, with node A as the source.
2. First, we will mark all the nodes as unvisited.
3. We will set the path to 0 at node A and INFINITY for all the other nodes.
4. We will now mark source node A as visited and access its neighboring nodes.
Note: We have only accessed the neighboring nodes, not visited them.
5. We will now update the path to node B by 4 with the help of relaxation because the
path to node A is 0 and the path from node A to B is 4, and the minimum((0 + 4),
INFINITY) is 4.
6. We will also update the path to node C by 5 with the help of relaxation because the
path to node A is 0 and the path from node A to C is 5, and the minimum((0 + 5),
INFINITY) is 5. Both the neighbors of node A are now relaxed; therefore, we can
move ahead.
26
7. We will now select the next unvisited node with the least path and visit it. Hence, we
will visit node B and perform relaxation on its unvisited neighbors. After performing
relaxation, the path to node C will remain 5, whereas the path to node E will
become 11, and the path to node D will become 13.
8. We will now visit node E and perform relaxation on its neighboring nodes B, D,
and F. Since only node F is unvisited, it will be relaxed. Thus, the path to node B will
remain as it is, i.e., 4, the path to node D will also remain 13, and the path to
node F will become 14 (8 + 6).
9. Now we will visit node D, and only node F will be relaxed. However, the path to
node F will remain unchanged, i.e., 14.
10. Since only node F is remaining, we will visit it but not perform any relaxation as all
its neighboring nodes are already visited.
11. Once all the nodes of the graphs are visited, the program will end.
A=0
B = 4 (A -> B)
C = 5 (A -> C)
D = 4 + 9 = 13 (A -> B -> D)
E = 5 + 3 = 8 (A -> C -> E)
F = 5 + 3 + 6 = 14 (A -> C -> E -> F)
Pseudocode for Dijkstra's Algorithm
o We have to maintain a record of the path distance of every node. Therefore, we can
store the path distance of each node in an array of size n, where n is the total
number of nodes.
27
o Moreover, we want to retrieve the shortest path along with the length of that path.
To overcome this problem, we will map each node to the node that last updated its
path length.
o Once the algorithm is complete, we can backtrack the destination node to the
source node to retrieve the path.
o We can use a minimum Priority Queue to retrieve the node with the least path
distance in an efficient way.
Pseudocode:
Explanation:
File: DijkstraAlgorithm.java
29
public void dijkstraAlgorithm(int[][] graph, int source)
{
// number of nodes
int nodes = graph.length;
boolean[] visited_vertex = new boolean[nodes];
int[] dist = new int[nodes];
for (int i = 0; i < nodes; i++)
{
visited_vertex[i] = false;
dist[i] = Integer.MAX_VALUE;
}
// Distance of self loop is zero
dist[source] = 0;
for (int i = 0; i < nodes; i++)
{
// Updating the distance between neighboring vertex and source vertex
int u = find_min_distance(dist, visited_vertex);
visited_vertex[u] = true;
// Updating the distances of all the neighboring vertices
for (int v = 0; v < nodes; v++)
{
if (!visited_vertex[v] && graph[u][v] != 0 && (dist[u] + graph[u][v] < dist[v])) {
dist[v] = dist[u] + graph[u][v];
}
}
}
for (int i = 0; i < dist.length; i++)
{
30
System.out.println(String.format("Distance from Vertex %s to Vertex %s is %s", source, i
, dist[i]));
}
}
// defining the method to find the minimum distance
private static int find_min_distance(int[] dist, boolean[] visited_vertex)
{
int minimum_distance = Integer.MAX_VALUE;
int minimum_distance_vertex = -1;
for (int i = 0; i < dist.length; i++) {
if (!visited_vertex[i] && dist[i] < minimum_distance)
{
minimum_distance = dist[i];
minimum_distance_vertex = i;
}
}
return minimum_distance_vertex;
}
// main function
public static void main(String[] args)
{
// declaring the nodes of the graphs
int graph[][] = new int[][] {
{ 0, 1, 1, 2, 0, 0, 0 },
{ 0, 0, 2, 0, 0, 3, 0 },
{ 1, 2, 0, 1, 3, 0, 0 },
{ 2, 0, 1, 0, 2, 0, 1 },
{ 0, 0, 3, 0, 0, 2, 0 },
{ 0, 3, 0, 0, 2, 0, 1 },
31
{ 0, 2, 0, 1, 0, 1, 0 }
};
// instantiating the DijkstraAlgorithm() class
DijkstraAlgorithm Test = new DijkstraAlgorithm();
// calling the dijkstraAlgorithm() method to find the shortest distance from the source node
to the destination node
Test.dijkstraAlgorithm(graph, 0);
}
}
Output
Explanation:
32
then updated the distances of the neighboring vertices of the selected vertex by
performing relaxation and printed the shortest distances for every vertex. We have then
defined a method to find the minimum distance from the source vertex to the destination
vertex. We then defined the main function where we declared the vertices of the graph and
instantiated the DijkstraAlgorithm() class. Finally, we have called
the dijkstraAlgorithm() method to find the shortest distance between the source vertex
and the destination vertices.
As a result, the required shortest possible paths for every node from the source
node are printed for the users.
Reference video link :
https://www.youtube.com/watch?v=XB4MIexjvY0
33
Let X = <x1, x2, x3, . . . . . , xn> is the set of n items. W = <w 1, w2, w3, . . . , wn> and V =
<v1, v2, v3, . . . , vn> are the set of weight and value associated with each items in x,
respectively. Knapsack capacity is M.
Select items one by one from the set of items x and fill the knapsack such that it
would maximize the value. Knapsack problem has two variants. 0/1 knapsack does
not allow breaking of items. Either add entire item in a knapsack or reject it. It is
also known as a binary knapsack. Fractional knapsack allows the breaking of items.
So profit will also be considered accordingly.
Knapsack problem can be formulated as follow :
Maximize
sumni=1vixi
subjected to
sumni=1vixileM
xiin(0,1)
for binary knapsack
xiin[0,1]
for fractional knapsack
Algorithm
Algorithm GREEDY_FRACTIONAL_KNAPSACK(X, V, W, M)
// Description : Solve the knapsack problem using greedy approach
// Input:
X: An array of n items
V: An array of profit associated with each item
W: An array of weight associated with each item
M: Capacity of knapsack
// Output :
SW: Weight of selected items
SP: Profit of selected items
34
// Items are presorted in decreasing order of pi = vi / wi ratio
while i ≤ n do
if (SW + w[i]) ≤ M then
S ← S ∪ X[i]
SW ← SW + W[i]
SP ← SP + V[i]
else
frac ← (M - SW) / W[i]
S ← S ∪ X[i] * frac // Add fraction of item X[i]
SP ← SP + V[i] * frac // Add fraction of profit
SW ← SW + W[i] * frac // Add fraction of weight
end
i←i+1
end
Complexity Analysis
For one item there are two choices, either to select or reject. For 2 items we have
four choices:
Select both items
Reject both items
Select first and reject second
Reject first and select second
In general, for n items, knapsack has 2 n choices. So brute force approach runs in
O(2n) time.
35
We can improve performance by sorting items in advance. Using merge sort or
heap sort, n items can be sorted in O(nlog 2n) time. Merge sort and heap sort are
non-adaptive and their running time is the same in best, average and worst case.
To select the items, we need one scan to this sorted list, which will take O(n) time.
So the total time required is
T(n) = O(nlog2n) + O(n) = O(nlog2n).
Examples of Fractional Knapsack
Solution:
Let us arrange items by decreasing order of profit density. Assume that items are
labeled as X = (I1, I2, I3), have profit V = {24, 25, 15} and weight W = {18, 15, 20}.
I2 25 15 1.67
I1 24 18 1.33
I3 15 20 0.75
We shall select one by one item from Table. If the inclusion of an item does not
cross the knapsack capacity, then add it. Otherwise, break the current item and select only
the portion of item equivalent to remaining knapsack capacity. Select the profit
accordingly. We should stop when knapsack is full or all items are scanned.
36
Here, Knapsack capacity M = 20.
SW ≤ M, so select I2
S = { I2 }, SW = 15, SP = 0 + 25 = 25
The remaining capacity of the knapsack is 5 unit, so select only 5 units of item I 1.
S = { I2, I1 * 5/18 }
The knapsack is full. Fractional Greedy algorithm selects items { I2, I1 * 5/18 }, and it gives a
profit of 31.67 units.
Problem: Find the optimal solution for knapsack problem (fraction) where knapsack
capacity = 28, P = {9, 5, 2, 7, 6, 16, 3} and w = {2, 5, 6, 11, 1, 9, 1}.
Solution:
I5 6 1 6.00
I1 9 2 4.50
I7 3 1 3.00
37
Item Profit pi Weight wi Ratio vi/wi
I6 16 9 1.78
I2 5 5 1.00
I4 7 11 0.64
I3 2 6 0.33
Where S is the solution set, P and W is profit and weight of included items, respectively. M
is the capacity of the knapsack.
Iteration 1
So, S = { I5 }, Weight = 0 + 1 = 1, P = 0 + 6= 6
Iteration 2
Iteration 3
Iteration 4
Iteration 6
(Weight + w4) > M, So I4 must be broken down into two parts x and y such that x =
capacity left in knapsack and y = I4 – x.
Available knapsack capacity is 10 units. So we can select only (28 – 18) / 11 = 0.91 unit of I 4.
So S = {I5, I1, I7, I6, I2, 0.91 * I4 }, Weight = 18 + 0.91*11 = 28, P = 39 + 0.91 * 7= 45.37
https://www.youtube.com/watch?v=oTTzNMHM05I
39