Enhance docs, add more tests in JohnsonsAlgorithm (#5964)

This commit is contained in:
Hardik Pawar
2024-10-24 10:48:12 +05:30
committed by GitHub
parent be0b1d58d6
commit 1b51e3e988
2 changed files with 59 additions and 56 deletions

View File

@ -21,17 +21,18 @@ import java.util.List;
*/ */
public final class JohnsonsAlgorithm { public final class JohnsonsAlgorithm {
// Constant representing infinity
private static final double INF = Double.POSITIVE_INFINITY; private static final double INF = Double.POSITIVE_INFINITY;
/**
* A private constructor to hide the implicit public one.
*/
private JohnsonsAlgorithm() { private JohnsonsAlgorithm() {
} }
/** /**
* Executes Johnson's algorithm on the given graph. * Executes Johnson's algorithm on the given graph.
* Steps:
* 1. Add a new vertex to the graph and run Bellman-Ford to compute modified weights
* 2. t the graph using the modified weights
* 3. Run Dijkstra's algorithm for each vertex to compute the shortest paths
* The final result is a 2D array of shortest distances between all pairs of vertices.
* *
* @param graph The input graph represented as an adjacency matrix. * @param graph The input graph represented as an adjacency matrix.
* @return A 2D array representing the shortest distances between all pairs of vertices. * @return A 2D array representing the shortest distances between all pairs of vertices.
@ -40,13 +41,10 @@ public final class JohnsonsAlgorithm {
int numVertices = graph.length; int numVertices = graph.length;
double[][] edges = convertToEdgeList(graph); double[][] edges = convertToEdgeList(graph);
// Step 1: Add a new vertex and run Bellman-Ford
double[] modifiedWeights = bellmanFord(edges, numVertices); double[] modifiedWeights = bellmanFord(edges, numVertices);
// Step 2: Reweight the graph
double[][] reweightedGraph = reweightGraph(graph, modifiedWeights); double[][] reweightedGraph = reweightGraph(graph, modifiedWeights);
// Step 3: Run Dijkstra's algorithm for each vertex
double[][] shortestDistances = new double[numVertices][numVertices]; double[][] shortestDistances = new double[numVertices][numVertices];
for (int source = 0; source < numVertices; source++) { for (int source = 0; source < numVertices; source++) {
shortestDistances[source] = dijkstra(reweightedGraph, source, modifiedWeights); shortestDistances[source] = dijkstra(reweightedGraph, source, modifiedWeights);
@ -74,7 +72,6 @@ public final class JohnsonsAlgorithm {
} }
} }
// Convert the List to a 2D array
return edgeList.toArray(new double[0][]); return edgeList.toArray(new double[0][]);
} }
@ -89,7 +86,7 @@ public final class JohnsonsAlgorithm {
private static double[] bellmanFord(double[][] edges, int numVertices) { private static double[] bellmanFord(double[][] edges, int numVertices) {
double[] dist = new double[numVertices + 1]; double[] dist = new double[numVertices + 1];
Arrays.fill(dist, INF); Arrays.fill(dist, INF);
dist[numVertices] = 0; // Distance to the new source vertex is 0 dist[numVertices] = 0;
// Add edges from the new vertex to all original vertices // Add edges from the new vertex to all original vertices
double[][] allEdges = Arrays.copyOf(edges, edges.length + numVertices); double[][] allEdges = Arrays.copyOf(edges, edges.length + numVertices);

View File

@ -23,114 +23,120 @@ class JohnsonsAlgorithmTest {
*/ */
@Test @Test
void testSimpleGraph() { void testSimpleGraph() {
// Test case for a simple graph without negative edges
double[][] graph = {{0, 4, INF, INF}, {INF, 0, 1, INF}, {INF, INF, 0, 2}, {INF, INF, INF, 0}}; double[][] graph = {{0, 4, INF, INF}, {INF, 0, 1, INF}, {INF, INF, 0, 2}, {INF, INF, INF, 0}};
double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);
double[][] expected = {{0, 4, 5, 7}, {INF, 0, 1, 3}, {INF, INF, 0, 2}, {INF, INF, INF, 0}}; double[][] expected = {{0, 4, 5, 7}, {INF, 0, 1, 3}, {INF, INF, 0, 2}, {INF, INF, INF, 0}};
assertArrayEquals(expected, result); assertArrayEquals(expected, result);
} }
/** /**
* Tests Johnson's Algorithm on a graph with negative edges but no * Tests Johnson's Algorithm on a graph with negative edges but no negative weight cycles.
* negative weight cycles. Verifies the algorithm handles negative
* edge weights correctly.
*/ */
@Test @Test
void testGraphWithNegativeEdges() { void testGraphWithNegativeEdges() {
// Graph with negative edges but no negative weight cycles
double[][] graph = {{0, -1, 4}, {INF, 0, 3}, {INF, INF, 0}}; double[][] graph = {{0, -1, 4}, {INF, 0, 3}, {INF, INF, 0}};
double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);
double[][] expected = {{0, INF, 4}, {INF, 0, 3}, {INF, INF, 0}}; double[][] expected = {{0, INF, 4}, {INF, 0, 3}, {INF, INF, 0}};
assertArrayEquals(expected, result); assertArrayEquals(expected, result);
} }
/** /**
* Tests the behavior of Johnson's Algorithm on a graph with a negative * Tests Johnson's Algorithm on a graph with a negative weight cycle.
* weight cycle. Expects an IllegalArgumentException to be thrown
* due to the presence of the cycle.
*/ */
@Test @Test
void testNegativeWeightCycle() { void testNegativeWeightCycle() {
// Graph with a negative weight cycle
double[][] graph = {{0, 1, INF}, {INF, 0, -1}, {-1, INF, 0}}; double[][] graph = {{0, 1, INF}, {INF, 0, -1}, {-1, INF, 0}};
assertThrows(IllegalArgumentException.class, () -> JohnsonsAlgorithm.johnsonAlgorithm(graph));
// Johnson's algorithm should throw an exception when a negative cycle is detected
assertThrows(IllegalArgumentException.class, () -> { JohnsonsAlgorithm.johnsonAlgorithm(graph); });
} }
/** /**
* Tests Dijkstra's algorithm as a part of Johnson's algorithm implementation * Tests Dijkstra's algorithm on a small graph as part of Johnson's Algorithm.
* on a small graph. Verifies that the shortest path is correctly calculated.
*/ */
@Test @Test
void testDijkstra() { void testDijkstra() {
// Testing Dijkstra's algorithm with a small graph
double[][] graph = {{0, 1, 2}, {INF, 0, 3}, {INF, INF, 0}}; double[][] graph = {{0, 1, 2}, {INF, 0, 3}, {INF, INF, 0}};
double[] modifiedWeights = {0, 0, 0};
double[] modifiedWeights = {0, 0, 0}; // No reweighting in this simple case
double[] result = JohnsonsAlgorithm.dijkstra(graph, 0, modifiedWeights); double[] result = JohnsonsAlgorithm.dijkstra(graph, 0, modifiedWeights);
double[] expected = {0, 1, 2}; double[] expected = {0, 1, 2};
assertArrayEquals(expected, result); assertArrayEquals(expected, result);
} }
/** /**
* Tests the conversion of an adjacency matrix to an edge list. * Tests the conversion of an adjacency matrix to an edge list.
* Verifies that the conversion process generates the correct edge list.
*/ */
@Test @Test
void testEdgeListConversion() { void testEdgeListConversion() {
// Test the conversion of adjacency matrix to edge list
double[][] graph = {{0, 5, INF}, {INF, 0, 2}, {INF, INF, 0}}; double[][] graph = {{0, 5, INF}, {INF, 0, 2}, {INF, INF, 0}};
// Running convertToEdgeList
double[][] edges = JohnsonsAlgorithm.convertToEdgeList(graph); double[][] edges = JohnsonsAlgorithm.convertToEdgeList(graph);
// Expected edge list: (0 -> 1, weight 5), (1 -> 2, weight 2)
double[][] expected = {{0, 1, 5}, {1, 2, 2}}; double[][] expected = {{0, 1, 5}, {1, 2, 2}};
// Verify the edge list matches the expected values
assertArrayEquals(expected, edges); assertArrayEquals(expected, edges);
} }
/** /**
* Tests the reweighting of a graph as a part of Johnson's Algorithm. * Tests the reweighting of a graph.
* Verifies that the reweighted graph produces correct results.
*/ */
@Test @Test
void testReweightGraph() { void testReweightGraph() {
// Test reweighting of the graph
double[][] graph = {{0, 2, 9}, {INF, 0, 1}, {INF, INF, 0}}; double[][] graph = {{0, 2, 9}, {INF, 0, 1}, {INF, INF, 0}};
double[] modifiedWeights = {1, 2, 3}; // Arbitrary weight function double[] modifiedWeights = {1, 2, 3};
double[][] reweightedGraph = JohnsonsAlgorithm.reweightGraph(graph, modifiedWeights); double[][] reweightedGraph = JohnsonsAlgorithm.reweightGraph(graph, modifiedWeights);
// Expected reweighted graph:
double[][] expected = {{0, 1, 7}, {INF, 0, 0}, {INF, INF, 0}}; double[][] expected = {{0, 1, 7}, {INF, 0, 0}, {INF, INF, 0}};
assertArrayEquals(expected, reweightedGraph); assertArrayEquals(expected, reweightedGraph);
} }
/** /**
* Tests the minDistance method used in Dijkstra's algorithm to find * Tests the minDistance method used in Dijkstra's algorithm.
* the vertex with the minimum distance that has not yet been visited.
*/ */
@Test @Test
void testMinDistance() { void testMinDistance() {
// Test minDistance method
double[] dist = {INF, 3, 1, INF}; double[] dist = {INF, 3, 1, INF};
boolean[] visited = {false, false, false, false}; boolean[] visited = {false, false, false, false};
int minIndex = JohnsonsAlgorithm.minDistance(dist, visited); int minIndex = JohnsonsAlgorithm.minDistance(dist, visited);
// The vertex with minimum distance is vertex 2 with a distance of 1
assertEquals(2, minIndex); assertEquals(2, minIndex);
} }
/**
* Tests Johnson's Algorithm on a graph where all vertices are disconnected.
*/
@Test
void testDisconnectedGraph() {
double[][] graph = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}};
double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);
double[][] expected = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}};
assertArrayEquals(expected, result);
}
/**
* Tests Johnson's Algorithm on a fully connected graph.
*/
@Test
void testFullyConnectedGraph() {
double[][] graph = {{0, 1, 2}, {1, 0, 1}, {2, 1, 0}};
double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);
double[][] expected = {{0, 1, 2}, {1, 0, 1}, {2, 1, 0}};
assertArrayEquals(expected, result);
}
/**
* Tests Dijkstra's algorithm on a graph with multiple shortest paths.
*/
@Test
void testDijkstraMultipleShortestPaths() {
double[][] graph = {{0, 1, 2, INF}, {INF, 0, INF, 1}, {INF, INF, 0, 1}, {INF, INF, INF, 0}};
double[] modifiedWeights = {0, 0, 0, 0};
double[] result = JohnsonsAlgorithm.dijkstra(graph, 0, modifiedWeights);
double[] expected = {0, 1, 2, 2};
assertArrayEquals(expected, result);
}
/**
* Tests Johnson's Algorithm with a graph where all edge weights are zero.
*/
@Test
void testGraphWithZeroWeights() {
double[][] graph = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);
double[][] expected = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}};
assertArrayEquals(expected, result);
}
} }