Sunteți pe pagina 1din 24

NATIONAL INSTITUTE OF TECHNOLOGY

TIRUCHIRAPPALLI
Department of Electrical and Electronics Engineering

PROJECT ON
DATA STRUCTURES AND C++

WARSHALLS ALGORITHM

PROJECT TEAM:
Bollu Karthik 107112015
Guru Praanesh R 107112030
Guru Raghav R 107112031
V Vignesh Venkatasubramanian 107112098

Page 2 of 24

PROBLEM STATEMENT

Given a list of cities and the interconnection between cities, write a program in C++ to
1) Find the path matrix using Warshalls algorithm.
2) List all possible paths between the given cities.
3) Check for existence of a cycle in the graph.
The input and output are from/to files. The files names are passed as command line
arguments.

Page 3 of 24

THEORY
Warshalls Algorithm to find Transitive Closure
Let S be the finite set {v1 , ..., v n }, R a relation on S. The adjacency matrix A of R is an n x n
Boolean (zero-one) matrix defined by
Ai,j = 1 if the digraph D has an edge from vi to vj
0 if the digraph D has an edge from vi to vj
Warshalls algorithm is an efficient method of finding the adjacency matrix of the transitive
closure of relation R on a finite set S from the adjacency matrix of R. It uses properties of the
digraph D, in particular, walks of various lengths in D.
Let A be the adjacency matrix of R and T be the adjacency matrix of the transitive closure of
R. T is called the reachability matrix of digraph D due to the property that T i, j = 1 if and only
if vj can be reached from vi in D by a sequence of arcs (edges).
In Warshalls algorithm we construct a sequence of Boolean matrices A = W[0] , W[1] , W[2] , ...,
W[n] =T, where A and T are as above. This can be done from digraph D as follows.
[W[1]] i, j = 1 if and only if there is a walk from vi to vj with elements of a subset of {v1 }as
interior vertices.
[W[2]] i, j = 1 if and only if there is a walk from vi to vj with elements of a subset of {v1,v2}as
interior vertices.
Continuing this process, we generalize to:
[W[k]] i, j = 1 if and only if there is a walk from vi to vj with elements of a subset of { v1, v 2 , ...
, v k } as interior vertices.
In constructing W[k ] from W[k 1] we either keep zeros or change some zeros to ones. No ones
ever get changed to zeros.
Generation of Transitive Closure (Path matrix)
The recurrence relating elements R(k) to elements of R(k-1) is:
R(k)[i,j] = R(k-1)[i,j] OR (R(k-1)[i,k] AND R(k-1)[k,j])
It implies the following rules for generating R(k) from R(k-1):
Rule 1: If an element in row i and column j is 1 in R(k-1), it remains 1 in R(k).
Rule 2: If an element in row i and column j is 0 in R(k-1), it has to be changed to 1 in R(k) if
and only if the element in its row i and column k and the element in its column j and row k
are both 1s in R(k-1).

Page 4 of 24

WARSHALLS ALGORITHM
Input: Adjacency matrix A of relation R on a set of n elements
Output: Adjacency matrix T of the transitive closure of R.
T := A [initialize T to A]
for j := 1 to n
for i := 1 to n
if i j T , = 1 then
i a := i j a a [form the Boolean OR of row i and row j, store it in i a ]
next i
next j
END
Time efficiency: (n3)
Space efficiency: Matrices can be written over their predecessors

File Handling in C++


C++ provides the following classes to perform output and input of characters to/from files:

ofstream: Stream class to write on files

ifstream: Stream class to read from files

fstream: Stream class to both read and write from/to files.

These classes are derived directly or indirectly from the classes istream and ostream. The first
operation generally performed on an object of one of these classes is to associate it to a real
file. This procedure is known as to open a file. An open file is represented within a program
by a stream and any input or output operation performed on this stream object will be applied
to the physical file associated to it. In order to open a file with a stream object we use its
member function open:
filename.open (filename, mode);
Here filename is a string representing the name of the file to be opened, and mode is an
optional parameter with a combination of the following flags:
ios::in
ios::out
ios::binary
ios::ate
ios::app
ios::trunc

Open for input operations.


Open for output operations.
Open in binary mode.
Set the initial position at the end of the file.
If this flag is not set, the initial position is the beginning of the file.
All output operations are performed at the end of the file, appending the content
to the current content of the file.
If the file is opened for output operations and it already existed, its previous
content is deleted and replaced by the new one.

