Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@
*/
public final class JohnsonsAlgorithm {

// Constant representing infinity
private static final double INF = Double.POSITIVE_INFINITY;

/**
* A private constructor to hide the implicit public one.
*/
private JohnsonsAlgorithm() {
}

/**
* 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.
* @return A 2D array representing the shortest distances between all pairs of vertices.
Expand All @@ -40,13 +41,10 @@ public static double[][] johnsonAlgorithm(double[][] graph) {
int numVertices = graph.length;
double[][] edges = convertToEdgeList(graph);

// Step 1: Add a new vertex and run Bellman-Ford
double[] modifiedWeights = bellmanFord(edges, numVertices);

// Step 2: Reweight the graph
double[][] reweightedGraph = reweightGraph(graph, modifiedWeights);

// Step 3: Run Dijkstra's algorithm for each vertex
double[][] shortestDistances = new double[numVertices][numVertices];
for (int source = 0; source < numVertices; source++) {
shortestDistances[source] = dijkstra(reweightedGraph, source, modifiedWeights);
Expand Down Expand Up @@ -74,7 +72,6 @@ public static double[][] convertToEdgeList(double[][] graph) {
}
}

// Convert the List to a 2D array
return edgeList.toArray(new double[0][]);
}

Expand All @@ -89,7 +86,7 @@ public static double[][] convertToEdgeList(double[][] graph) {
private static double[] bellmanFord(double[][] edges, int numVertices) {
double[] dist = new double[numVertices + 1];
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
double[][] allEdges = Arrays.copyOf(edges, edges.length + numVertices);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,114 +23,120 @@ class JohnsonsAlgorithmTest {
*/
@Test
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[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);

double[][] expected = {{0, 4, 5, 7}, {INF, 0, 1, 3}, {INF, INF, 0, 2}, {INF, INF, INF, 0}};

assertArrayEquals(expected, result);
}

/**
* Tests Johnson's Algorithm on a graph with negative edges but no
* negative weight cycles. Verifies the algorithm handles negative
* edge weights correctly.
* Tests Johnson's Algorithm on a graph with negative edges but no negative weight cycles.
*/
@Test
void testGraphWithNegativeEdges() {
// Graph with negative edges but no negative weight cycles
double[][] graph = {{0, -1, 4}, {INF, 0, 3}, {INF, INF, 0}};

double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph);

double[][] expected = {{0, INF, 4}, {INF, 0, 3}, {INF, INF, 0}};

assertArrayEquals(expected, result);
}

/**
* Tests the behavior of Johnson's Algorithm on a graph with a negative
* weight cycle. Expects an IllegalArgumentException to be thrown
* due to the presence of the cycle.
* Tests Johnson's Algorithm on a graph with a negative weight cycle.
*/
@Test
void testNegativeWeightCycle() {
// Graph with a negative weight cycle
double[][] graph = {{0, 1, INF}, {INF, 0, -1}, {-1, INF, 0}};

// Johnson's algorithm should throw an exception when a negative cycle is detected
assertThrows(IllegalArgumentException.class, () -> { JohnsonsAlgorithm.johnsonAlgorithm(graph); });
assertThrows(IllegalArgumentException.class, () -> JohnsonsAlgorithm.johnsonAlgorithm(graph));
}

/**
* Tests Dijkstra's algorithm as a part of Johnson's algorithm implementation
* on a small graph. Verifies that the shortest path is correctly calculated.
* Tests Dijkstra's algorithm on a small graph as part of Johnson's Algorithm.
*/
@Test
void testDijkstra() {
// Testing Dijkstra's algorithm with a small graph
double[][] graph = {{0, 1, 2}, {INF, 0, 3}, {INF, INF, 0}};

double[] modifiedWeights = {0, 0, 0}; // No reweighting in this simple case

double[] modifiedWeights = {0, 0, 0};
double[] result = JohnsonsAlgorithm.dijkstra(graph, 0, modifiedWeights);
double[] expected = {0, 1, 2};

assertArrayEquals(expected, result);
}

/**
* Tests the conversion of an adjacency matrix to an edge list.
* Verifies that the conversion process generates the correct edge list.
*/
@Test
void testEdgeListConversion() {
// Test the conversion of adjacency matrix to edge list
double[][] graph = {{0, 5, INF}, {INF, 0, 2}, {INF, INF, 0}};

// Running convertToEdgeList
double[][] edges = JohnsonsAlgorithm.convertToEdgeList(graph);

// Expected edge list: (0 -> 1, weight 5), (1 -> 2, weight 2)
double[][] expected = {{0, 1, 5}, {1, 2, 2}};

// Verify the edge list matches the expected values
assertArrayEquals(expected, edges);
}

/**
* Tests the reweighting of a graph as a part of Johnson's Algorithm.
* Verifies that the reweighted graph produces correct results.
* Tests the reweighting of a graph.
*/
@Test
void testReweightGraph() {
// Test reweighting of the graph
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);

// Expected reweighted graph:
double[][] expected = {{0, 1, 7}, {INF, 0, 0}, {INF, INF, 0}};

assertArrayEquals(expected, reweightedGraph);
}

/**
* Tests the minDistance method used in Dijkstra's algorithm to find
* the vertex with the minimum distance that has not yet been visited.
* Tests the minDistance method used in Dijkstra's algorithm.
*/
@Test
void testMinDistance() {
// Test minDistance method
double[] dist = {INF, 3, 1, INF};
boolean[] visited = {false, false, false, false};

int minIndex = JohnsonsAlgorithm.minDistance(dist, visited);

// The vertex with minimum distance is vertex 2 with a distance of 1
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);
}
}