A*算法

本文详细介绍A*算法原理及其在路径搜索中的应用。A*算法是一种高效直接搜索方法,结合实际成本g(n)与启发式估计h(n)来评估路径成本f(n),确保找到最优路径。文章还提供了一个具体示例及C++实现代码。
摘要由CSDN通过智能技术生成

一、算法简介

A* (A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是许多其他问题的常用启发式算法。是最有效的直接搜索算法,之后涌现了很多预处理算法(如ALT,CH,HL等等),在线查询效率是A*算法的数千甚至上万倍。
公式表示为: f(n)=g(n)+h(n),
其中, f(n) 是从初始状态经由状态n到目标状态的代价估计,
g(n) 是在状态空间中从初始状态到状态n的实际代价,
h(n) 是从状态n到目标状态的最佳路径的估计代价。
(对于路径搜索问题,状态就是图中的节点,代价就是距离)
h(n)的选取
保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取(或者说h(n)的选取)。
我们以d(n)表达状态n到目标状态的距离,那么h(n)的选取大致有如下三种情况:
如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

二、示例演示

这里写图片描述

三、程序编写分析

A*与Dijkstra算法的区别就是加了一个h函数,在OPEN表中取最小值时就不是取离起始点最近的值,而是取离起始点距离+h函数计算得到距离的最小值代表的节点,这样的话就可以朝着目标点进行搜索。
建立两个列表,然后建立一个struct节点,编写h函数,进行判断。

把起始格添加到 "开启列表"  
do  
{  
       寻找开启列表中F值最低的格子, 我们称它为当前格.  
       把它切换到关闭列表.  
       对当前格相邻的8格中的每一个  
          if (它不可通过 || 已经在 "关闭列表" 中)  
          {  
                什么也不做.  
           }  
          if (它不在开启列表中)  
          {  
                把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH  
          if (它已经在开启列表中)  
          {  
                if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径)  
                    {  
                            把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值.  
                    }  
} while( 目标格已经在 "开启列表", 这时候路径被找到)  
如果开启列表已经空了, 说明路径不存在. 

最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.

四、程序编写

#pragma once
#include<iostream>
#include<vector>
#include<algorithm>
#include<list>
#include<math.h>
using namespace std;

//创建地图
vector<vector<int>>maze = {
    { 0, 1, 0, 0, 0, 0 },
    { 0, 1, 0, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0 },
};
//创建节点的通用形式
struct Point{
    int x, y;
    float F, G, H;
    Point *parent;
    Point(int _x, int _y) :x(_x), y(_y), F(0), G(0), H(0), parent(NULL){}
};
Point startPoint(0, 0), endPoint(1, 4);

//实现升排列的谓词
bool cmp(Point *a, Point* b)
{
    return (a->F < b->F);
}

//是否在链表中
bool isInList(Point *p, list<Point*>list){
    for (auto p1 : list){
        if (p->x == p1->x && p->y == p1->y)
            return true;
    }
    return false;
}

//得到估价值F/G/H
void getFGH(Point &middle){
    if (middle.parent == NULL){
        middle.G = 0;
    }
    else middle.G = middle.parent->G + sqrt((double)(middle.parent->x - middle.x)*(middle.parent->x - middle.x) + (double)(middle.parent->y - middle.y)*(middle.parent->y - middle.y));
    middle.H = sqrt((double)(endPoint.x - middle.x)*(endPoint.x - middle.x) + (double)(endPoint.y - middle.y)*(endPoint.y - middle.y));
    middle.F = middle.G + middle.H;
}
//得到两点距离值,这是一个简单的函数
float getdis(Point &a, Point &b){ return (double)sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); }

//进行路径的寻找
int Astar(){
    list<Point*>openlist;
    list<Point*>closelist;
    //将第一个点放入openlist
    getFGH(startPoint);
    openlist.push_back(&startPoint);

    //创建一个指针类型的变量,作为中介接收取出的最小值点
    Point *temp = new Point(0, 0);
    while (!openlist.empty()){
        //找出最小F的点,list中自带有排序函数就不用algorithm
        openlist.sort(cmp);
        temp = openlist.front();
        openlist.pop_front();
        closelist.push_back(temp);
        for (int i = temp->x - 1; i <= temp->x + 1; i++){
            if (i<0 || i>5) continue;
            for (int j = temp->y - 1; j <= temp->y + 1; j++){
                Point* temp1 = new Point(i, j);
                //超出范围、不可通过、在关闭列表
                if (j<0 || j>5 || maze[i][j] == 1 || isInList(temp1, closelist)) continue;
                //不在开始列表,则应该添加进开始列表,同时要以此节点为父节点
                if (!isInList(temp1, openlist)){
                    temp1->parent = temp;
                    getFGH(*temp1);
                    openlist.push_back(temp1);
                }
                //在开始列表,判断G值是否会更短,会的话就要更新(这一步同Dijkstra)
                if (isInList(temp1, openlist)){
                    getFGH(*temp);
                    if (temp1->G > temp->G + getdis(*temp, *temp1)){
                        temp1->G = temp->G + getdis(*temp, *temp1);
                        temp1->F = temp1->G + temp1->H;
                        temp1->parent = temp;
                    }
                }
                //是否终点已经放入开始列表
                if (isInList(&endPoint, openlist)) {
                    endPoint = *temp1;
                    return 0;
                }
            }
        }
    }
}

void main(){

    Astar();
    //将各个节点依照前插的方式进行存储,那么输出就可以顺序输出了
    list<Point>path;
    path.push_front(endPoint);
    while (endPoint.parent != NULL){

        path.push_front(*(endPoint.parent));
        endPoint = (*(endPoint.parent));
    }
    for (auto p : path){
        cout << "( " << p.x << ", " << p.y << ") " << endl;
    }

    system("pause");
}

五、参考链接

http://www.cnblogs.com/wlzy/p/7096114.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值