跳转到内容

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*算法的效率:

  • 曼哈顿距离(适用于网格移动):h(n)=|x1x2|+|y1y2|
  • 欧几里得距离h(n)=(x1x2)2+(y1y2)2
  • 对角线距离(Chebyshev距离):h(n)=max(|x1x2|,|y1y2|)

可采纳性验证[编辑 | 编辑源代码]

启发式函数必须满足:h(n)h*(n)(其中h*(n)是真实成本),否则可能无法找到最优解。

示例:网格路径规划[编辑 | 编辑源代码]

假设在一个4x4网格中寻找从(0,0)到(3,3)的路径,障碍物位于(1,1)和(2,2)。使用曼哈顿距离作为启发式函数:

graph TD A((0,0)) -->|1| B((0,1)) A -->|1| D((1,0)) B -->|1| C((0,2)) B -->|1| E((1,1)):::obstacle D -->|1| E D -->|1| G((2,0)) C -->|1| F((0,3)) C -->|1| H((1,2)) G -->|1| I((3,0)) G -->|1| K((2,1)) H -->|1| L((2,2)):::obstacle H -->|1| M((1,3)) K -->|1| L K -->|1| N((3,1)) M -->|1| O((2,3)) N -->|1| P((3,2)) O -->|1| Q((3,3)) P -->|1| Q classDef obstacle fill:#f96;

搜索过程关键步骤: 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*:动态调整启发式权重
  • 跳点搜索:针对均匀网格的优化

复杂度分析[编辑 | 编辑源代码]

  • 时间复杂度:O(bd)(其中b是分支因子,d是解深度)
  • 空间复杂度:O(bd)(需存储所有生成的节点)

常见问题[编辑 | 编辑源代码]

如何避免重复节点处理?[编辑 | 编辑源代码]

使用闭集合(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*应用