A*(A-Star)算法解析(附代码理解)

A*(A-Star)算法是一种广泛使用的启发式搜索算法,用于在图形平面或网络中找到从起点到终点的最短路径。它结合了Best-First Search算法和Dijkstra算法的特点,通过使用启发函数来指导搜索过程,从而提高搜索效率。

1. 核心概念的详细解释

节点(Node)

在图论中,节点代表图中的每一个顶点,可以是地图上的一个点、网络中的一个路由器或者任何可以表示为图中一个独立元素的实体。在A*算法中,节点通常代表可能的路径点,例如在地图上从一个城市到另一个城市可能的路线点。

边(Edge)

边是连接两个节点的线,在A*算法中,边代表从一个节点到另一个节点的路径。边通常具有权重或代价,表示通过这条边移动所需的成本或时间。

启发式函数(Heuristic Function)

启发式函数是一个用于估计从当前节点到目标节点的代价的函数。它不计算实际的代价,而是提供一个乐观的估计,帮助算法更快地收敛到目标。启发式函数的选择对算法的性能有很大影响。常见的启发式函数包括:

  • 曼哈顿距离(Manhattan Distance)

曼哈顿距离,又称城市街区距离,是在网格地图中计算两点间距离的一种方法。它仅考虑水平和垂直方向上的距离之和,忽略了对角线方向,因此特别适合网格布局,如城市街道网格。计算公式为:

d=\left \| x_{1}-x_{2} \right \|+\left \| y_{1}-y_{2} \right \|

这里x1y1, x2y2分别是两个点的坐标。

  • 欧几里得距离(Euclidean Distance)

欧几里得距离是两点之间的直线距离,是最直观的距离度量方法。它基于直角坐标系中的勾股定理,适用于连续空间中两点之间的距离计算。计算公式为:

d=\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}

对于更高维的空间,欧几里得距离可以扩展到:

d=\sqrt{\sum_{i=1}^{n}(x_{i}-y_{i})^{2}}

  • 切比雪夫距离(Chebyshev Distance)

切比雪夫距离,也被称为国际象棋中的国王移动距离,它定义为两个点坐标差的最大绝对值。在二维空间中,计算公式为:

d=max(\left | x_{1}-x_{2} \right |,\left | y_{1}-y_{2} \right |)

切比雪夫距离在网格布局中很有用,尤其是在国际象棋等游戏中,因为国王可以向任何相邻的8个方向移动,而距离计算只考虑最大的水平或垂直移动量。

 

2. 算法原理的深入分析

A*算法的核心在于评估每个节点的代价,这涉及到两个主要的值:

g(n)代表从起点到当前节点n的实际代价。这个值是算法在搜索过程中逐步累积的,表示已经走过的路径的代价。

h(n)是从当前节点n到目标节点的估计代价,由启发式函数计算得出。这个值是基于启发式函数的估计,不需要实际走过路径。

f(n)=g(n)+h(n)

f(n)是节点n的总代价,由g(n)h(n)相加得出。f(n)值越小,表示从起点到当前节点,再加上到目标点的估计代价越低,这个节点就越有可能是最短路径的一部分。

3. 算法步骤的详细阐述

初始化

在算法开始之前,需要初始化两个列表:

  • 开放列表(Open List):用于存储待评估的节点,即算法接下来将要探索的节点。
  • 关闭列表(Closed List):用于存储已评估的节点,即算法已经探索过的节点。
起始节点加入开放列表

算法开始时,将起始节点加入开放列表,并计算其f(n)值。这个值由g(n)h(n)相加得出,其中g(n)为0,因为起始节点没有走过任何路径。

循环探索

算法的核心是一个循环,只要开放列表不为空,就执行以下步骤:

  1. 选择节点:从开放列表中选择具有最低f(n)值的节点。这个节点被认为是当前最有希望的候选节点。
  2. 检查目标:如果选择的节点是目标节点,算法结束,找到了最短路径。
  3. 移动到关闭列表:如果选择的节点不是目标节点,将其从开放列表移动到关闭列表,表示这个节点已经被探索过。
  4. 探索邻居节点:检查该节点的所有邻居节点:
    • 如果邻居节点已经在关闭列表中,说明这个节点已经被探索过,可以忽略。
    • 如果邻居节点不在开放列表或关闭列表中,计算其g(n)f(n)值,并将这个邻居节点加入开放列表。
    • 如果邻居节点已在开放列表中,但是通过当前节点到达邻居节点的路径更短,更新其g(n)值和f(n)值,并更新其父节点,以记录最短路径。

