參考:
https://www.geeksforgeeks.org/a-search-algorithm/
https://www.101computing.net/a-star-search-algorithm/
一. 概述:
A*算法是一種包含了啟發的Djkstra算法,可以用來求帶權值的圖的最短路徑。
A*算法比起Djkstra算法,在尋找最短路徑的問題上更加有效率。

算法中,引入了距離作為啟發,通過終點和節點的距離計算,選擇迭代的點。
通常求距離有以下幾種方案:
1. 曼哈頓距離
h = abs (current_cell.x – goal.x) +
abs (current_cell.y – goal.y)

2. 對角線距離
h = max { abs(current_cell.x – goal.x),
abs(current_cell.y – goal.y) }

3. 歐氏距離
h = sqrt ( (current_cell.x – goal.x)2 +
(current_cell.y – goal.y)2 )

二. 示例:
以下示例介紹運用A*算法的詳細步驟,假設圖如下,求A到Z的最短路徑,各節點與Z的距離已經求得,下圖中標為橙色。
第一步:
初始化數據表如下,初始化每個節點到A的最短距離為無窮大

第二步:
以A為當前點,求出以A為起點的兩條路徑,並更新數據表。更新Total Distance為已計算的最短距離 + 與Z的啟發距離

第三步:
由於C的Total Distance小於b,從c開始計算並更新e和d的距離

第四步:
B和D的Total Distance一樣,我們從B開始迭代,進一步更新數據表

第五步:

第六步:

第七步:
迭代到Z,獲得最小距離。最小路徑可以從Z的前一個節點向前追溯,得到路徑A-C-D-E。
最后再遍歷沒有訪問的節點F,由於F的Total Distance > 17, 則不可能是最短路徑節點。

三. Python代碼實現:
1 #!/usr/bin/python3 2 # -*- coding: utf-8 -*- 3 # @author: Asp1rant 4 5 6 # 存放中間數據的數據結構 7 class PriorityData: 8 visited = False 9 shortest_distance_start = 0 10 heuristic_distance_end = 0 11 total_distance = 0 12 previous_node = '' 13 14 def __init__(self, heuristic_value): 15 self.heuristic_distance_end = heuristic_value 16 self.shortest_distance_start = 9999 17 self.total_distance = 9999 18 19 20 def a_star(graph, heuristic_value, start, end): 21 priority_dic = {} 22 for k in graph.keys(): 23 priority_dic[k] = PriorityData(heuristic_value[k]) 24 priority_dic[start].shortest_distance_start = 0 25 26 # 判斷終點是否訪問 27 def isEndVisited(): 28 ret = False 29 if priority_dic[end].visited: 30 distance = priority_dic[end].total_distance 31 ret = True 32 return ret 33 34 # 更新節點數據表 35 def updateNode(node): 36 neighbors = graph[node] 37 for k in neighbors.keys(): 38 neighbor_data = priority_dic[k] 39 if priority_dic[node].shortest_distance_start + neighbors[k] < neighbor_data.shortest_distance_start: 40 neighbor_data.shortest_distance_start = priority_dic[node].shortest_distance_start + neighbors[k] 41 neighbor_data.total_distance = neighbor_data.shortest_distance_start + neighbor_data.heuristic_distance_end 42 neighbor_data.previous_node = node 43 priority_dic[node].visited = True 44 45 find_point = start 46 while not isEndVisited(): 47 updateNode(find_point) 48 shortest_distance = 9999 49 for k in priority_dic.keys(): 50 if not priority_dic[k].visited and priority_dic[k].total_distance < shortest_distance: 51 find_point = k 52 shortest_distance = priority_dic[k].total_distance 53 54 path_node_list = [end] 55 result_distance = priority_dic[end].total_distance 56 previous_node = priority_dic[end].previous_node 57 while previous_node != start: 58 path_node_list.append(previous_node) 59 previous_node = priority_dic[previous_node].previous_node 60 path_node_list.append(start) 61 path = path_node_list[len(path_node_list) - 1] 62 for i in range(len(path_node_list)-2, -1, -1): 63 path += "-" 64 path += path_node_list[i] 65 return (path, result_distance) 66 67 68 if __name__ == '__main__': 69 # 用於測試的圖 70 graph = { 71 "A": {"B": 4, "C": 3}, 72 "B": {"E": 12, "F": 5}, 73 "C": {"E": 10, "D": 7}, 74 "D": {"E": 2}, 75 "E": {"Z": 5}, 76 "F": {"Z": 16}, 77 "Z": {} 78 } 79 heuristic_value = {"A": 14, "B": 12, "C": 11, "D": 6, "E": 4, "F": 11, "Z": 0} 80 result = a_star(graph, heuristic_value, "A", "Z") 81 print(result)
結果:
('A-C-D-E-Z', 17)
