A*算法详尽的入门教程

A*算法详尽的入门教程

一: 为什么我们需要A*算法

求最短路径或者最小代价的算法有很多。其本质就是图的搜索策略。图的直接搜索方法有很多种,比较典型的是广度优先搜索、深度优先搜索。所谓的广度优先搜索是每到达一个节点就优先遍历该节点的所有相邻节点。而对应的深度优先搜索是指一直延伸到从未达到过的节点。基于以上两种基本思想的最短路径算法有Dijkstra算法和Floyd算法。当搜索完毕也遍历了整张图,其时间开销是很大的,尤其是在图非常大的时候这种时间复杂度是不能接受的。上述算法之所以时间开销大是因为在搜索的过程并没有对当前状态进行评估,因此是基于穷搜的。另一种思路就是在搜索的过程中利用一些合适的评估函数来进行剪枝,首先去除那些不可能产生最优解的分支。这就是所谓的启发式搜索。而A*算法就是基于这种思想的搜索算法。

二: 什么是A*算法

A*算法是一个可采纳的最好优先算法(BFS)。

A*算法的估价函数可表示为:

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

这里,f‘(n)是估价函数,g‘(n)是起点到节点n的最短路径值,h‘(n)是n到目标的最短路经的启发值。由于这个f‘(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g‘(n),但 g(n)>=g‘(n)才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h‘(n),但h(n)<=h‘(n)才可(这一点特别的重要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的最好优先算法就是A*算法。

举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯定小于h‘(n),所以由前述可知广度优先算法是一种可采纳的。实际也是。当然它是一种最坏的A*算法。

三: A*算法的过程

上节简单介绍了A*算法的基本原理。那么对于一个具体的问题怎么用A*算法呢?下面给出A*算法的执行过程。

1.   初始化

将开始节点放入开放列表(开始节点的F和G值都视为0);

2.   重复以下过程

①     :在开放列表中查找具有最小F值的节点,并把查找到的节点作为当前节点;

②     :把当前节点从开放列表删除,加入到封闭列表;

③     :对当前节点相邻的每一个节点依次执行以下步骤:

1.如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点;

2.如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;

3. 如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值.

④     循环结束条件:

当终点节点被加入到开放列表作为待检验节点时, 表示路径被找到,此时应终止循环;

或者当开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环;

3.打印路径

从终点节点开始沿父节点遍历,
并保存整个遍历到的节点坐标,遍历所得的节点就是最后得到的路径;

四: A*算法的伪代码

while(OPEN!=NULL)
{
从OPEN表中取估价值f(n)最小的节点n;
if(n节点==目标节点)
break;
for(当前节点n的每个子节点X)
{
算X的估价值;
if(XinOPEN)
if(X的估价值小于OPEN表的估价值)
{
把n设置为X的父亲;
更新OPEN表中的估价值;//取最小路径的估价值
}
if(XinCLOSE)
continue;
if(Xnotinboth)
{
把n设置为X的父亲;
求X的估价值;
并将X插入OPEN表中;//还没有排序
}
}//endfor
将n节点插入CLOSE表中;
按照估价值将OPEN表中的节点排序;//实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。
}//endwhile(OPEN!=NULL)

五: A*算法的一个小例子

为了更形象的了解A*算法,我们举一个简单的求两点间的最短距离来了解A*算法的执行过程。

问题如上图,求从S->T的最短路径。A*算法的执行过程如下:

Step1:

Step2:

 

 

Step3:

Step4:

Step5:

Step6:

 

因为T是目标节点, 所以我们得到解:

  S->V1 ->V4 ->T

 

六: 利用A*算法解决八数码问题

1.  问题描述

八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。

所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。

八数码问题一般使用搜索法来解。搜索法有广度优先搜索法、深度优先搜索法、A*算法等。

下面用一张图即可表明八数码问题。

  

初始态                过渡态               终态

2.  A*算法解决八数码问题

用A*算法解决八数码问题的核心是找出代价函数。

F(n) =g(n) + h(n)

f为实际路径(估价);

g为已经走过的状态数;

h为由状态n到达最终状态的困难程度(这里是与最终状态不同的个数)。

注意,这里的h为状态n与最终状态不同的个数,这也仅仅对于八数码问题合适,对于15数码和更多的问题就不适合了,需要寻找更好的估价了。

这里用一维数组分别代表从上至下,从左至右的排列顺序来代表某一状态。

有了这个以后我们就可以遵循A*算法的步骤来处理八数码问题了。

3.   算法的实现

Ⅰ 实验环境

硬件环境:PC

开发环境:VisualStudio 2013

开发语言:C++

Ⅱ 算法流程图

Ⅲ 数据结构

ⅰ 状态图的状态节点:

struct Node

{

int State[9];//该节点的状态

int Parent;//状态图中该节点的父节点

int h;//节点到达目的的困难程度

int g;//节点的实际路径

int f;//节点的总路径

};

保存当前状态数组:

static int End[9]; //目标状态

static int Start[9]; //初始状态

static int Invalid[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //无效的状态

ⅲ CLOSE和OPEN表

static Node OPEN[500];

static Node CLOSE[500];

Ⅳ 实验结果

测试数据:

                                                         
                                                             

初始状态                                                                                    最终状态

共经历了4步,运行截图如下:

测试数据的手算算法过程如下:

可以看到和我们得到的结果是一样的,说明这个程序是没问题的。

Ⅴ 程序源代码

http://download.csdn.net/detail/simon_world/8303877

时间: 2024-10-12 21:33:52

A*算法详尽的入门教程的相关文章

STL 算法部分 原创入门教程,要详细资料请百度

STL算法部分 库为  Algorithm 算法库  Functional函数式编程       Numeric  基础性的数值算法 一一:find 查找类有13个函数,详情请百度. 使用方法,s.find(“a”):s就是一个数据类型,比方说set 集合,就是在一个集合中找到第一个和a一样的 字符,然后返回他的迭代器, 迭代器可以大概理解为指针. 用法是  queue<int> : :  iterator it; 因为大多数函数返回的都是it 类型的指针,所有必须认真掌握. 二:排序和通用算

【转载】GBDT(MART) 迭代决策树入门教程 | 简介

      转载地址:http://blog.csdn.net/w28971023/article/details/8240756        GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论累加起来做最终答案.它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法.近些年更因为被用于搜索排

转:GBDT(MART) 迭代决策树入门教程 | 简介

在网上看到一篇对从代码层面理解gbdt比较好的文章,转载记录一下: GBDT(Gradient Boosting Decision Tree) 又 叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论 累加起来做最终答案.它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法.近些年更因为被用于搜索排序的机器学习模型而引起大家关注. 后记:发现GBDT除了我描述的残差版本外

Android基础入门教程——8.3.4 Paint API之—— Xfermode与PorterDuff详解(一)

Android基础入门教程--8.3.4 Paint API之-- Xfermode与PorterDuff详解(一) 标签(空格分隔): Android基础入门教程 本节引言: 不知道标题这两个玩意你熟不熟悉啦,如果自己实现过圆角或者圆形图片,相信对这两个名词 并不模式,一时半伙没想起来?没关系,下面这个图你可曾见过? PS:网上都说在:\samples\android-XX\legacy\ApiDemos\src\com\example\android\apis\graphics 下能找到这个

Android基础入门教程——8.1.1 Android中的13种Drawable小结 Part 1

Android基础入门教程--8.1.1 Android中的13种Drawable小结 Part 1 标签(空格分隔): Android基础入门教程 本节引言: 从本节开始我们来学习Android中绘图与动画中的一些基础知识,为我们进阶部分的自定义 打下基础!而第一节我们来扣下Android中的Drawable!Android中给我们提供了多达13种的 Drawable,本节我们就来一个个撸一遍! Drawable资源使用注意事项 Drawable分为两种: 一种是我们普通的图片资源,在Andr

【OpenCV入门教程之十七】OpenCV重映射 &amp; SURF特征点检测合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 本篇文章中,我们一起探讨了OpenCV中

Spring Cloud 入门教程(五): Ribbon实现客户端的负载均衡

接上节,假如我们的Hello world服务的访问量剧增,用一个服务已经无法承载, 我们可以把Hello World服务做成一个集群. 很简单,我们只需要复制Hello world服务,同时将原来的端口8762修改为8763.然后启动这两个Spring Boot应用, 就可以得到两个Hello World服务.这两个Hello world都注册到了eureka服务中心.这时候再访问http://localhost:8761, 可以看到两个hello world服务已经注册.(服务与注册参见Spr

系列文章 -- OpenCV入门教程

<OpenCV3编程入门>内容简介&勘误&配套源代码下载 [OpenCV入门教程之十八]OpenCV仿射变换 & SURF特征点描述合辑 [OpenCV入门教程之十七]OpenCV重映射 & SURF特征点检测合辑 [OpenCV入门教程之十六]OpenCV角点检测之Harris角点检测 [OpenCV入门教程之十五]水漫金山:OpenCV漫水填充算法(Floodfill) [OpenCV入门教程之十四]OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑 [Ope

迷你MVVM框架 avalonjs 入门教程(司徒正美)

迷你MVVM框架 avalonjs 入门教程 关于AvalonJs 开始的例子 扫描 视图模型 数据模型 绑定属性与动态模板 作用域绑定(ms-controller, ms-important) 模板绑定(ms-include) 数据填充(ms-text, ms-html) 类名切换(ms-class, ms-hover, ms-active) 事件绑定(ms-on,……) 显示绑定(ms-visible) 插入绑定(ms-if) 双工绑定(ms-duplex) 样式绑定(ms-css) 数据绑