diff --git a/Ass3.pdf b/Ass3.pdf new file mode 100644 index 0000000..a5c7725 Binary files /dev/null and b/Ass3.pdf differ diff --git a/Assignment 3.pdf b/Assignment 3.pdf new file mode 100644 index 0000000..ba7b3b3 Binary files /dev/null and b/Assignment 3.pdf differ diff --git a/ass3.c b/ass3.c new file mode 100644 index 0000000..dd8fee5 --- /dev/null +++ b/ass3.c @@ -0,0 +1,369 @@ +/* License: AGPLv3 or later. https://www.gnu.org/licenses/licenses.html + * + * IMPORTANT: THIS PROGRAM REQUIRES THE -lm FLAG TO COMPILE. + * Compile it as: + * gcc -Wall -std=c11 ass3.c -o ass3 -lm + * + * Assignment 3 - Optimum Routing + * Manish + * Student Login: ***** + * + * NOTE: I have not implemented memorisation for heuristics (euclidean + * distance) even though we find shortest path several times (as we also find + * second shortest path). Because, it would have marginal speed improvement + * (and has complexity of O(1)) but can potentially require (no. of vertices)^2 + * space which is not very scalable. + */ + +#include +#include +#include +#include + + +typedef struct +{ + double x; + double y; +} vertex; + +vertex * V = NULL; // Vertices +int nV; // Number of Vertices +double * E = NULL; // Edge Weights +int nE; // Number of Edges +int src; // source/from +int dst; // destination/to + + // reused in multiple aStar calls +double* D = NULL; // Best path distance/weight +int* P = NULL; // Parent of current vertices following best path +int* C = NULL; // Candidate vertices. It's min heap +int nC; // Number of Candidate vertices/ C size. + +/* NOTE: Same memory as for V is allocated as dynamic + * memory allocation later is not allowed and in worst case + * would have to go through all vertices (or path does not exist). + */ +// Used for removing edges while finding second_shortest path +int* shortestPath = NULL; +int shortestPathVertices = 0; + +// Used for storing second best candidate yet while search remaining options +int* secondShortestPath = NULL; +int secondShortestPathVertices = 0; + + +void aStar(); + +double euclidianDistance(int i, int j); // Heuristics +double absolute(double n); + +// Work on C (Candidate List) +void heapify(); +void shiftdown(int i); +void shiftup(int i); +void swap(int i, int j); + + +int main(void) +{ + printf("Enter file name containing graph details: "); + char filename[257]; + // Assuming filename/file path won't be longer than 256 characters + scanf("%256s", filename); + + FILE* file = fopen(filename, "r"); + if (!file) + { + perror(filename); + exit(EXIT_FAILURE); + } + + // Read nV & nE and dynamically allocate memory + if (fscanf(file, " %d %d ", &nV, &nE) != EOF) + { + // +1 as keeping 1 indexed for code readability + V = malloc(sizeof(vertex)*(nV+1)); + E = malloc(sizeof(double)*(nV+1)*(nV+1)); + D = malloc(sizeof(double)*(nV+1)); + P = malloc(sizeof(int)*(nV+1)); + // Can be nV-1 since one would always be source node + C = malloc(sizeof(int)*nV); + shortestPath = malloc(sizeof(vertex)*nV); + secondShortestPath = malloc(sizeof(vertex)*nV); + if ( + V == NULL + || E == NULL + || D == NULL + || P == NULL + || C == NULL + || shortestPath == NULL + || secondShortestPath == NULL + ) + { + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + } + else + { + fprintf(stderr, "File read error\n"); + return 1; + } + + // Initializing all edge weights to double max; + for (int i=0; i <= nV; i++) + { + for (int j=0; j <= nV; j++) + { + E[i*nV+j] = DBL_MAX; + if (i == j) + E[i*nV+j] = 0; // Distance/weight to self is 0 + } + } + + // Read vertices + int v; + double x, y; + for (int i=1; i <= nV; i++) + { + fscanf(file, " %d %lf %lf ", &v, &x, &y); + if (v <= nV) + { + V[v].x = x; + V[v].y = y; + } + else + { + fprintf(stderr, "Vertex i (%d) > nV (%d)\n", v, nV); + return 1; + } + } + + // Read Edges/Weights + int v1, v2; + double w; + for (int i=0; i < nE; i++) + { + fscanf(file, " %d %d %lf", &v1, &v2, &w); + if (v1 <= nV && v2 <= nV) + { + if (E[v1*nV+v2] > w) + { + E[v1*nV+v2] = w; + E[v1+v2*nV] = w; + } + } + else + { + fprintf(stderr, "v1 or v2 > nE in Edges\n"); + return 1; + } + } + + // Read source and destination vertices for path finding + fscanf(file, " %d %d ", &src, &dst); + + fclose(file); + + aStar(); // Find shortest path + if (D[dst] == DBL_MAX) + printf( + "Path from vertex %d to vertex %d does not exist\n", src, dst); + else + { + // Recreate path from P (parents array) + int parent = dst; + while (P[parent] != parent) + { + shortestPath[shortestPathVertices++] = parent; + parent = P[parent]; + } + shortestPath[shortestPathVertices++] = parent; + + printf( + "Shortest path from vertex %d to vertex %d is:\n%d", + src, + dst, + src); + for (int i=shortestPathVertices-2; i >= 0; i--) + printf(" -> %d", shortestPath[i]); + printf( + "\nDistance/weight from vertex %d to vertex %d following the " + "shortest path is %.2lf\n\n", + src, + dst, + D[dst]); + + // Find second shortest path + double secondShortestDistance = DBL_MAX; + for (int i=0; i < shortestPathVertices-1; i++) + { + double w = E[shortestPath[i]*nV+shortestPath[i+1]]; + // Remove ith edge from shortest path + E[shortestPath[i]*nV+shortestPath[i+1]] = DBL_MAX; + E[shortestPath[i+1]*nV+shortestPath[i]] = DBL_MAX; + aStar(); // Find new path + // Add back ith edge from shortest path + E[shortestPath[i]*nV+shortestPath[i+1]] = w; + E[shortestPath[i+1]*nV+shortestPath[i]] = w; + // if current distance is shorter than previously found 2nd best + if (D[dst] < secondShortestDistance) + { // Update 2nd shortest distance and path + secondShortestDistance = D[dst]; + secondShortestPathVertices = 0; + int parent = dst; + while (P[parent] != parent) + { + secondShortestPath[ + secondShortestPathVertices++] = parent; + parent = P[parent]; + } + secondShortestPath[secondShortestPathVertices++] = parent; + } + } + // Print second shortest path found (or not found) + if (secondShortestDistance == DBL_MAX) + printf( + "Another/second Path from vertex %d to vertex %d does not " + "exist\n", + src, + dst); + else + { + printf("Second shortest path from vertex %d to vertex %d is:\n%d", src, dst, src); + for (int i=secondShortestPathVertices-2; i >= 0; i--) + printf(" -> %d", secondShortestPath[i]); + printf( + "\nDistance/weight from vertex %d to vertex %d following the " + "second shortest path is %.2lf\n", + src, + dst, + secondShortestDistance + ); + } + } + + // Free dynamically allocated memory + free(V); + free(E); + free(D); + free(P); + free(C); + free(shortestPath); + free(secondShortestPath); + + return 0; +} + + +void aStar() +{ + // Infantilise/Reset global variables for each iteration. + nC = 0; + for (int i=1; i <= nV; i++) + { + D[i] = DBL_MAX; + P[i] = i; + C[nC++] = i; + } + + // Remove source vertex from candidate list + swap(src-1, --nC); + int v = src; + D[src] = 0; // distance from src to src is 0 + for (int i=1; i <= nV; i++) + { + D[i] = E[src*nV+i]; + if (D[i] != DBL_MAX) + P[i] = src; + } + + heapify(); + + while (nC) + { + v = C[0]; + swap(0, --nC); + shiftdown(0); + + if (v == dst) + break; + + for (int j=0; j < nC; j++) + { + if (D[C[j]] > D[v] + E[v*nV+C[j]]) + { + D[C[j]] = D[v] + E[v*nV+C[j]]; + P[C[j]] = v; + shiftup(j); + } + } + } +} + +double euclidianDistance(int i, int j) +{ + double xDiff = absolute(V[i].x - V[j].x); + double yDiff = absolute(V[i].y - V[j].y); + return sqrt((xDiff*xDiff)+(yDiff*yDiff)); +} + + +double absolute(double n) +{ + return (n < 0) ? -n : n; +} + + +void heapify() +{ + int shiftdowns_required = (nC/2)-1; + for (int i = shiftdowns_required; i >= 0; i--) + { + shiftdown(i); + }; +} + +void shiftdown(int i) +{ + int child = (i * 2) + 1; // left child + if (child < nC) // has at least one child + { + if (child < nC - 1) // has both children + { + // if right child smaller + if ( + D[C[child]]+euclidianDistance(C[child], dst) > + D[C[child + 1]]+euclidianDistance(C[child+1], dst)) + child++; // pick right child + } + if ( + D[C[i]]+euclidianDistance(C[i], dst) + > D[C[child]]+euclidianDistance(C[child], dst)) + { + swap(i, child); + shiftdown(child); + } + } +} + +void shiftup(int i) +{ + if (i == 0) + return; + int parent = (i - 1) / 2; + if (D[C[parent]]+euclidianDistance(C[parent], dst) > + D[C[i]]+euclidianDistance(C[i], dst)) + { + swap(parent, i); + shiftup(parent); + } +} + +void swap(int i, int j) +{ + int tmp = C[i]; + C[i] = C[j]; + C[j] = tmp; +}