|
24 | 24 | 'topological_sort', |
25 | 25 | 'topological_sort_parallel', |
26 | 26 | 'max_flow' |
| 27 | + 'a_star_with_manhattan' |
27 | 28 | ] |
28 | 29 |
|
29 | 30 | Stack = Queue = deque |
@@ -700,6 +701,7 @@ def shortest_paths(graph: Graph, algorithm: str, |
700 | 701 | 'bellman_ford' -> Bellman-Ford algorithm as given in [1]. |
701 | 702 |
|
702 | 703 | 'dijkstra' -> Dijkstra algorithm as given in [2]. |
| 704 | + 'a_star_with_manhattan' -> A* algorithm with Manhattan distance |
703 | 705 | source: str |
704 | 706 | The name of the source the node. |
705 | 707 | target: str |
@@ -736,16 +738,27 @@ def shortest_paths(graph: Graph, algorithm: str, |
736 | 738 | ({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'}) |
737 | 739 | >>> shortest_paths(G, 'dijkstra', 'V1') |
738 | 740 | ({'V2': 11, 'V3': 21, 'V1': 0}, {'V1': None, 'V2': 'V1', 'V3': 'V2'}) |
| 741 | + >>> grid_graph = Graph(AdjacencyListGraphNode("0,0"), AdjacencyListGraphNode("1,1")) |
| 742 | + >>> grid_graph.add_edge('0,0', '1,1', 2) |
| 743 | + >>> shortest_paths(grid_graph, 'a_star_with_manhattan', '0,0', '1,1') |
| 744 | + (2, {'1,1': '0,0'}) |
739 | 745 |
|
740 | 746 | References |
741 | 747 | ========== |
742 | 748 |
|
743 | 749 | .. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm |
744 | 750 | .. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm |
| 751 | + .. [3] https://en.wikipedia.org/wiki/A*_search_algorithm |
745 | 752 | """ |
746 | 753 | raise_if_backend_is_not_python( |
747 | 754 | shortest_paths, kwargs.get('backend', Backend.PYTHON)) |
748 | 755 | import pydatastructs.graphs.algorithms as algorithms |
| 756 | + if algorithm == 'a_star_with_manhattan': |
| 757 | + if not target: |
| 758 | + raise ValueError("Target must be specified for A* algorithm") |
| 759 | + |
| 760 | + func = "_a_star_with_manhattan_adjacency_list" |
| 761 | + return getattr(algorithms, func)(graph, source, target) |
749 | 762 | func = "_" + algorithm + "_" + graph._impl |
750 | 763 | if not hasattr(algorithms, func): |
751 | 764 | raise NotImplementedError( |
@@ -811,6 +824,51 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str): |
811 | 824 |
|
812 | 825 | _dijkstra_adjacency_matrix = _dijkstra_adjacency_list |
813 | 826 |
|
| 827 | +def _a_star_with_manhattan_adjacency_list(graph: Graph, start: str, target: str, **kwargs): |
| 828 | + """ |
| 829 | + A* algorithm with Manhattan distance as the heuristic function for grid-based graphs. |
| 830 | + """ |
| 831 | + def manhattan_distance(node1: str, node2: str) -> float: |
| 832 | + try: |
| 833 | + x1, y1 = map(int, node1.split(",")) |
| 834 | + x2, y2 = map(int, node2.split(",")) |
| 835 | + return abs(x1 - x2) + abs(y1 - y2) |
| 836 | + except (ValueError, TypeError): |
| 837 | + raise ValueError(f"Invalid node format. Expected 'x,y', got {node1} or {node2}") |
| 838 | + if start == target: |
| 839 | + return 0, {start: None} |
| 840 | + if start not in graph.vertices or target not in graph.vertices: |
| 841 | + raise ValueError(f"Start or target node not in graph. Start: {start}, Target: {target}") |
| 842 | + g_score = {v: float('inf') for v in graph.vertices} |
| 843 | + f_score = {v: float('inf') for v in graph.vertices} |
| 844 | + pred = {v: None for v in graph.vertices} |
| 845 | + visited = {v: False for v in graph.vertices} |
| 846 | + g_score[start] = 0 |
| 847 | + f_score[start] = manhattan_distance(start, target) |
| 848 | + pq = PriorityQueue(implementation='binomial_heap') |
| 849 | + pq.push(start, f_score[start]) |
| 850 | + while not pq.is_empty: |
| 851 | + current = pq.pop() |
| 852 | + if current == target: |
| 853 | + return g_score[target], {target: start} |
| 854 | + visited[current] = True |
| 855 | + for neighbor in graph.neighbors(current): |
| 856 | + if visited[neighbor.name]: |
| 857 | + continue |
| 858 | + edge = graph.get_edge(current, neighbor.name) |
| 859 | + if not edge: |
| 860 | + continue |
| 861 | + tentative_g_score = g_score[current] + edge.value |
| 862 | + if tentative_g_score < g_score[neighbor.name]: |
| 863 | + pred[neighbor.name] = current |
| 864 | + g_score[neighbor.name] = tentative_g_score |
| 865 | + f_score[neighbor.name] = ( |
| 866 | + tentative_g_score + |
| 867 | + manhattan_distance(neighbor.name, target) |
| 868 | + ) |
| 869 | + pq.push(neighbor.name, f_score[neighbor.name]) |
| 870 | + raise ValueError(f"No path exists between {start} and {target}") |
| 871 | +_a_star_with_manhattan_adjacency_matrix = _a_star_with_manhattan_adjacency_list |
814 | 872 | def all_pair_shortest_paths(graph: Graph, algorithm: str, |
815 | 873 | **kwargs) -> tuple: |
816 | 874 | """ |
|
0 commit comments