A 搜索算法
外观
A*搜索算法(A-star search algorithm)是一种广泛使用的路径规划和图搜索算法,它结合了Dijkstra算法的完备性和贪心最佳优先搜索的高效性。A*算法通过启发式函数(heuristic function)来估计从当前节点到目标节点的成本,从而智能地选择最有希望的路径进行扩展。
算法概述[编辑 | 编辑源代码]
A*算法属于启发式搜索算法,它在搜索过程中维护一个优先队列(通常称为开放列表),按照评估函数f(n) = g(n) + h(n)的值对节点进行排序:
- g(n):从起点到当前节点n的实际路径成本。
- h(n):从当前节点n到目标节点的启发式估计成本(必须满足可采纳性,即不高估实际成本)。
- f(n):节点的总评估成本,用于决定扩展顺序。
算法特性[编辑 | 编辑源代码]
- 完备性:如果解存在,A*算法总能找到解。
- 最优性:当启发式函数h(n)可采纳(admissible)且一致(consistent)时,A*算法能找到最优解。
- 效率:相比Dijkstra算法,A*通常能显著减少搜索空间。
算法步骤[编辑 | 编辑源代码]
以下是A*算法的伪代码实现:
def A_star_search(start, goal, heuristic):
open_set = PriorityQueue()
open_set.put(start, 0)
came_from = {} # 记录路径
g_score = {node: float('inf') for node in graph}
g_score[start] = 0
f_score = {node: float('inf') for node in graph}
f_score[start] = heuristic(start, goal)
while not open_set.empty():
current = open_set.get()
if current == goal:
return reconstruct_path(came_from, current)
for neighbor in graph.neighbors(current):
tentative_g_score = g_score[current] + graph.cost(current, neighbor)
if tentative_g_score < g_score[neighbor]:
came_from[neighbor] = current
g_score[neighbor] = tentative_g_score
f_score[neighbor] = g_score[neighbor] + heuristic(neighbor, goal)
if neighbor not in open_set:
open_set.put(neighbor, f_score[neighbor])
return None # 无路径存在
启发式函数设计[编辑 | 编辑源代码]
启发式函数h(n)的设计直接影响A*算法的效率:
- 曼哈顿距离(适用于网格移动):
- 欧几里得距离:
- 对角线距离(Chebyshev距离):
可采纳性验证[编辑 | 编辑源代码]
启发式函数必须满足:(其中是真实成本),否则可能无法找到最优解。
示例:网格路径规划[编辑 | 编辑源代码]
假设在一个4x4网格中寻找从(0,0)到(3,3)的路径,障碍物位于(1,1)和(2,2)。使用曼哈顿距离作为启发式函数:
搜索过程关键步骤: 1. 扩展(0,0): f=0+6=6 2. 扩展(1,0): f=1+5=6 3. 扩展(0,1): f=1+5=6 4. 扩展(2,0): f=2+4=6 5. 最终路径:(0,0)→(1,0)→(2,0)→(3,0)→(3,1)→(3,2)→(3,3)
实际应用[编辑 | 编辑源代码]
A*算法在以下领域有广泛应用:
- 游戏开发:NPC寻路(如《星际争霸》等RTS游戏)
- 机器人导航:无人车/无人机路径规划
- 网络路由:数据包传输路径优化
- 拼图求解:8数码问题等
变体与优化[编辑 | 编辑源代码]
- 双向A*:同时从起点和目标点开始搜索
- 迭代加深A* (IDA*):内存优化版本
- 动态加权A*:动态调整启发式权重
- 跳点搜索:针对均匀网格的优化
复杂度分析[编辑 | 编辑源代码]
- 时间复杂度:(其中b是分支因子,d是解深度)
- 空间复杂度:(需存储所有生成的节点)
常见问题[编辑 | 编辑源代码]
如何避免重复节点处理?[编辑 | 编辑源代码]
使用闭集合(closed set)记录已处理的节点,但需注意在发现更优路径时重新开放节点。
启发式函数不一致会怎样?[编辑 | 编辑源代码]
可能导致重复处理节点,此时需要允许重新开放闭集合中的节点(如使用动态A*)。
代码实现示例[编辑 | 编辑源代码]
以下是Python完整实现(针对网格地图):
import heapq
def heuristic(a, b):
return abs(a[0] - b[0]) + abs(a[1] - b[1])
def a_star(grid, start, goal):
neighbors = [(0,1),(1,0),(0,-1),(-1,0)] # 4方向移动
close_set = set()
came_from = {}
gscore = {start:0}
fscore = {start:heuristic(start, goal)}
open_set = []
heapq.heappush(open_set, (fscore[start], start))
while open_set:
current = heapq.heappop(open_set)[1]
if current == goal:
path = []
while current in came_from:
path.append(current)
current = came_from[current]
return path[::-1]
close_set.add(current)
for i, j in neighbors:
neighbor = current[0] + i, current[1] + j
if 0 <= neighbor[0] < len(grid) and 0 <= neighbor[1] < len(grid[0]):
if grid[neighbor[0]][neighbor[1]] == 1: # 1表示障碍物
continue
tentative_g_score = gscore[current] + 1
if neighbor in close_set and tentative_g_score >= gscore.get(neighbor, float('inf')):
continue
if tentative_g_score < gscore.get(neighbor, float('inf')) or neighbor not in [i[1] for i in open_set]:
came_from[neighbor] = current
gscore[neighbor] = tentative_g_score
fscore[neighbor] = tentative_g_score + heuristic(neighbor, goal)
heapq.heappush(open_set, (fscore[neighbor], neighbor))
return None
# 示例使用
grid = [[0,0,0,0], [0,1,0,1], [0,1,0,0], [0,0,0,0]]
print(a_star(grid, (0,0), (3,3))) # 输出:[(0,1),(0,2),(1,2),(2,2),(3,2),(3,3)]
扩展阅读[编辑 | 编辑源代码]
- 对比其他搜索算法:Dijkstra、GBFS、BFS
- 进阶主题:任意时间A*(ARA*)、终身规划A*(D* Lite)
- 三维空间中的A*应用