All these flags can be combined using the bitwise operator OR (|). For example, if we want to
Page 5 of 24

open the file example.bin in binary mode to add data we could do it by the following call to
member function open:
ofstream myfile;
myfile.open ("example.bin", ios::out | ios::app | ios::binary);
Each of the open member functions of classes ofstream, ifstream and fstream has a default
mode that is used if the file is opened without a second argument:

CLASS

DEFAULT MODE PARAMETER

ofstream

ios::out

ifstream

ios::in

fstream

ios::in | ios::out

For ifstream and ofstream classes, ios::in and ios::out are automatically and respectively
assumed, even if a mode that does not include them is passed as second argument to
the open member
function
(the
flags
are
combined).
For fstream, the default value is only applied if the function is called without specifying any
value for the mode parameter. If the function is called with any value in that parameter the
default
mode
is
overridden,
not
combined.
File streams opened in binary mode perform input and output operations independently of
any format considerations. Non-binary files are known as text files, and some translations
may occur due to formatting of some special characters (like newline and carriage return
characters).
To check if a file stream was successful opening a file, we can do it by calling to
member is_open(). This member function returns a bool value of true in the case that indeed
the stream object is associated with an open file, or false otherwise:
if (myfile.is_open()){ }
When we are finished with our input and output operations on a file we shall close it so that
the operating system is notified and its resources become available again. For that, we call the
stream's member function close. This member function takes flushes the associated buffers
and closes the file:
myfile.close();
Once this member function is called, the stream object can be re-used to open another file,
and the file is available again to be opened by other processes. In case an object is destroyed
while still associated with an open file, the destructor automatically calls the member
function close().
Page 6 of 24

Text files
Text file streams are those where the ios::binary flag is not included in their opening mode.
These files are designed to store text and thus all values that are input or output from/to them
can suffer some formatting transformations, which do not necessarily correspond to their
literal binary value.
State flags
The following member functions exist to check for specific states of a stream (all of them
return a bool value):
bad()
Returns true if a reading or writing operation fails. For example, in the case that we try to
write to a file that is not open for writing or if the device where we try to write has no space
left.
fail()
Returns true in the same cases as bad(), but also in the case that a format error happens, like
when an alphabetical character is extracted when we are trying to read an integer number.
eof()
Returns true if a file open for reading has reached the end.
good()
It is the most generic state flag: it returns false in the same cases in which calling any of the
previous functions would return true. Note that good and bad are not exact opposites
(good checks more state flags at once).
The member function clear() can be used to reset the state flags.
get and put stream positioning
All i/o streams objects keep internally at least one internal position:
ifstream, like istream, keeps an internal get position with the location of the element to be
read in the next input operation.
ofstream, like ostream, keeps an internal put position with the location where the next
element has to be written.
Finally, fstream, keeps both, the get and the put position, like iostream.
These internal stream positions point to the locations within the stream where the next
reading or writing operation is performed. These positions can be observed and modified
using the following member functions:

Page 7 of 24

tellg() and tellp()


These two member functions with no parameters return a value of the member
type streampos, which is a type representing the current get position (in the case of tellg) or
the put position (in the case of tellp).
seekg() and seekp()
These functions allow to change the location of the get and put positions. Both functions are
overloaded with two different prototypes. The first form is:
seekg ( position );
seekp ( position );
Using this prototype, the stream pointer is changed to the absolute position position (counting
from the beginning of the file). The type for this parameter is streampos, which is the same
type as returned by functions tellg and tellp.
The other form for these functions is:
seekg ( offset, direction );
seekp ( offset, direction );
Using this prototype, the get or put position is set to an offset value relative to some specific
point determined by the parameter direction. offset is of type streamoff. And direction is of
type seekdir, which is an enumerated type that determines the point from where offset is
counted from, and that can take any of the following values:

ios::beg offset counted from the beginning of the stream


ios::cur offset counted from the current position
ios::end offset counted from the end of the stream

Page 8 of 24

FUNCTIONS USED IN THE PROGRAM


