Created
April 27, 2025 17:57
-
-
Save EssamWisam/58685c246e74bf4bf94cc89ef171adb0 to your computer and use it in GitHub Desktop.
Shortest Paths
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"id": "aea99bab", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Testing Dijkstra's algorithm:\n", | |
"----------------------------------------\n", | |
"Node s: Expected=0, Actual=0 - PASS\n", | |
"Node t: Expected=8, Actual=8 - PASS\n", | |
"Node x: Expected=9, Actual=9 - PASS\n", | |
"Node y: Expected=5, Actual=5 - PASS\n", | |
"Node z: Expected=7, Actual=7 - PASS\n", | |
"----------------------------------------\n", | |
"All tests passed! The implementation is correct.\n" | |
] | |
} | |
], | |
"source": [ | |
"import heapq\n", | |
"\n", | |
"def dijkstra(graph, start):\n", | |
" \"\"\"\n", | |
" Implements Dijkstra's algorithm to find shortest paths from start node to all other nodes.\n", | |
" \n", | |
" Args:\n", | |
" graph: Dictionary where keys are nodes and values are dictionaries of connected nodes and edge weights\n", | |
" start: Starting node\n", | |
" \n", | |
" Returns:\n", | |
" distances: Dictionary of shortest distances from start to all nodes\n", | |
" predecessors: Dictionary of predecessors in the shortest path\n", | |
" \"\"\"\n", | |
" # Initialize distances with infinity for all nodes except start\n", | |
" distances = {node: float('infinity') for node in graph}\n", | |
" distances[start] = 0\n", | |
" \n", | |
" # Initialize predecessors\n", | |
" predecessors = {node: None for node in graph}\n", | |
" \n", | |
" # Priority queue to store vertices to visit\n", | |
" priority_queue = [(0, start)]\n", | |
" \n", | |
" # Keep track of visited nodes\n", | |
" visited = set()\n", | |
" \n", | |
" while priority_queue:\n", | |
" # Get the node with the smallest distance\n", | |
" current_distance, current_node = heapq.heappop(priority_queue)\n", | |
" \n", | |
" # If we've already processed this node, skip it\n", | |
" if current_node in visited:\n", | |
" continue\n", | |
" \n", | |
" # Mark node as visited\n", | |
" visited.add(current_node)\n", | |
" \n", | |
" # If current distance is greater than the known distance, skip\n", | |
" if current_distance > distances[current_node]:\n", | |
" continue\n", | |
" \n", | |
" # Check all neighbors of the current node\n", | |
" for neighbor, weight in graph[current_node].items():\n", | |
" # Calculate potential new distance\n", | |
" distance = current_distance + weight\n", | |
" \n", | |
" # If we found a shorter path, update distance and predecessor\n", | |
" if distance < distances[neighbor]:\n", | |
" distances[neighbor] = distance\n", | |
" predecessors[neighbor] = current_node\n", | |
" heapq.heappush(priority_queue, (distance, neighbor))\n", | |
" \n", | |
" return distances, predecessors\n", | |
"\n", | |
"\n", | |
"def test_dijkstra():\n", | |
" \"\"\"\n", | |
" Test function to verify Dijkstra's algorithm against expected results.\n", | |
" \"\"\"\n", | |
" # Define the graph from the image\n", | |
" graph = {\n", | |
" 's': {'t': 10, 'y': 5},\n", | |
" 't': {'x': 1, 'y': 2},\n", | |
" 'y': {'t': 3, 'z': 2},\n", | |
" 'x': {'z': 4},\n", | |
" 'z': {'x': 6, 's': 7}\n", | |
" }\n", | |
" \n", | |
" # Expected shortest distances from node 's'\n", | |
" expected_distances = {\n", | |
" 's': 0,\n", | |
" 't': 8,\n", | |
" 'x': 9,\n", | |
" 'y': 5,\n", | |
" 'z': 7\n", | |
" }\n", | |
" \n", | |
" # Run Dijkstra's algorithm from node 's'\n", | |
" start_node = 's'\n", | |
" actual_distances, predecessors = dijkstra(graph, start_node)\n", | |
" \n", | |
" # Verify results\n", | |
" all_correct = True\n", | |
" print(\"Testing Dijkstra's algorithm:\")\n", | |
" print(\"-\" * 40)\n", | |
" \n", | |
" for node, expected in expected_distances.items():\n", | |
" actual = actual_distances[node]\n", | |
" result = \"PASS\" if actual == expected else \"FAIL\"\n", | |
" print(f\"Node {node}: Expected={expected}, Actual={actual} - {result}\")\n", | |
" if actual != expected:\n", | |
" all_correct = False\n", | |
" \n", | |
" print(\"-\" * 40)\n", | |
" if all_correct:\n", | |
" print(\"All tests passed! The implementation is correct.\")\n", | |
" else:\n", | |
" print(\"Some tests failed. Check the implementation.\")\n", | |
"\n", | |
"# Run the test\n", | |
"if __name__ == \"__main__\":\n", | |
" test_dijkstra()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"id": "8b3557e8", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"No negative cycle found: True\n", | |
"Shortest distances from source vertex 's':\n", | |
"Vertex s: 0\n", | |
"Vertex t: 2\n", | |
"Vertex x: 4\n", | |
"Vertex y: 7\n", | |
"Vertex z: -2\n", | |
"✓ Distance to s is correct: 0\n", | |
"✓ Distance to t is correct: 2\n", | |
"✓ Distance to x is correct: 4\n", | |
"✓ Distance to y is correct: 7\n", | |
"✓ Distance to z is correct: -2\n", | |
"\n", | |
"All shortest distances match the expected values!\n" | |
] | |
} | |
], | |
"source": [ | |
"def bellman_ford(graph, source):\n", | |
" # Step 1: Initialize distances from source to all other vertices as INFINITY\n", | |
" # and distance to source as 0\n", | |
" distances = {vertex: float('inf') for vertex in graph}\n", | |
" distances[source] = 0\n", | |
" \n", | |
" # Step 2: Relax all edges |V| - 1 times\n", | |
" # A path can have at most |V| - 1 edges\n", | |
" for _ in range(len(graph) - 1):\n", | |
" for u in graph:\n", | |
" for v, weight in graph[u].items():\n", | |
" # If edge (u, v) can be relaxed, relax it\n", | |
" if distances[u] != float('inf') and distances[u] + weight < distances[v]:\n", | |
" distances[v] = distances[u] + weight\n", | |
" \n", | |
" # Step 3: Check for negative-weight cycles\n", | |
" # If we can relax further, then there is a negative-weight cycle\n", | |
" for u in graph:\n", | |
" for v, weight in graph[u].items():\n", | |
" if distances[u] != float('inf') and distances[u] + weight < distances[v]:\n", | |
" return False, distances # Negative cycle exists\n", | |
" \n", | |
" return True, distances # No negative cycles, return distances\n", | |
"\n", | |
"# Test with the graph from the image\n", | |
"def test_bellman_ford():\n", | |
" # Create the graph as an adjacency list with all edges and their weights\n", | |
" graph = {\n", | |
" 's': {'t': 6, 'y': 7},\n", | |
" 't': {'x': 5, 'y': 8, 'z': -4},\n", | |
" 'x': {'t': -2},\n", | |
" 'y': {'x': -3, 'z': 9},\n", | |
" 'z': {'s': 2, 'x': 7},\n", | |
" }\n", | |
" \n", | |
" # Add the additional edges from x to z and z to x that weren't initially included\n", | |
" graph['x']['z'] = -3\n", | |
" graph['z']['x'] = 7\n", | |
" \n", | |
" # Run Bellman-Ford algorithm with source vertex 's'\n", | |
" no_negative_cycle, distances = bellman_ford(graph, 's')\n", | |
" \n", | |
" print(\"No negative cycle found:\", no_negative_cycle)\n", | |
" print(\"Shortest distances from source vertex 's':\")\n", | |
" for vertex, distance in sorted(distances.items()):\n", | |
" print(f\"Vertex {vertex}: {distance}\")\n", | |
" \n", | |
" # Verify the expected shortest distances\n", | |
" expected_distances = {'s': 0, 't': 2, 'x': 4, 'y': 7, 'z': -2}\n", | |
" for vertex, expected_distance in sorted(expected_distances.items()):\n", | |
" if distances[vertex] == expected_distance:\n", | |
" print(f\"✓ Distance to {vertex} is correct: {distances[vertex]}\")\n", | |
" else:\n", | |
" print(f\"✗ Distance to {vertex} is incorrect. Expected: {expected_distance}, Got: {distances[vertex]}\")\n", | |
" \n", | |
" all_correct = all(distances[v] == expected_distances[v] for v in expected_distances)\n", | |
" if all_correct:\n", | |
" print(\"\\nAll shortest distances match the expected values!\")\n", | |
" else:\n", | |
" print(\"\\nSome distances don't match the expected values.\")\n", | |
" \n", | |
" return distances, expected_distances\n", | |
"\n", | |
"# Run the test\n", | |
"if __name__ == \"__main__\":\n", | |
" test_bellman_ford()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"id": "a923c011", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Initial Distance Matrix D(0):\n", | |
"[0.0, 3.0, 8.0, '∞', -4.0]\n", | |
"['∞', 0.0, '∞', 1.0, 7.0]\n", | |
"['∞', 4.0, 0.0, '∞', '∞']\n", | |
"[2.0, '∞', -5.0, 0.0, '∞']\n", | |
"['∞', '∞', '∞', 6.0, 0.0]\n", | |
"\n", | |
"Initial Predecessor Matrix Pi(0):\n", | |
"['NIL', 1, 1, 'NIL', 1]\n", | |
"['NIL', 'NIL', 'NIL', 2, 2]\n", | |
"['NIL', 3, 'NIL', 'NIL', 'NIL']\n", | |
"[4, 'NIL', 4, 'NIL', 'NIL']\n", | |
"['NIL', 'NIL', 'NIL', 5, 'NIL']\n", | |
"\n", | |
"Final Distance Matrix D(5):\n", | |
"[0.0, 1.0, -3.0, 2.0, -4.0]\n", | |
"[3.0, 0.0, -4.0, 1.0, -1.0]\n", | |
"[7.0, 4.0, 0.0, 5.0, 3.0]\n", | |
"[2.0, -1.0, -5.0, 0.0, -2.0]\n", | |
"[8.0, 5.0, 1.0, 6.0, 0.0]\n", | |
"\n", | |
"Final Predecessor Matrix Pi(5):\n", | |
"['NIL', 3, 4, 5, 1]\n", | |
"[4, 'NIL', 4, 2, 1]\n", | |
"[4, 3, 'NIL', 2, 1]\n", | |
"[4, 3, 4, 'NIL', 1]\n", | |
"[4, 3, 4, 5, 'NIL']\n", | |
"\n", | |
"Expected D(5) from example:\n", | |
"[ 0 1 -3 2 -4]\n", | |
"[ 3 0 -4 1 -1]\n", | |
"[7 4 0 5 3]\n", | |
"[ 2 -1 -5 0 -2]\n", | |
"[8 5 1 6 0]\n", | |
"\n", | |
"Expected Pi(5) from example:\n", | |
"['NIL', 3, 4, 5, 1]\n", | |
"[4, 'NIL', 4, 2, 1]\n", | |
"[4, 3, 'NIL', 2, 1]\n", | |
"[4, 3, 4, 'NIL', 1]\n", | |
"[4, 3, 4, 5, 'NIL']\n", | |
"\n", | |
"Do our D(5) and the expected D(5) match? True\n", | |
"Do our Pi(5) and the expected Pi(5) match? True\n" | |
] | |
} | |
], | |
"source": [ | |
"import numpy as np\n", | |
"\n", | |
"def floyd_warshall_algorithm(graph):\n", | |
" n = len(graph)\n", | |
" \n", | |
" # Initialize distance and predecessor matrices\n", | |
" D = [graph.copy()]\n", | |
" \n", | |
" # Create initial predecessor matrix\n", | |
" # Using None to represent NIL during calculations\n", | |
" Pi = [[None for _ in range(n)] for _ in range(n)]\n", | |
" \n", | |
" # Using the values from Pi(0) in the example\n", | |
" Pi[0][1] = 1\n", | |
" Pi[0][2] = 1\n", | |
" Pi[0][4] = 1\n", | |
" Pi[1][3] = 2\n", | |
" Pi[1][4] = 2\n", | |
" Pi[2][1] = 3\n", | |
" Pi[3][0] = 4\n", | |
" Pi[3][2] = 4\n", | |
" Pi[4][3] = 5\n", | |
" \n", | |
" # Store initial Pi matrix\n", | |
" Pi_matrices = [np.array(Pi, dtype=object)]\n", | |
" \n", | |
" # Main Floyd-Warshall algorithm\n", | |
" for k in range(n):\n", | |
" # Create new distance matrix for this iteration\n", | |
" current_D = D[k].copy()\n", | |
" current_Pi = Pi_matrices[k].copy()\n", | |
" \n", | |
" for i in range(n):\n", | |
" for j in range(n):\n", | |
" if current_D[i][k] != float('inf') and current_D[k][j] != float('inf'):\n", | |
" if current_D[i][j] > current_D[i][k] + current_D[k][j]:\n", | |
" current_D[i][j] = current_D[i][k] + current_D[k][j]\n", | |
" current_Pi[i][j] = current_Pi[k][j]\n", | |
" \n", | |
" D.append(current_D)\n", | |
" Pi_matrices.append(current_Pi)\n", | |
" \n", | |
" return D, Pi_matrices\n", | |
"\n", | |
"# Initial distance matrix D(0) from the example\n", | |
"D0 = np.array([\n", | |
" [0, 3, 8, float('inf'), -4],\n", | |
" [float('inf'), 0, float('inf'), 1, 7],\n", | |
" [float('inf'), 4, 0, float('inf'), float('inf')],\n", | |
" [2, float('inf'), -5, 0, float('inf')],\n", | |
" [float('inf'), float('inf'), float('inf'), 6, 0]\n", | |
"])\n", | |
"\n", | |
"# Run the algorithm\n", | |
"D_matrices, Pi_matrices = floyd_warshall_algorithm(D0)\n", | |
"\n", | |
"# Print initial and final matrices\n", | |
"print(\"Initial Distance Matrix D(0):\")\n", | |
"for row in D_matrices[0]:\n", | |
" print([x if x != float('inf') else \"∞\" for x in row])\n", | |
"\n", | |
"print(\"\\nInitial Predecessor Matrix Pi(0):\")\n", | |
"for row in Pi_matrices[0]:\n", | |
" print([\"NIL\" if x is None else x for x in row])\n", | |
"\n", | |
"print(\"\\nFinal Distance Matrix D(5):\")\n", | |
"for row in D_matrices[5]:\n", | |
" print([x if x != float('inf') else \"∞\" for x in row])\n", | |
"\n", | |
"print(\"\\nFinal Predecessor Matrix Pi(5):\")\n", | |
"for row in Pi_matrices[5]:\n", | |
" print([\"NIL\" if x is None else x for x in row])\n", | |
"\n", | |
"# Expected matrices from the example\n", | |
"D5_expected = np.array([\n", | |
" [0, 1, -3, 2, -4],\n", | |
" [3, 0, -4, 1, -1],\n", | |
" [7, 4, 0, 5, 3],\n", | |
" [2, -1, -5, 0, -2],\n", | |
" [8, 5, 1, 6, 0]\n", | |
"])\n", | |
"\n", | |
"Pi5_expected = [\n", | |
" [None, 3, 4, 5, 1],\n", | |
" [4, None, 4, 2, 1],\n", | |
" [4, 3, None, 2, 1],\n", | |
" [4, 3, 4, None, 1],\n", | |
" [4, 3, 4, 5, None]\n", | |
"]\n", | |
"\n", | |
"print(\"\\nExpected D(5) from example:\")\n", | |
"for row in D5_expected:\n", | |
" print(row)\n", | |
"\n", | |
"print(\"\\nExpected Pi(5) from example:\")\n", | |
"for row in Pi5_expected:\n", | |
" print([\"NIL\" if x is None else x for x in row])\n", | |
"\n", | |
"# Check if our final matrices match the expected ones\n", | |
"D_matches = np.array_equal(D_matrices[5], D5_expected)\n", | |
"print(\"\\nDo our D(5) and the expected D(5) match?\", D_matches)\n", | |
"\n", | |
"# Verify Pi matrix manually\n", | |
"Pi_matches = True\n", | |
"for i in range(len(Pi_matrices[5])):\n", | |
" for j in range(len(Pi_matrices[5][0])):\n", | |
" if Pi_matrices[5][i][j] != Pi5_expected[i][j]:\n", | |
" Pi_matches = False\n", | |
" print(f\"Mismatch at Pi[{i}][{j}]: Calculated={Pi_matrices[5][i][j]}, Expected={Pi5_expected[i][j]}\")\n", | |
"\n", | |
"print(\"Do our Pi(5) and the expected Pi(5) match?\", Pi_matches)\n", | |
"\n", | |
"if not D_matches:\n", | |
" print(\"\\nDifferences in D:\")\n", | |
" diff = D_matrices[5] - D5_expected\n", | |
" for i in range(len(diff)):\n", | |
" for j in range(len(diff[0])):\n", | |
" if diff[i][j] != 0:\n", | |
" print(f\"Position [{i},{j}]: Calculated={D_matrices[5][i][j]}, Expected={D5_expected[i][j]}\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "d6c1c5bc", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "m1", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.12.4" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment