A*启发式搜索基础
A*启发式搜索基础
传统的搜索方式是盲目搜索,即到每一步的时候并没有对每种情况进行有效的区分,这样的结果是浪费了大量的时间,对很多没有必要的数据进行了搜索。
而A*算法则在搜索的过程中会选取认为“最优”的状态进行搜索,而这正是这种算法的精华部分。
其实我们可以将他和Dijkstra进行一定的对比,他们的共同点很多,都是每次选取最优的点开始搜索,但是他们的“最优”策略不同也就决定了不同的效率。
- Dijkstra是每次选取距离“出发点”最近的点开始搜索
- A*算法则是选取距离“目标点”估计值最小的点开始搜索
注意这里的估计值就是A*算法的核心部分了,本博文最后会介绍和Dijkstra的具体效率的区别。
A*算法将点到重点的距离分为两个部分:
F(n)=G(n)+H(n)
介绍:
- F(n)表示起点到目标的估计距离
- G(n)表示起点到n点的准确距离
- H(n)表示n点到目标点的估计距离
我们先假设从n点到目标点的准确距离为H′(n),当然H′(n)的准确值我们并不知道,否则我们就不用费力气去求了。
这里H(n)需要满足的条件是
- H(n)<=H′(n)
需要说明的是,我们A*搜索的核心就是H(n)的构建,最后构建的好坏直接决定了我们搜索的效率,我们的好是指:H(n)越贴近H′(n)越好,但是注意一定不能超过实际H′(n)的值。
为了和大多数的讲解名字对应,本博文采用相同名字,即名openlist的优先队列来存放需要搜索的内容,具体搜索步骤如下
- 预定义H(n)=|beginx−endx|+|beginy−endy|
- 预定义F(n)=G(n)+H(n)
openlist中F(n)值小的优先级别高
- 将起点加入openlist中
- 从队列中弹出元素
- 对弹出元素的周边进行搜索,如果发现有未被搜索到的位置,并且不是墙,河水,峡谷等不能行走的位置,则将其加入到队列中,并将其指向弹出元素。
如果发现有已经被搜索过的位置,则查看其F(n)值是否比现在走到那个位置的F(n)值小,若现在是更优解,则更新其F(n)值。如果其不在队列中则把其重新加入队列,否则什么也不用做。- 重复2-4步,直到找到目标点
- 最小路径即目标点的F(n)的值,同时根据维护的指针就可把最优路径推出来
补充:
我们从F(n)=G(n)+H(n)中可以推导出
- 当G(n)=0时,算法相当与bfs;
- 当H(n)=0时,算法相当于dfs;
自己写了一个示例代码如下:
@Frosero
#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;
char mps[12][21] = { //x*
{"...................."}, //0
{"...S................"}, //1
{"...................."}, //2
{"...................."}, //3
{"....##########......"}, //4
{"...................."}, //5
{"...................."}, //6
{"...................."}, //7
{".............#......"}, //8
{".............#..P..."}, //9
{".............#......"}, //10
{"...................."}, //11
}; //原地图
const int beginx = 1,beginy = 3,endx = 9,endy = 16;
int link[12][21] = {0}; //1-up 2-right 3-down 4-left
int dx[] = {1,-1,0,0},dy[] = {0,0,1,-1},f[12][21]; //f用于储存F值
bool can_move(int x,int y){
return x >= 0 && x < 12 && y >= 0 && y < 20 && mps[x][y] != '#';
}
class Node{
public:
int x,y;
int f,g,h;
Node() = default;
Node(int X,int Y,int G):x(X),y(Y),g(G) {
this->h = abs(x - endx) + abs(y - endy) ; //计算H(n)
this->f = this->g + this->h;
}
bool operator < (const Node &rhs) const {
return this->f > rhs.f;
}
};
void A_bfs(){
for(int i=0;i<12;i++) for(int j=0;j<21;j++) f[i][j] = INF;
priority_queue<Node> openlist;
while(!openlist.empty()) openlist.pop();
openlist.push(Node(beginx,beginy,0));
f[beginx][beginy] = abs(beginx - endx) + abs(beginy - endy) ;
while(!openlist.empty()){
Node now = openlist.top(); openlist.pop();
if(now.x == endx && now.y == endy) break;
if(now.f > f[now.x][now.y]) continue; //如果发现刚才记录的F值比现在实际F值小
//说明该点已经被更新过,忽略即可
for(int i=0;i<4;i++){
int nx = now.x + dx[i];
int ny = now.y + dy[i];
if(can_move(nx,ny) && now.g + 1 + abs(nx - endx) + abs(ny - endy) < f[nx][ny]){
mps[nx][ny] = mps[nx][ny] == 'P' ? 'P' : '*' ;
f[nx][ny] = now.g + 1 + abs(nx - endx) + abs(ny - endy) ;
if(nx == now.x + 1) link[nx][ny] = 1;
else if(nx == now.x - 1) link[nx][ny] = 3;
else if(ny == now.y + 1) link[nx][ny] = 4;
else if(ny == now.y - 1) link[nx][ny] = 2; //标记指针数组
openlist.push(Node(nx,ny,now.g + 1));
}
}
}
}
int main(){
A_bfs();
int x = endx,y = endy;
while(1){
if(link[x][y] == 1) x--;
else if(link[x][y] == 2) y++;
else if(link[x][y] == 3) x++;
else if(link[x][y] == 4) y--;
if(x == beginx && y == beginy) break;
mps[x][y] = '+';
}
printf("ans is : %d\n",f[endx][endy]);
for(int i=0;i<12;i++,printf("\n")){
for(int j=0;j<20;j++) printf("%c",mps[i][j]);
}
return 0;
}
/*
输出:
ans is : 21
...*****............
..*S************....
..*+*************...
..*+************....
..*+##########***...
..*+++++++++++++**..
..*************+**..
...************+**..
....*********#*+**..
.....********#*+P...
......*.*...*#.*....
....................
符号解释
S:起点
P:目标点
*:搜索过的路径
+:搜索过的最优路径
#:墙
Process returned 0 (0x0) execution time : 0.362 s
Press any key to continue.
*/
作为比较,我们用Dijkstra再做一次结果:
@Frosero
#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;
char mps[12][21] = { //x*
{"...................."}, //0
{"...S................"}, //1
{"...................."}, //2
{"...................."}, //3
{"....##########......"}, //4
{"...................."}, //5
{"...................."}, //6
{"...................."}, //7
{".............#......"}, //8
{".............#..P..."}, //9
{".............#......"}, //10
{"...................."}, //11
};
const int beginx = 1,beginy = 3,endx = 9,endy = 16;
int link[12][21] = {0}; //1-up 2-right 3-down 4-left
int dx[] = {1,-1,0,0},dy[] = {0,0,1,-1},f[12][21];
bool can_move(int x,int y){
return x >= 0 && x < 12 && y >= 0 && y < 20 && mps[x][y] != '#';
}
class Node{
public:
int x,y;
int g;
Node() = default;
Node(int X,int Y,int G):x(X),y(Y),g(G) { }
bool operator < (const Node &rhs) const {
return this->g > rhs.g;
}
};
void A_bfs(){
for(int i=0;i<12;i++) for(int j=0;j<21;j++) f[i][j] = INF;
priority_queue<Node> openlist;
while(!openlist.empty()) openlist.pop();
openlist.push(Node(beginx,beginy,0));
f[beginx][beginy] = 0 ;
while(!openlist.empty()){
Node now = openlist.top(); openlist.pop();
if(now.x == endx && now.y == endy) break;
for(int i=0;i<4;i++){
int nx = now.x + dx[i];
int ny = now.y + dy[i];
if(can_move(nx,ny) && now.g + 1 < f[nx][ny]){
mps[nx][ny] = mps[nx][ny] == 'P' ? 'P' : '*' ;
f[nx][ny] = now.g + 1;
if(nx == now.x + 1) link[nx][ny] = 1;
else if(nx == now.x - 1) link[nx][ny] = 3;
else if(ny == now.y + 1) link[nx][ny] = 4;
else if(ny == now.y - 1) link[nx][ny] = 2;
openlist.push(Node(nx,ny,now.g + 1));
}
}
}
}
int main(){
A_bfs();
int x = endx,y = endy;
while(1){
if(link[x][y] == 1) x--;
else if(link[x][y] == 2) y++;
else if(link[x][y] == 3) x++;
else if(link[x][y] == 4) y--;
if(x == beginx && y == beginy) break;
mps[x][y] = '+';
}
printf("ans is : %d\n",f[endx][endy]);
for(int i=0;i<12;i++,printf("\n")){
for(int j=0;j<20;j++) printf("%c",mps[i][j]);
}
return 0;
}
/*
输出:
ans is : 21
********************
***S++++++++++******
*************+******
*************++*****
****##########+*****
**************+*****
**************+*****
**************+*****
*************#+****.
*************#++P...
*************#**....
***************.....
符号解释
S:起点
P:目标点
*:搜索过的路径
+:搜索过的最优路径
#:墙
Process returned 0 (0x0) execution time : 0.362 s
Press any key to continue.
*/
通过比较我们可以很轻松的发现A*搜索相比于Dijkstra的优越性
下面是在网上找的几组图片来进行一些对比
图片来源:[http://blog.csdn.net/luckyxiaoqiang/article/details/6996963]
例一
1. A*(曼哈顿距离)
2. A*(欧氏距离)
3. A*(切比雪夫距离)
4. Dijkstra
5. 双向BFS
例二
1. A*(曼哈顿距离)
2. A*(欧氏距离)
3. A*(切比雪夫距离)
4. Dijkstra
5. 双向BFS
例三
1. A*(曼哈顿距离)
2. A*(欧氏距离)
3. A*(切比雪夫距离)
4. Dijkstra
5. 双向BFS
例四
1. A*(曼哈顿距离)
2. A*(欧氏距离)
3. A*(切比雪夫距离)
4. Dijkstra
5. 双向BFS
A*启发式搜索基础的更多相关文章
- new 经典基础模板总结
NOIP-NOI-ZJOI基础模板总结 目录 C++语言和STL库操作 重载运算符操作 /* 重载运算符 格式 如重载小于号 这里是以x递减为第一关键字比较,y递减为第二关键字比较 */ bool o ...
- 启发式搜索A-Star算法 【寻找 最短路径 算法】【地理几何位置 可利用的情况】
在处理最短路径问题时,有一种启发式算法是我们应该了解的,由于其有着优秀的探索效率在各自现实项目中多有应用,它就是 A-star 算法,或 A* 算法. 个人观点: A* 算法并不保证找到的路径一 ...
- 启发式搜索技术A*
开篇 这篇文章介绍找最短路径的一种算法,它的字我比较喜欢:启发式搜索. 对于入门的好文章不多,而这篇文章就是为初学者而写的,很适合入门的一篇.文章定位:非专业性A*文章,很适合入门. 有图有真相,先给 ...
- 基础路径规划算法(Dijikstra、A*、D*)总结
引言 在一张固定地图上选择一条路径,当存在多条可选的路径之时,需要选择代价最小的那条路径.我们称这类问题为最短路径的选择问题.解决这个问题最经典的算法为Dijikstra算法,其通过贪心选择的步骤从源 ...
- java基础集合经典训练题
第一题:要求产生10个随机的字符串,每一个字符串互相不重复,每一个字符串中组成的字符(a-zA-Z0-9)也不相同,每个字符串长度为10; 分析:*1.看到这个题目,或许你脑海中会想到很多方法,比如判 ...
- node-webkit 环境搭建与基础demo
首先去github上面下载(地址),具体更具自己的系统,我的是windows,这里只给出windows的做法 下载windows x64版本 下载之后解压,得到以下东西 为了方便,我们直接在这个目录中 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- Golang, 以17个简短代码片段,切底弄懂 channel 基础
(原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...
- [C#] C# 基础回顾 - 匿名方法
C# 基础回顾 - 匿名方法 目录 简介 匿名方法的参数使用范围 委托示例 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法. 而 C# 2.0 -- 引进了匿名方法,在 ...
随机推荐
- java基础学习笔记三(多态)
多态? 多态是同一个行为具有多个不同表现形式或形态的能力. 存在的必要条件 继承 重写 父类引用指向子类对象 比如: Parent p = new Child(); 当使用多态方式调用方法时,首先检查 ...
- 【LeetCode 35】搜索插入位置
题目链接 [题解] 还是那句话,想知道l或者r所在的数字的含义 就想想它最后一次执行的时候在干什么就行. [代码] class Solution { public: int searchInsert( ...
- kubernetes使用kubeadm升级集群
升级前准本 官网: https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-upgrade/查看可升级的组件 [root@h ...
- paper 136:ARM ADS集成开发环境的使用(新版)
[转载]:http://blog.csdn.net/yhmhappy2006/article/details/1673203 ARM ADS集成开发环境的使用 在这里,将介绍ARM开发软件ADS(AR ...
- SLA(服务等级协议)
SLA:Service-Level Agreement的缩写,意思是服务等级协议.是关于网络服务供应商和客户间的一份合同,其中定义了服务类型.服务质量和客户付款等术语. 定义SLA:Service-L ...
- 极限IO优化
namespace IO{ #define BUF_SIZE 100000 #define OUT_SIZE 100000 #define ll long long //fread->read ...
- 一个普通函数的冷僻属性(length、caller、arguments、name、[[Scopes]]和[[FunctionLocation]])
https://blog.csdn.net/qq_17175013/article/details/81915059
- GDB can't continue if no space left
[root@premta ~]# df -hFilesystem Size Used Avail Use% Mounted on/dev/sda3 36G 36G 0 100% /tmpfs 1.5G ...
- 2019牛客多校第⑨场J Symmetrical Painting(思维,离散化)
原题:https://ac.nowcoder.com/acm/contest/889/J 题意: 二维平面上有n个矩形,每个矩形左下角是(i−1,Li)(i−1,Li), 右上角是(i,Ri)(i,R ...
- Java中创建泛型数组
Java中创建泛型数组 使用泛型时,我想很多人肯定尝试过如下的代码,去创建一个泛型数组 T[] array = new T[]; 当我们写出这样的代码时编译器会报Cannot create a gen ...