1. void read_mat( )
This function opens the input file and reads the adjacency matrix. It checks whether there are
any errors in opening the file. The adjacency matrix is stored in the array city.
2. int BOR(int,int)
This function returns the binary OR result of the two input arguments. This is used in the
generation of transitive closure through Warshalls algorithm.
3. void path_mat(int)
This function implements Warshalls algorithm and forms the path matrix TC from the city
matrix. If the argument is 1, it additionally prints the generated transitive closure TC onto the
command line as well as the output file.
4. void push(int)
It pushes the argument supplied into the stack.
5. int pop( )
It returns the topmost element stored in the stack.
6. int compare(int)
It checks whether the argument already exists in the stack. If yes, it returns 1, else 0. This
function is used to determine whether a given node has already been traversed while
determining the paths between any two nodes of the graph.
7. void print_paths(int)
This method is responsible for the formatting and display of the output onto the command
line and output file. It is called from the find_all_paths( ) function as and when a path is
determined between the start_city and the end_city.
8. void find_all_paths( )
This function implements the algorithm to find all paths between any two given nodes. It
accepts two different nodes start_city and end_city. From both these numbers, 1 is
subtracted to make them in accordance to the array indices in the adjacency matrix. The
algorithm used is as follows:

The search starts from the row corresponding to the start_city. The algorithm
traverses all elements in this row.
If a 1 is encountered, it moves to the row corresponding to the column where the 1
was encountered. This city number is pushed into the stack.
If the whole row does not have any 1s, the function returns, printing There is no
connection between these two cities.
In this way, when the algorithm reaches the row corresponding to the end city, the
current path from the stack is printed. The topmost element is popped out. An
alternate path, if it exists, is searched from that index to the end_city.
When the end of the row of the start_city is encountered, the function returns.
Page 9 of 24

9. int closed_loop( )
This method determines if there exists a closed loop in the given graph. This is determined
from the path matrix. If there exists a 1 in the leading diagonal, that is, TC[i][i] =1, then there
is a loop originating and terminating at the city with index i. The function returns 1 if there
exists a loop, else 0.
10. void print( )
This function uses two nested for loops to print the adjacency matrix city which is read from
the input file.
11. int main( int argc , char* argv[ ] )
The main method is executed first in the program. This method takes in three command line
arguments, namely the program name, the input and output files. There is a provision to
check whether all the arguments have been supplied on program execution.
Two file objects are created, one each for the input and output files. The program is menu
driven, and exists completely within an infinite loop, which terminates only when the user
enters a 0. The user is provided the following options:
i.
ii.
iii.
iv.
v.

Print the path matrix.


Find all paths between two cities.
Check for existence of closed loops.
Print the adjacency matrix.
Exit the program

Page 10 of 24