这个过程会不断重复,直到找到目标节点或者开放列表为空(表示没有可行的路径)。

 

4. 算法特点的深入讨论

A*算法是最优的,因为它总是选择当前看起来最佳的路径。这意味着算法会找到从起点到终点的最短路径,前提是启发式函数是可接受的(admissible),即它不会高估实际的代价。算法是完备的,只要存在一条从起点到终点的路径,算法就能找到它。这使得A算法在确定性问题中非常可靠。A*算法的效率受到启发式函数选择的影响。一个好的启发式函数可以显著提高搜索速度,因为它可以更快地引导算法向目标节点靠近。

5. 算法的应用场景

A*算法的应用非常广泛,包括但不限于:

  • 游戏开发:在游戏AI中,A*算法常用于角色寻路,确保角色能够智能地避开障碍物,找到到达目的地的最短路径。
  • 自动驾驶汽车:在自动驾驶领域,A*算法可以用于路径规划,帮助汽车在复杂的交通环境中找到最佳行驶路线。
  • 机器人导航:在机器人领域,A*算法可以帮助机器人在未知环境中进行路径规划,避开障碍物,达到指定位置。
#include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <functional> // 包含函数对象

// 定义网格点的结构
struct Node {
    int x, y;
    double g, h, f;
    Node* parent;

    Node(int x_, int y_) : x(x_), y(y_), g(0), h(0), f(0), parent(nullptr) {}

    // 重载<运算符
    bool operator<(const Node& other) const {
        return f > other.f; // 小顶堆,取出f值最小的元素
    }
};

// 启发式函数,计算曼哈顿距离
double heuristic(const Node& a, const Node& b) {
    return std::abs(a.x - b.x) + std::abs(a.y - b.y);
}

// A* 搜索算法
std::vector<Node> astar(std::vector<std::vector<int>>& grid, int startX, int startY, int endX, int endY) {
    std::priority_queue<Node, std::vector<Node>, std::greater<Node>> openSet;
    Node start(startX, startY);
    Node end(endX, endY);

    std::map<Node, double> gScore;
    std::map<Node, double> fScore;
    std::map<Node, Node> cameFrom;

    openSet.push(start);
    gScore[start] = 0;
    fScore[start] = heuristic(start, end);

    while (!openSet.empty()) {
        Node current = openSet.top();
        openSet.pop();

        if (current.x == end.x && current.y == end.y) {
            // 找到终点,重建路径
            std::vector<Node> path;
            for (Node at = current; at.parent != nullptr; at = *at.parent) {
                path.push_back(at);
            }
            std::reverse(path.begin(), path.end()); // 反转路径
            return path;
        }

        for (int dx = -1; dx <= 1; ++dx) {
            for (int dy = -1; dy <= 1; ++dy) {
                if (dx == 0 && dy == 0) continue;

                Node next(current.x + dx, current.y + dy);
                if (next.x < 0 || next.x >= grid[0].size() || next.y < 0 || next.y >= grid.size() || grid[next.y][next.x] == 1) {
                    continue;
                }

                double tentative_gScore = gScore[current] + 1.0;
                if (!gScore.count(next) || tentative_gScore < gScore[next]) {
                    cameFrom[next] = current;
                    gScore[next] = tentative_gScore;
                    fScore[next] = gScore[next] + heuristic(next, end);
                    openSet.push(next);
                }
            }
        }
    }

    return std::vector<Node>(); // 没有找到路径
}

int main() {
    std::vector<std::vector<int>> grid = {
        {0, 1, 0, 0, 0, 0},
        {0, 1, 0, 1, 1, 0},
        {0, 0, 0, 0, 1, 0},
        {0, 1, 1, 0, 1, 0},
        {0, 0, 0, 0, 0, 0}
    };

    std::vector<Node> path = astar(grid, 0, 0, 4, 5);

    for (const Node& n : path) {
        std::cout << "(" << n.x << ", " << n.y << ") ";
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值