0.简介
前面有了Dijkstra算法的铺垫以后,我们来看A*算法。
1.启发式
这个启发式从字面理解就是启发,受到启发,算法在寻路的时候会受到启发?差不多是这样。看下面这句代码
float t_cost = t.cost+ mapNodes[node.x][node.y].cost+qf*1000;//此节点与父节点距离+父节点到起点距离+启发因子
我们发现,这个与Dijkstra的主要区别就是在更新路径长度的时候,多了一个启发因子,这个启发因子我们可以设置成当前点到目标点的欧拉距离,或者是曼哈顿距离。
这里用的是曼哈顿距离。
float qf = abs(t.x- disX)+abs(t.y- disY);
本质上就是在Dijkstra算法求路径长度上添加了一个额外的权重。
看下图。
图中蓝色是走过的路线,橙色是当前点到终点的连线。
假设 S1 = X + Y,S2 = A + B,如果不是启发式搜索,那么算法要搜索全部路径后才能知道最短路径是哪条,现在是启发式算法,也就是有了图中Y和B的长度作为参考,我们发现虽然X比A大,应该选A这条路走, 但是由于B比Y大很多,橙色线告诉我们,走A的话就会使得我们距离目标更远了,走X的话会让我们距离目标更近了,所以我们不再只看X与A的大小,而是看S1与S2的大小,也就是算法有了一定的预判能力,这样可以预先筛选掉许多路线,把搜索范围缩小。
那么这个筛选能有多大效率提升呢?
我将启发因子权重调整为0,此时算法退化为Dijkstra算法,下图中灰色部分是搜索的范围,中间白色的线是最短路径。
我们发现算法几乎搜索了整个地图,黑色部分是不可走的地方。
如果将启发权值设置成了1000
搜索范围大大减少,虽然得到的最短路径和上面的有所不同,但是相比来说,也是最短。算法在寻路的过程中更加有目的性,不想上面的方法漫无目的的搜索。
2.实现
本篇内容其实并不多,所以直接看代码吧,毕竟算法比较简单。代码需要配置opencv
#include<iostream>
#include<unordered_map>
#include<list>
#include<queue>
#include<opencv2/opencv.hpp>
#include<windows.h>
using namespace std;
using namespace cv;
class GEdge
{
public:
int begin, end;
double cost;
GEdge() {}
GEdge(int _begin,int _end,double _cost):begin(_begin),end(_end),cost(_cost){}
GEdge(int _begin, int _end) :begin(_begin), end(_end), cost(1.0) {}
};
class GNode
{
public:
int id;
GNode(int _id) : id(_id) {}
GNode(){}
list<GEdge>edges;
};
class SelectNode
{
public:
int id;
double cost;
SelectNode() {}
SelectNode(int _id,double _cost) : id(_id),cost(_cost){}
bool operator<(const SelectNode& a) const
{
return cost > a.cost;
}
};
class mapNode
{
public:
mapNode() {}
mapNode(int _x,int _y,double pc,double _cost):x(_x),y(_y), pathCost(pc),cost(_cost) {}
int x, y;
double cost;
double pathCost;
bool operator<(const mapNode& a) const
{
return (pathCost > a.pathCost);
}
};
mapNode getNode(int x, int y,int index)
{
float cost = 0;
if (index == 0)
{
x--;
y--;
cost = 14;
}
else if (index == 1)
{
y--;
cost = 10;
}
else if (index == 2)
{
x++;
y--;
cost = 14;
}
else if (index == 3)
{
x--;
cost = 10;
}
else if (index == 4)
{
x++;
cost = 10;
}
else if (index == 5)
{
x--;
y++;
cost = 14;
}
else if (index == 6)
{
y++;
cost = 10;
}
else if (index == 7)
{
x++;
y++;
cost = 14;
}
if (x < 0 || y < 0 || x >= 100 || y >= 100)
cost = 999;
return mapNode(x, y,0,cost);
}
int main()
{
Mat bmp = imread("123.bmp");
cvtColor(bmp, bmp, CV_BGR2GRAY);
mapNode **mapNodes = new mapNode*[600];
for (int i = 0; i < 600; i++)
{
mapNodes[i] = new mapNode[600];
memset(mapNodes[i], 0, 600 * sizeof(mapNode));
}
priority_queue<mapNode> queue;
queue.push(mapNode(10, 23,0,0));
int disX = 71, disY =71;
while (!queue.empty())
{
mapNode node = queue.top();
queue.pop();
for (int i = 0; i < 8; i++)
{
mapNode t = getNode(node.x, node.y, i);
//这里根据图片提前判断是否有墙壁
if (bmp.at<uchar>(t.x, t.y) < 20)
continue;
//float qf = sqrt((t.x - disX) * (t.x - disX) + (t.y - disY) * (t.y - disY));
float qf = abs(t.x- disX)+abs(t.y- disY);
float t_cost = t.cost+ mapNodes[node.x][node.y].cost+qf*1000;//此节点与父节点距离+父节点到起点距离+启发因子
if (mapNodes[t.x][t.y].pathCost == 0|| t_cost < mapNodes[t.x][t.y].pathCost)
{
//这里存储最短路径的结果
//这是启发式路径长度
mapNodes[t.x][t.y].pathCost = t_cost;
//这是之前走过的实际路径长度
mapNodes[t.x][t.y].cost = t.cost+ mapNodes[node.x][node.y].cost;
mapNodes[t.x][t.y].x = node.x;
mapNodes[t.x][t.y].y = node.y;
queue.push(mapNode(t.x,t.y,t_cost, mapNodes[t.x][t.y].cost));
uchar* data = bmp.ptr<uchar>(t.x);
data[t.y] = 100;
if(node.x==93&& node.y==67)
cout << t.x << " " << t.y << endl;
if (t.x == disX && t.y == disY)
{
while (!queue.empty())
queue.pop();
break;
}
Mat showim = bmp;
resize(bmp, showim, Size(500, 500));
imshow("map_path", showim);
waitKey(1);
}
}
}
int startX= disX, startY= disY;
while (1)
{
int t_startX = mapNodes[startX][startY].x;
int t_startY = mapNodes[startX][startY].y;
startX = t_startX;
startY = t_startY;
uchar* data = bmp.ptr<uchar>(startX);
data[startY] = 200;
if (startX == 2 && startY == 2)
break;
Mat showim = bmp;
resize(bmp, showim, Size(500, 500));
imshow("map_path", showim);
waitKey(20);
}
imwrite("111.bmp",bmp);
resize(bmp,bmp,Size(600,600));
imshow("map_path",bmp);
waitKey(0);
return 0;
}