How to Implement Johnson’s Algorithm in Python
In this tutorial, we will learn "How to implement Johnson’s Algorithm in Python". The main objective is to understand and implement the algorithm effectively. This guide will walk you step by step through the process, making it easier to follow and apply. By the end of this tutorial, you will have a solid understanding of how Johnson’s Algorithm works in Python, helping you strengthen your problem-solving abilities and improve your overall coding skills in data structure implementation.
This topic is straightforward and easy to understand. By following the instructions provided, you will be able to complete it with ease. The program will guide you step by step through the process of implementing Johnson’s Algorithm. So, let’s dive into the coding process and start implementing the solution to gain a deeper understanding of graph algorithms in Python.
Getting Started:
First you will have to download & install the Python IDLE's, here's the link for the Integrated Development And Learning Environment for Python https://www.python.org/downloads/.
Creating Main Function
This is the main function of the application. The following code will display a simple GUI in terminal console that will display program. To do this, simply copy and paste these blocks of code into the IDLE text editor.- class Graph:
- def __init__(self):
- self.vertices = {}
- def add_vertex(self, key):
- self.vertices[key] = Vertex(key)
- def get_vertex(self, key):
- return self.vertices[key]
- def __contains__(self, key):
- return key in self.vertices
- def add_edge(self, src_key, dest_key, weight=1):
- self.vertices[src_key].add_neighbour(self.vertices[dest_key], weight)
- def does_edge_exist(self, src_key, dest_key):
- return self.vertices[src_key].does_it_point_to(self.vertices[dest_key])
- def __len__(self):
- return len(self.vertices)
- def __iter__(self):
- return iter(self.vertices.values())
- class Vertex:
- def __init__(self, key):
- self.key = key
- self.points_to = {}
- def get_key(self):
- return self.key
- def add_neighbour(self, dest, weight):
- self.points_to[dest] = weight
- def get_neighbours(self):
- return self.points_to.keys()
- def get_weight(self, dest):
- return self.points_to[dest]
- def set_weight(self, dest, weight):
- self.points_to[dest] = weight
- def does_it_point_to(self, dest):
- return dest in self.points_to
- def bellman_ford(g, source):
- distance = dict.fromkeys(g, float('inf'))
- distance[source] = 0
- for _ in range(len(g) - 1):
- for v in g:
- for n in v.get_neighbours():
- if distance[v] + v.get_weight(n) < distance[n]:
- distance[n] = distance[v] + v.get_weight(n)
- # NEGATIVE CYCLE CHECK
- for v in g:
- for n in v.get_neighbours():
- if distance[v] + v.get_weight(n) < distance[n]:
- print("Graph contains a negative weight cycle!")
- return None
- return distance
- def dijkstra(g, source):
- unvisited = set(g)
- distance = dict.fromkeys(g, float('inf'))
- distance[source] = 0
- while unvisited:
- closest = min(unvisited, key=lambda v: distance[v])
- unvisited.remove(closest)
- for neighbour in closest.get_neighbours():
- if neighbour in unvisited:
- new_distance = distance[closest] + closest.get_weight(neighbour)
- if new_distance < distance[neighbour]:
- distance[neighbour] = new_distance
- return distance
- def johnson(g):
- # Step 1: Add temporary vertex q
- g.add_vertex('q')
- q = g.get_vertex('q')
- for v in list(g): # avoid modifying during iteration
- if v.get_key() != 'q':
- g.add_edge('q', v.get_key(), 0)
- # Step 2: Bellman-Ford
- bell_dist = bellman_ford(g, q)
- if bell_dist is None:
- return None
- # Step 3: Reweight edges
- for v in g:
- for n in v.get_neighbours():
- w = v.get_weight(n)
- v.set_weight(n, w + bell_dist[v] - bell_dist[n])
- # Step 4: Remove q safely
- del g.vertices['q']
- # Step 5: Run Dijkstra for each vertex
- distance = {}
- for v in g:
- distance[v] = dijkstra(g, v)
- # Step 6: Restore original weights
- for v in g:
- for n in v.get_neighbours():
- w = v.get_weight(n)
- v.set_weight(n, w + bell_dist[n] - bell_dist[v])
- return distance
- # MAIN PROGRAM
- g = Graph()
- while True:
- print("\n========== Johnson’s Algorithm ==========")
- print("Menu")
- print("add vertex <key>")
- print("add edge <src> <dest> <weight>")
- print("johnson")
- print("display")
- print("quit")
- do = input('What would you like to do? ').split()
- if not do:
- continue
- operation = do[0]
- if operation == 'add':
- suboperation = do[1]
- if suboperation == 'vertex':
- key = int(do[2])
- if key not in g:
- g.add_vertex(key)
- else:
- print('Vertex already exists.')
- elif suboperation == 'edge':
- src = int(do[2])
- dest = int(do[3])
- weight = int(do[4])
- if src not in g:
- print(f'Vertex {src} does not exist.')
- elif dest not in g:
- print(f'Vertex {dest} does not exist.')
- else:
- if not g.does_edge_exist(src, dest):
- g.add_edge(src, dest, weight)
- else:
- print('Edge already exists.')
- elif operation == 'johnson':
- distance = johnson(g)
- if distance:
- print('Shortest distances:')
- for start in g:
- for end in g:
- print(f'{start.get_key()} -> {end.get_key()} = {distance[start][end]}')
- elif operation == 'display':
- print('Vertices:', end=' ')
- for v in g:
- print(v.get_key(), end=' ')
- print()
- print('Edges:')
- for v in g:
- for dest in v.get_neighbours():
- w = v.get_weight(dest)
- print(f'(src={v.get_key()}, dest={dest.get_key()}, weight={w})')
- elif operation == 'quit':
- print("Exiting program...")
- break
- else:
- print("Invalid command.")
This Python program implements Johnson’s algorithm for finding the shortest paths between all pairs of vertices in a weighted graph, including graphs with negative edge weights but no negative cycles. It defines `Graph` and `Vertex` classes to represent the graph structure, storing vertices and weighted edges. The program combines Bellman-Ford (to detect negative cycles and compute vertex potentials) and Dijkstra’s algorithm (to efficiently compute shortest paths after reweighting edges). Users interact with the program via a command-line menu to add vertices and edges, display the graph, run Johnson’s algorithm to compute all-pairs shortest paths, and exit. The algorithm ensures correct shortest distances are reported even in the presence of negative edges, while preventing negative weight cycles from producing invalid results.
Output:
There you have it we successfully created How to Implement Johnson’s Algorithm in Python. I hope that this simple tutorial help you to what you are looking for. For more updates and tutorials just kindly visit this site. Enjoy Coding!
More Tutorials for Python Language