|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +## |
| 4 | +# Dijkstra's Algorithm |
| 5 | +# |
| 6 | +# Shortest path using Dijkstra's algorithm |
| 7 | +## |
| 8 | + |
| 9 | +import numpy as np |
| 10 | +import sys |
| 11 | + |
| 12 | +# Graph structure - adjacency matrix |
| 13 | +vertices = {'arad': 0, 'zerind': 1, 'oradea': 2, 'sibiu': 3, 'timisoara': 4, |
| 14 | + 'lugoj': 5, 'mehadia': 6, 'dobreta': 7, 'craiova': 8, 'rimnicu': 9, |
| 15 | + 'fagaras': 10, 'pitesti': 11, 'bucharest': 12, 'giurgiu': 13} |
| 16 | + |
| 17 | +# Reverse lookup for cities |
| 18 | +cities = {0: 'arad', 1: 'zerind', 2: 'oradea', 3: 'sibiu', 4: 'timisoara', |
| 19 | + 5: 'lugoj', 6: 'mehadia', 7: 'dobreta', 8: 'craiova', 9: 'rimnicu', |
| 20 | + 10: 'fagaras', 11: 'pitesti', 12: 'bucharest', 13: 'giurgiu'} |
| 21 | + |
| 22 | +# Initialize adjacency matrix for the graph |
| 23 | +edges = np.zeros([len(cities), len(cities)], dtype=int) |
| 24 | + |
| 25 | +# Set edge weights for the graph |
| 26 | +edges[vertices['arad'], [vertices['zerind']]] = 75 |
| 27 | +edges[vertices['arad'], [vertices['sibiu']]] = 140 |
| 28 | +edges[vertices['arad'], [vertices['timisoara']]] = 118 |
| 29 | + |
| 30 | +edges[vertices['zerind'], [vertices['arad']]] = 75 |
| 31 | +edges[vertices['zerind'], [vertices['oradea']]] = 71 |
| 32 | + |
| 33 | +edges[vertices['oradea'], [vertices['zerind']]] = 71 |
| 34 | +edges[vertices['oradea'], [vertices['sibiu']]] = 151 |
| 35 | + |
| 36 | +edges[vertices['sibiu'], [vertices['oradea']]] = 151 |
| 37 | +edges[vertices['sibiu'], [vertices['arad']]] = 140 |
| 38 | +edges[vertices['sibiu'], [vertices['fagaras']]] = 99 |
| 39 | +edges[vertices['sibiu'], [vertices['rimnicu']]] = 80 |
| 40 | + |
| 41 | +edges[vertices['timisoara'], [vertices['arad']]] = 118 |
| 42 | +edges[vertices['timisoara'], [vertices['lugoj']]] = 111 |
| 43 | + |
| 44 | +edges[vertices['lugoj'], [vertices['timisoara']]] = 111 |
| 45 | +edges[vertices['lugoj'], [vertices['mehadia']]] = 70 |
| 46 | + |
| 47 | +edges[vertices['mehadia'], [vertices['lugoj']]] = 70 |
| 48 | +edges[vertices['mehadia'], [vertices['dobreta']]] = 75 |
| 49 | + |
| 50 | +edges[vertices['dobreta'], [vertices['mehadia']]] = 75 |
| 51 | +edges[vertices['dobreta'], [vertices['craiova']]] = 120 |
| 52 | + |
| 53 | +edges[vertices['craiova'], [vertices['dobreta']]] = 120 |
| 54 | +edges[vertices['craiova'], [vertices['pitesti']]] = 138 |
| 55 | +edges[vertices['craiova'], [vertices['rimnicu']]] = 146 |
| 56 | + |
| 57 | +edges[vertices['rimnicu'], [vertices['craiova']]] = 146 |
| 58 | +edges[vertices['rimnicu'], [vertices['sibiu']]] = 80 |
| 59 | +edges[vertices['rimnicu'], [vertices['pitesti']]] = 97 |
| 60 | + |
| 61 | +edges[vertices['fagaras'], [vertices['sibiu']]] = 99 |
| 62 | +edges[vertices['fagaras'], [vertices['bucharest']]] = 211 |
| 63 | + |
| 64 | +edges[vertices['pitesti'], [vertices['rimnicu']]] = 97 |
| 65 | +edges[vertices['pitesti'], [vertices['craiova']]] = 138 |
| 66 | +edges[vertices['pitesti'], [vertices['bucharest']]] = 101 |
| 67 | + |
| 68 | +edges[vertices['bucharest'], [vertices['fagaras']]] = 211 |
| 69 | +edges[vertices['bucharest'], [vertices['pitesti']]] = 101 |
| 70 | +edges[vertices['bucharest'], [vertices['giurgiu']]] = 90 |
| 71 | + |
| 72 | + |
| 73 | +# Dijkstra's Algorithm class |
| 74 | +class Dijkstra: |
| 75 | + """ |
| 76 | + Dijkstra's algorithm to find the shortest path in a graph. |
| 77 | +
|
| 78 | + Attributes: |
| 79 | + - vertices: List of graph vertices |
| 80 | + - edges: Adjacency matrix representing the graph |
| 81 | + - start: Starting vertex for the algorithm |
| 82 | + """ |
| 83 | + |
| 84 | + def __init__(self, vertices, edges, start): |
| 85 | + """ |
| 86 | + Initializes the Dijkstra object with the graph and starting vertex. |
| 87 | +
|
| 88 | + :param vertices: List of vertices in the graph |
| 89 | + :param edges: Adjacency matrix representing the edges of the graph |
| 90 | + :param start: The starting vertex for the algorithm |
| 91 | + """ |
| 92 | + self.size = len(vertices) |
| 93 | + self.vertices = vertices |
| 94 | + self.graph = edges |
| 95 | + self.start = start |
| 96 | + |
| 97 | + def display_solution(self, distances): |
| 98 | + """ |
| 99 | + Displays the shortest distances from the start vertex to all other vertices. |
| 100 | +
|
| 101 | + :param distances: List of shortest distances |
| 102 | + """ |
| 103 | + print(f'Shortest distances from {self.vertices[self.start]} to all other vertices:') |
| 104 | + for vertex in range(self.size): |
| 105 | + print(f'{self.vertices[vertex]}: {distances[vertex]}') |
| 106 | + |
| 107 | + def find_min_distance(self, distance, visited): |
| 108 | + """ |
| 109 | + Finds the vertex with the minimum distance that has not been visited. |
| 110 | +
|
| 111 | + :param distance: List of current distances |
| 112 | + :param visited: List of visited vertices |
| 113 | + :return: Index of the vertex with the minimum distance |
| 114 | + """ |
| 115 | + min_distance = sys.maxsize |
| 116 | + for vertex in range(self.size): |
| 117 | + if distance[vertex] < min_distance and not visited[vertex]: |
| 118 | + min_distance = distance[vertex] |
| 119 | + min_index = vertex |
| 120 | + return min_index |
| 121 | + |
| 122 | + def run_dijkstra(self): |
| 123 | + """ |
| 124 | + Runs Dijkstra's algorithm to find the shortest path from the start vertex. |
| 125 | + """ |
| 126 | + distance = [sys.maxsize] * self.size |
| 127 | + distance[self.start] = 0 |
| 128 | + visited = [False] * self.size |
| 129 | + |
| 130 | + for _ in range(self.size): |
| 131 | + min_index = self.find_min_distance(distance, visited) |
| 132 | + visited[min_index] = True |
| 133 | + |
| 134 | + for vertex in range(self.size): |
| 135 | + if self.graph[min_index][vertex] > 0 and not visited[vertex] \ |
| 136 | + and distance[vertex] > distance[min_index] + self.graph[min_index][vertex]: |
| 137 | + distance[vertex] = distance[min_index] + self.graph[min_index][vertex] |
| 138 | + |
| 139 | + self.display_solution(distance) |
| 140 | + |
| 141 | + |
| 142 | +# Example with the first graph |
| 143 | +dijkstra = Dijkstra(cities, edges, vertices['arad']) |
| 144 | +dijkstra.run_dijkstra() |
| 145 | + |
| 146 | +# Test with another graph |
| 147 | +vertices2 = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5} |
| 148 | +vertices2_inv = {0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F'} |
| 149 | + |
| 150 | +edges2 = np.zeros([len(vertices2), len(vertices2)], dtype=int) |
| 151 | +edges2[vertices2['A'], [vertices2['B']]] = 2 |
| 152 | +edges2[vertices2['A'], [vertices2['C']]] = 1 |
| 153 | +edges2[vertices2['B'], [vertices2['D']]] = 1 |
| 154 | +edges2[vertices2['C'], [vertices2['D']]] = 3 |
| 155 | +edges2[vertices2['C'], [vertices2['E']]] = 4 |
| 156 | +edges2[vertices2['D'], [vertices2['F']]] = 2 |
| 157 | +edges2[vertices2['E'], [vertices2['F']]] = 2 |
| 158 | + |
| 159 | +# Running Dijkstra on the second graph |
| 160 | +dijkstra = Dijkstra(vertices2_inv, edges2, vertices2['A']) |
| 161 | +dijkstra.run_dijkstra() |
0 commit comments