PROGRAM
#include<conio.h>
#include<fstream>
#include<iostream>
using namespace std;
const int NOC = 7; //Number of cities
int city[NOC][NOC], TC[NOC][NOC];
int top=-1, stack[NOC] , output_count = 1;
ofstream out;
ifstream in;
// TAKE INPUT FROM FILE
void read_mat()
{
//CHECK IF FILE IS OPENED
if ( in.good() == 0 )
{
cout<<"Sorry, an error occured!! Please restart the program.";
getch();
exit(0);
}
//END
// READ FROM FILE
for(int i=0; i<NOC ; i++ )
{
for ( int j=0 ; j <NOC ; j++ )
{
if ( !in.eof() )
{ in>>city[i][j];
}
}
}
//END
in.close();
}
/*------------------------------------------------------------------------*/
//FUNCTIONS ASSOCIATED WITH ALGORITHM 1
//BINARY OR OPERATION
int BOR( int A , int B)
{
if ( A == 0 && B == 0 )
return 0;
Page 11 of 24

else
return 1;
}
// WARSHALL'S ALGORITHM-TO GENERATE THE PATH MATRIX
void path_mat(int print)
{
for ( int i = 0 ; i<NOC ; i++ )
{
for ( int j =0 ; j<NOC ; j++ )
{
TC[i][j] = city[i][j];
}
}
//ALGORITHM STARTS HERE
for ( int j=0 ; j< NOC ; j++ )
{
for ( int i= 0 ; i< NOC ; i++ )
{
if ( TC[i][j] == 1 )
{
for ( int n = 0 ; n<NOC ; n++ )
{ TC[i][n] = BOR( TC[j][n] , TC[i][n] ) ;
}
}
}
}
//END
//PRINT PATH MATRIX
if ( print == 1)
{
out<<"\n\nOUTPUT "<<output_count<<" : \n\n";
output_count++;
cout<<"The path matrix is : \n\n";
out<<"The path matrix is : \n\n";
for ( int i=0 ; i<NOC ; i++ )
{
for ( int j=0 ; j<NOC ; j++ )
{
cout<<TC[i][j]<<" ";
out<<TC[i][j]<<" ";
}
cout<<"\n";
out<<"\n";
}
getch();
}
Page 12 of 24

//END
}
/*------------------------------------------------------------------------*/
// FUNCTIONS ASSOCIATED WITH ALGORITHM 2
void push( int element )
{
top++;
stack[top] = element ;
}
int pop()
{
int temp = stack[top];
top--;
return temp;
}
int compare(int element)
{
for ( int i=0 ; i<=top ; i++ )
{
if ( stack[i] == element )
return 1;
}
return 0;
}
void print_paths(int print)
{
if ( print == 1)
{
cout<<"\nPath number : ";
out<<"\nPath number : ";
}
cout<<"\n\n"<<print<<" : ";
out<<"\n\n"<<print<<" : ";
output_count++;
for ( int i=0 ; i<= top-1 ; i++ )
{ cout<<stack[i]+1<<" -> " ;
out<<stack[i]+1<<" -> " ;
}
cout<<stack[top]+1;
out<<stack[top]+1;
}
void find_all_paths()
{
int start_city, end_city , print_count = 1;
Page 13 of 24

out<<"\n\nOUTPUT "<<output_count<<" : \n\n"; output_count++;


cout<<"Enter cities between 1 - "<<NOC<<".\nEnter start city : ";
out<<"Enter cities between 1 - "<<NOC<<".\nEnter start city : ";
cin>>start_city;
out<<start_city<<"\n";
cout<<"Enter end city other than the starting city : ";
cin>>end_city;
out<<"Enter end city other than the starting city : ";
out<<end_city<<"\n";
cout<<"\n\n";
out<<"\n\n";
//TO MATCH USER INPUT TO MATRIX INDICES
end_city--;
start_city--;
// SEARCH ALGORITHM STARTS HERE
push(start_city);
int i = start_city , j= 0;
while ( true )
{
if(start_city==end_city){
cout<<"\nThe starting and the end cities cannot be the same!\n";
out<<"\nThe starting and the end cities cannot be the same!\n";
print_count=0;
break;
}
if((start_city+1)>NOC || (start_city+1)<=0 || (end_city+1)>NOC ||
(end_city+1)<=0){
cout<<"Please enter correct city numbers! Restart from main menu.";
out<<"Please enter correct city numbers! Restart from main menu.";
print_count=0;
break;
}
if ( i == end_city)
{
print_paths(print_count);
print_count++;
j = pop()+1;
if ( j > (NOC-1) ){
if(top>0) j = pop()+1;
else break;
}
i = stack[top];
}

Page 14 of 24

if ( j > (NOC-1) && i == start_city )


{
break;
}
if ( city[i][j] == 0 )
{
if( j == (NOC-1) )
{ if(i==start_city) break;
j = pop()+1;
if ( j > (NOC-1) ){
if(top>0) j = pop()+1;
else break;
}
i = stack[top];
}
else
j++;
}
else
{
if( compare(j) == 0 )
{
i=j;
push(j);
j=0;
}
else if( j == (NOC-1) )
{
j = pop()+1;
if ( j > (NOC-1) ){
if(top>0) j = pop()+1;
else break;
}
i = stack[top];
}
else
j++;
}
}
while( top != -1) //Simply to empty the stack
pop();
if ( print_count == 1 )
Page 15 of 24

cout<<"\nThere is no connection between these two cities.";


out<<"\nThere is no connection between these two cities.";

}
else if(print_count>1)
{
cout<<"\n\nThese are all the available paths!";
out<<"\nThese are all the available paths!";
}
getch();
}
/*------------------------------------------------------------------------*/
//ALGORITHM 3
int closed_loop()
{
path_mat(0);
for(int i = 0; i<NOC ; i++ )
{
if ( TC[i][i] == 1)
return 1;
}
return 0;
}
/*------------------------------------------------------------------------*/
//PRINT ADJACENCY MATRIX
void print()
{
out<<"\n\nOUTPUT "<<output_count<<" : \n\n";
output_count++;
cout<<"The adjacency matrix is : \n\n";
out<<"The adjacency matrix is : \n\n";
for ( int i=0 ; i<NOC ; i++ )
{
for ( int j=0 ; j<NOC ; j++ )
{
cout<<city[i][j]<<" ";
out<<city[i][j]<<" ";
}
cout<<"\n";
out<<"\n";
}
getch();
Page 16 of 24

}
/*------------------------------------------------------------------------*/
int main(int argc, char* argv[])
{
if(argc!=3) {
cout<<"Enter the program with the input and output file names.";
return 1;
}
out.open(argv[2]);
in.open(argv[1]);
read_mat();
while(1)
{
cout<<endl<<"\n1. Print path matrix - transitive closure for the given city matrix.";
cout<<"\n2. Find all paths between two cities.";
cout<<"\n3. Check for existence of loops.";
cout<<"\n4. Print the adjacency matrix.";
cout<<"\n0. Exit";
cout<<"\n\nEnter choice : ";
out<<"\n\nOUTPUT "<<output_count<<" : ";
output_count++;
out<<"\n\n1. Print path matrix - transitive closure for the given city matrix.";
out<<"\n2. Find all paths between two cities.";
out<<"\n3. Check for existence of loops.";
out<<"\n4. Print the adjacency matrix.";
out<<"\n0. Exit";
out<<"\n\nEnter choice : ";
int choice;
cin>>choice;
out<<choice<<"\n";
switch(choice)
{
case 1 : {

path_mat(1);
break;

}
case 2 : {

find_all_paths();
break;

}
Page 17 of 24

case 3 : {
int tru = closed_loop();
if ( tru == 1 ){
cout<<"\n\nThe given city graph has closed loops!";
out<<"\n\nThe given city graph has closed loops!";
}
else{
cout<<"\n\nThe given city graph does not have closed loops!";
out<<"\n\nThe given city graph does not have closed loops!";
}
getch();
break;
}
case 4 : {

print();
break;

}
case 0 :return 0;
}
if( choice != 1 || choice!= 2 || choice != 3 || choice != 4 || choice != 0 )
{
cout<<"\nWrong choice. Please enter again!";
getch();
out<<"\nWrong choice. Please enter again!";
}
}
out.close();
}

