From d44fba1ad37fb7667b7de4caa96fc10fc2e05340 Mon Sep 17 00:00:00 2001 From: AnanteshG Date: Sun, 13 Oct 2024 21:11:50 +0530 Subject: [PATCH] Dijkstra visualizer added --- .../Dijkstra/main.py | 75 +++++++++++++++++++ .../Dijkstra/test_main.py | 41 ++++++++++ 2 files changed, 116 insertions(+) create mode 100644 Algorithms_and_Data_Structures/Dijkstra/main.py create mode 100644 Algorithms_and_Data_Structures/Dijkstra/test_main.py diff --git a/Algorithms_and_Data_Structures/Dijkstra/main.py b/Algorithms_and_Data_Structures/Dijkstra/main.py new file mode 100644 index 0000000000..a78e80f087 --- /dev/null +++ b/Algorithms_and_Data_Structures/Dijkstra/main.py @@ -0,0 +1,75 @@ +import networkx as nx +import matplotlib.pyplot as plt + +# function to create graph +def graph_create(): + G = nx.Graph() + nodes_num = int(input("Enter the number of nodes: ")) + for i in range(nodes_num): + nodes_name = input(f"Enter the name of node {i+1} : ") + + edges = int(input("Enter the number of edges: ")) + for i in range(edges): + edge_name = input(f"Enter edge {i+1} : (format: source, destination, value): ") + source, destination, weight = edge_name.split() + G.add_edge(source, destination, weight = int(weight)) + + return G + +# dijkstra algorithm implementation +def dijkstra(graph, start): + distances = {node: float("inf") for node in graph.nodes} #dictionary to store shortest distances + distances[start] = 0 + paths = {node: [] for node in graph.nodes} #dictionary to store shortest paths + visited = set() + + while len(visited) < len(graph.nodes): #loop till all nodes are visited + not_visited = {node: distances[node] for node in graph.nodes if node not in visited} #dictionary contains distances of unvisited nodes + min_node = min(not_visited, key=not_visited.get) # to get node with minimum distance from start node + visited.add(min_node) + + for neighbor, weight in graph[min_node].items(): + # If the distance to the neighbor through the current node is less than the previously known shortest distance to the neighbor + if distances[min_node] + weight["weight"] < distances[neighbor]: + # Update the shortest distance and path to the neighbor + distances[neighbor] = distances[min_node] + weight["weight"] + paths[neighbor] = paths[min_node] + [min_node] + + # After visiting all nodes, finalize the shortest paths by adding the destination node to each path + + paths = {node: path + [node] for node, path in paths.items() if path} + + return distances, paths + +def visualise_dijkstra(graph, start): + if start not in graph.nodes: + print("Start node not found in graph") + return + + distances, paths = dijkstra(graph, start) + pos = nx.spring_layout(graph) + plt.get_current_fig_manager().window.title("Dijkstra Algorithm Visualiser") + nx.draw(graph, pos, with_labels = True, node_color = "lightblue", edgecolors="black", node_size = 500, font_size = 15, font_weight = "bold") + labels = nx.get_edge_attributes(graph, "weight") + nx.draw_networkx_edge_labels(graph, pos, edge_labels = labels, font_size = 8) + + plt.title("Dijkstra's Algorithm Visualisation") + print("Shortest distances from the start node:") + for node, distance in distances.items(): + print(f"{node}: {distance}") + + print("Shortest paths from the start node:") + for node, path in paths.items(): + print(f"{node}: {' -> '.join(path)}") + + plt.show() + +if __name__ == "__main__": + user_graph = graph_create() + start_node = input("Enter the start node: ") + visualise_dijkstra(user_graph, start_node) + + + + + \ No newline at end of file diff --git a/Algorithms_and_Data_Structures/Dijkstra/test_main.py b/Algorithms_and_Data_Structures/Dijkstra/test_main.py new file mode 100644 index 0000000000..1a7ed36bdb --- /dev/null +++ b/Algorithms_and_Data_Structures/Dijkstra/test_main.py @@ -0,0 +1,41 @@ +import unittest +from main import dijkstra + +class DijkstraTestCase(unittest.TestCase): + def test_shortest_path(self): + graph = { + 'A': {'B': {'weight': 5}, 'C': {'weight': 3}}, + 'B': {'A': {'weight': 5}, 'C': {'weight': 2}, 'D': {'weight': 1}}, + 'C': {'A': {'weight': 3}, 'B': {'weight': 2}, 'D': {'weight': 4}, 'E': {'weight': 6}}, + 'D': {'B': {'weight': 1}, 'C': {'weight': 4}, 'E': {'weight': 2}}, + 'E': {'C': {'weight': 6}, 'D': {'weight': 2}} + } + start_node = 'A' + expected_distances = {'A': 0, 'B': 3, 'C': 3, 'D': 4, 'E': 6} + expected_paths = {'A': ['A'], 'B': ['A', 'B'], 'C': ['A', 'C'], 'D': ['A', 'B', 'D'], 'E': ['A', 'B', 'D', 'E']} + + distances, paths = dijkstra(graph, start_node) + + self.assertEqual(distances, expected_distances) + self.assertEqual(paths, expected_paths) + + def test_disconnected_graph(self): + graph = { + 'A': {'B': {'weight': 5}, 'C': {'weight': 3}}, + 'B': {'A': {'weight': 5}, 'C': {'weight': 2}, 'D': {'weight': 1}}, + 'C': {'A': {'weight': 3}, 'B': {'weight': 2}, 'D': {'weight': 4}, 'E': {'weight': 6}}, + 'D': {'B': {'weight': 1}, 'C': {'weight': 4}, 'E': {'weight': 2}}, + 'E': {'C': {'weight': 6}, 'D': {'weight': 2}}, + 'F': {} # Disconnected node + } + start_node = 'A' + expected_distances = {'A': 0, 'B': 3, 'C': 3, 'D': 4, 'E': 6} + expected_paths = {'A': ['A'], 'B': ['A', 'B'], 'C': ['A', 'C'], 'D': ['A', 'B', 'D'], 'E': ['A', 'B', 'D', 'E']} + + distances, paths = dijkstra(graph, start_node) + + self.assertEqual(distances, expected_distances) + self.assertEqual(paths, expected_paths) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file