-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjohnson.py
136 lines (108 loc) · 4.5 KB
/
johnson.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from heapq import heappop, heappush
import math
class Graph:
"""
A class to represent a directed graph for Johnson's Algorithm.
"""
def __init__(self, vertices):
"""
Initialize the graph with a specified number of vertices.
:param vertices: The number of vertices in the graph.
"""
self.vertices = vertices
self.edges = [] # List to store edges as (u, v, weight).
def add_edge(self, u, v, weight):
"""
Add a directed edge to the graph.
:param u: The source vertex.
:param v: The destination vertex.
:param weight: The weight of the edge.
"""
self.edges.append((u, v, weight))
def _bellman_ford(self, source):
"""
Perform the Bellman-Ford algorithm to find shortest paths from a source.
:param source: The source vertex.
:return: A list of shortest distances from the source to all vertices.
Returns None if a negative cycle is detected.
"""
distances = [math.inf] * self.vertices
distances[source] = 0
# Relax edges up to |V|-1 times.
for _ in range(self.vertices - 1):
for u, v, weight in self.edges:
if distances[u] + weight < distances[v]:
distances[v] = distances[u] + weight
# Check for negative weight cycles.
for u, v, weight in self.edges:
if distances[u] + weight < distances[v]:
return None # Negative cycle detected.
return distances
def _dijkstra(self, source, weights):
"""
Perform Dijkstra's algorithm to find shortest paths from a source.
:param source: The source vertex.
:param weights: Adjusted weights of the edges for Dijkstra's algorithm.
:return: A list of shortest distances from the source to all vertices.
"""
distances = [math.inf] * self.vertices
distances[source] = 0
priority_queue = [(0, source)]
while priority_queue:
current_distance, u = heappop(priority_queue)
if current_distance > distances[u]:
continue
for v, weight in weights.get(u, []):
new_distance = distances[u] + weight
if new_distance < distances[v]:
distances[v] = new_distance
heappush(priority_queue, (new_distance, v))
return distances
def johnsons_algorithm(self):
"""
Perform Johnson's Algorithm to find shortest paths between all pairs.
:return: A matrix of shortest path distances.
Returns None if a negative cycle is detected.
"""
# Step 1: Add a new vertex s connected to all vertices with 0-weight edges.
self.edges.extend((self.vertices, v, 0) for v in range(self.vertices))
self.vertices += 1 # Temporarily increase vertex count.
# Step 2: Perform Bellman-Ford from the new vertex.
h = self._bellman_ford(self.vertices - 1)
self.vertices -= 1 # Restore original vertex count.
if h is None:
return None # Negative cycle detected.
# Step 3: Adjust edge weights using the vertex weights h.
reweighted_edges = {}
for u, v, weight in self.edges:
if u < self.vertices:
reweighted_edges.setdefault(u, []).append((v, weight + h[u] - h[v]))
# Step 4: Perform Dijkstra's algorithm for each vertex.
all_pairs_distances = []
for u in range(self.vertices):
distances = self._dijkstra(u, reweighted_edges)
# Adjust distances back to original weights.
adjusted_distances = [
d - h[u] + h[v] if d != math.inf else math.inf for v, d in enumerate(distances)
]
all_pairs_distances.append(adjusted_distances)
return all_pairs_distances
# Example usage:
if __name__ == "__main__":
# Create a graph with 5 vertices.
graph = Graph(5)
# Add edges with their weights.
graph.add_edge(0, 1, 3)
graph.add_edge(0, 2, 8)
graph.add_edge(1, 3, 1)
graph.add_edge(2, 3, -4)
graph.add_edge(3, 4, 2)
graph.add_edge(4, 0, -1)
# Run Johnson's Algorithm.
distances = graph.johnsons_algorithm()
if distances is None:
print("The graph contains a negative weight cycle.")
else:
print("Shortest distances between all pairs of vertices:")
for u, row in enumerate(distances):
print(f"From vertex {u}: {row}")