Page 18 of 24

OUTPUT
Input file contents (adjacency matrix):
0000000
0010101
0001000
0010110
0000010
0010001
1010010
Command line output:
C:\Users\Raghav\Desktop\DS Project> proj adja.txt output.txt
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 4
The adjacency matrix is :
0000000
0010101
0001000
0010110
0000010
0010001
1010010

1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 1
The path matrix is :

Page 19 of 24

0000000
1011111
1011111
1011111
1011111
1011111
1011111

1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 2
Enter cities between 1 - 7.
Enter start city : 3
Enter end city other than the starting city : 5
Path number :
1 : 3 -> 4 -> 5
These are all the available paths!
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 2
Enter cities between 1 - 7.
Enter start city : 1
Enter end city other than the starting city : 5
There is no connection between these two cities.
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Page 20 of 24

Enter choice : 2
Enter cities between 1 - 7.
Enter start city : 3
Enter end city other than the starting city : 6
Path number :
1 : 3 -> 4 -> 5 -> 6
2 : 3 -> 4 -> 6
These are all the available paths!
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 3
The given city graph has closed loops!
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 0
C:\Users\Raghav\Desktop\DS Project>

Output file contents:


OUTPUT 1 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Page 21 of 24

Enter choice : 4
OUTPUT 2 :
The adjacency matrix is :
0000000
0010101
0001000
0010110
0000010
0010001
1010010
OUTPUT 3 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 1
OUTPUT 4 :
The path matrix is :
0000000
1011111
1011111
1011111
1011111
1011111
1011111
OUTPUT 5 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 2
Page 22 of 24

OUTPUT 6 :
Enter cities between 1 - 7.
Enter start city : 3
Enter end city other than the starting city : 5
Path number :
1 : 3 -> 4 -> 5
These are all the available paths!
OUTPUT 8 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 2
OUTPUT 9 :
Enter cities between 1 - 7.
Enter start city : 1
Enter end city other than the starting city : 5
There is no connection between these two cities.
OUTPUT 10 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 2
OUTPUT 11 :
Enter cities between 1 - 7.
Enter start city : 3
Enter end city other than the starting city : 6
Page 23 of 24

Path number :
1 : 3 -> 4 -> 5 -> 6
2 : 3 -> 4 -> 6
These are all the available paths!
OUTPUT 14 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 3
The given city graph has closed loops!
OUTPUT 15 :
1. Print path matrix - transitive closure for the given city matrix.
2. Find all paths between two cities.
3. Check for existence of loops.
4. Print the adjacency matrix.
0. Exit
Enter choice : 0

Page 24 of 24

S-ar putea să vă placă și