Applying Recursion Routing in Computer Networks Review We’ve seen that recursion provides an elegant, simple, and (sometimes) efficient way to solve certain problems. A recursive solution consists of: – a base case: an instance of the problem that is trivial to solve; and – an induction step: a means of solving non-trivial instances using “smaller” solutions to the problem. Problem A computer network is a collection of computers, that communicate via links. 2 0 1 5 6 Links may represent 3 4 – phone lines – coaxial cable – infared transmissions – microwave relays – satellite communication links – ... Network Operations If we could represent a network in software, some of the useful operations might be: • Initialization (presumably from a file). • Is there a computer with id i in the network? • Is there a link from computer i to computer j ? • Is there a path from computer i to computer j ? Design We can represent the computers in the network by assigning each one a unique integer value (i.e., its network id). One way to represent the links in a network is to use an adjacency matrix -- a boolean matrix in which entry [i][j] is true if and only if there is a link from i to j. 2 0 3 1 5 4 6 [0] [1] [2] [3] [4] [5] [6] [0] F T F F F F T [1] T F T F F T F [2] F T F T F F F [3] F F T F T F F [4] F F F T F T T [5] F T F F T F F [6] T F F F T F F Class Members We might thus begin a Network class as follows: // Network.h // ... // Directives omitted class Network { public: Network(const string & fileName); int Size() const; bool HasComputer(int id) const; bool HasLink(int i, int j) const; vector<int> RouteFrom(int i, int j) const; private: vector<int> PathFrom(int i, int j, vector<int> visited) const; int mySize; // number of computers typedef vector<bool> Row; vector<Row> myLinks; // adjacency matrix }; Initialization For convenience, we will initialize our network using information from a file, whose format will be: – the first line of the file provides the number of computers in the network; and – each subsequent line is a pair i j indicating a bidirectional link between computers i and j. 2 0 3 1 5 6 4 7 0 1 2 3 4 4 0 1 1 2 3 4 5 6 6 5 Constructor We can define the class constructor as follows: // Network.cpp // ... #include “Network.h” Network::Network(const string & fileName) { ifstream in(fileName.data()); // open stream to file assert(in.is_open()); // verify int n; in >> n; assert(n > 0); mySize = n; // ... // // // // for convenience read number of comps check validity save Constructor // ... Network constructor (ct’d) myLinks = vector<Row>(n, Row(n, false)); int i, j; for (;;) { in >> i >> j; } } // n Rows // of n values // all ‘F’ // computers // loop: // read i, j if (in.eof()) break; // if done, quit assert(i >= 0 && i < mySize); assert(j >= 0 && j < mySize); // check i // check j myLinks[i][j] = myLinks[j][i] = true; // set links i,j // & j,i to ‘T’ in.close(); // close stream Declaration We can now declare a Network object: Network net(inputFile); If inputFile contains the values shown earlier, net will be constructed as follows: net mySize myLinks [0] 7 [0] [1] [2] [3] [4] [5] [6] F T F F F F T [1] T F T F F T F [2] F T F T F F F [3] F F T F T F F [4] F F F T F T T [5] F T F F T F F [6] T F F F T F F The Size() Operation The Size() operation is a simple extractor: // ... inline int Network::Size() const { return mySize; } The HasComputer() Operation The HasComputer() operation is also simple: // ... inline bool Network::HasComputer(int id) const { return id >= 0 && id < mySize; } The HasLink() Operation Thanks to our adjacency matrix, the HasLink() operation is also quite simple: // ... inline bool Network::HasLink(int i, int j) const { return myLinks[i][j]; } The RouteFrom() Operation The RouteFrom() operation is more complicated, since in some networks, it may be necessary to check many links in order to find a path. Example: How do we find the path from 5 to 4 in the following network? 2 0 3 1 5 6 4 RouteFrom() RouteFrom(i,j) can be implemented recursively. However, we must keep track of those nodes we have already visited on a route... If we pass this information via a parameter, we should define a recursive utility function PathFrom(i, j, visited) to do the actual work. Defining RouteFrom() PathFrom() makes RouteFrom() easy to define: // ... inline vector<int> Network::RouteFrom(int i, int j) const { vector<int> visited; return PathFrom(i, j, visited); } We could just make PathFrom() a public operation, but RouteFrom() provides a more convenient interface by not requiring the user to pass visited. Designing PathFrom() PathFrom(i, j, visited): Base case: HasLink(i,j) is true. Return a vector containing i and j. Induction Step: HasLink(i,j) is false. For each computer c: If HasLink(i,c) && c has not been visited: If there is a PathFrom(c,j) Add i to and return that path. Defining PathFrom() Here is one way PathFrom() can be defined: // ... vector<int> Network::PathFrom(int i, int j, vector<int> beenTo) const { vector<int> result; result.push_back(i); beenTo.push_back(i); if (myLinks[i][j]) // base case { result.push_back(j); beenTo.push_back(j); } // ... Defining PathFrom() (Ct’d) } // ... else // induction step { for (int v = 0; v < mySize; v++) { if (myLinks[i][v] && find(beenTo.begin(), beenTo.end(), v) == beenTo.end()) { vector<int> temp = PathFrom(v, j, beenTo); if (temp.size() > 1) { for (int k = 0; k < temp.size(); k++) result.push_back(temp[k]); break; } } } } return result; Class Members Our final declaration of Network is thus as follows: // Network.h // ... // Directives omitted class Network { public: Network(const string & fileName); int Size() const; bool HasComputer(int id) const; bool HasLink(int i, int j) const; vector<int> RouteFrom(int i, int j) const; private: vector<int> PathFrom(int i, int j, vector<int> visited) const; int mySize; // number of computers typedef vector<bool> Row; vector<Row> myLinks; // adjacency matrix }; Discussion PathFrom() is not very efficient, and the path it returns may not be the shortest path from i to j. The graph (a set of nodes and a set of edges connecting them) is often used to model networks. Efficient solutions have been devised for many graphtheory problems (e.g., the shortest path problem), providing ready-made solutions to the corresponding networking problems. Summary We have barely scratched the surface of C++! C++ is an incredibly rich programming language, that lets the programmer specifiy precisely what he or she wishes the computer to do. C++ provides many other libraries, containing more ready-made solutions to common problems. Continue on to the next course and learn about more features of the C++ programming language!