Dijkstra算法与A*算法关于H值取值分析(含c++代码)
程序员文章站
2022-04-15 16:33:23
...
A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。
A*算法是在Dijkstra的进一步发展,由于Dijkstra算法虽然能够搜索到最优路径,但是其搜索空间一般比较大。在此基础上,对其进行优化,加入了一个启发式函数H,便于在搜索到最优路径的情况下,减小其搜索空间。
二者的伪代码如下所示:
Dijkstra算法伪代码:
Astar算法伪代码:
具体算法实现如下:
Astar.h
#pragma once
/*
//A*算法对象类
*/
#include <vector>
#include <list>
const int kcost1 = 10;
const int kcost2 = 14;
struct Point {
int x;
int y;
int f_score, g_score, h_score;
Point* father;
Point(int _x, int _y) :x(_x), y(_y), f_score(0), g_score(0), h_score(0), father(NULL)
{
}
};
class Astar {
public:
void InitAstar(std::vector<std::vector<int>>& _map);
std::list<Point*> GetPath(Point& start, Point& end, bool isIgnoreCorner);
std::list<Point*> openlist();
std::list<Point*> closelist();
private:
Point* findPath(Point& start, Point& end, bool isIgnoreCorner);
std::vector<Point*> getSurPoints(const Point* curpoint, bool isIgnoreCorner) const;
bool isCanSearch(const Point* start, const Point* target, bool isIgnoreCorner) const;
Point* isInList(const std::list<Point*>& list, const Point* point) const;
Point* getLeastFpoint();
int getH(Point* start, Point* end);
int getG(Point* start, Point* temp);
int getF(Point* current);
private:
std::vector<std::vector<int>> map;
std::list<Point*> openList;
std::list<Point*> closeList;
};
Astar.cpp:
#include <math.h>
#include "Astar.h"
#include <algorithm>
void Astar::InitAstar(std::vector<std::vector<int>>& _map) {
map = _map;
}
int Astar::getH(Point* start, Point* end) {
int dx = abs(end->x - start->x);
int dy = abs(end->y - start->y);
//Euclidean距离
//return sqrt((double)(end->x - start->x) * (double)(end->x - start->x) + (double)(end->y - start->y) * (double)(end->y - start->y)) * kcost1;
//return (abs(end->x - start->x) + abs(end->y - start->y)) * kcost1; //Manhattan距离
//return (dx+dy+(double)(sqrt(2)-2)*std::min(dx,dy))*kcost1; //对角线距离
return 0; //Dijkstra算法
}
int Astar::getG(Point* start, Point* temp) {
int g_temp = (abs(temp->x - start->x) + abs(temp->y - start->y)) == 1 ? kcost1 : kcost2;
int g_before = temp->father == NULL ? 0 : temp->father->g_score;
return g_temp + g_before;
}
int Astar::getF(Point* current) {
return current->g_score + current->h_score;
}
Point* Astar::getLeastFpoint() {
if (!openList.empty()) {
auto respoint = openList.front();
for (auto& p : openList)
if (p->f_score < respoint->f_score)
respoint = p;
return respoint;
}
return NULL;
}
Point* Astar::findPath(Point& start, Point& end, bool isIgnoreCorner) {
openList.push_back(new Point(start.x, start.y));
while (!openList.empty())
{
auto curpoint = getLeastFpoint();
openList.remove(curpoint);
closeList.push_back(curpoint);
auto surPoints = getSurPoints(curpoint, isIgnoreCorner);
for (auto& target : surPoints) {
if (!isInList(openList, target)) {
target->father = curpoint;
target->g_score = getG(curpoint, target);
target->h_score = getH(target, &end);
target->f_score = getF(target);
openList.push_back(target);
}
else {
Point* p_open = isInList(openList, target);
int tempG = getG(curpoint, target);
if (tempG < p_open->g_score) {
target->father = curpoint;
target->g_score = tempG;
target->f_score = getF(target);
}
}
Point* respoint = isInList(openList, &end);
if (respoint)
return respoint;
}
}
return NULL;
}
std::list<Point*> Astar::GetPath(Point& start, Point& end, bool isIgnoreCorner) {
std::list<Point*> path;
Point* curpoint = findPath(start, end, isIgnoreCorner);
while (curpoint)
{
path.push_front(curpoint);
curpoint = curpoint->father;
}
//openList.clear();
//closeList.clear();
return path;
}
Point* Astar::isInList(const std::list<Point*>& list, const Point* point) const {
for (auto p : list)
if (p->x == point->x && p->y == point->y)
return p;
return NULL;
}
bool Astar::isCanSearch(const Point* start, const Point* target, bool isIgnoreCorner) const
{
if (target->x < 0 || target->x > map.size() - 1
|| target->y < 0 || target->y > map[0].size() - 1
|| map[target->x][target->y] == 1 //障碍物
|| target->x == start->x && target->y == start->y //重合
|| isInList(closeList, target))
return false;
else {
if (abs(target->x - start->x) + abs(target->y - start->y) == 1)
return true;
else {
if (map[start->x][target->y] == 0 && map[target->x][start->y] == 0) {
return true;
}
else
return isIgnoreCorner;
}
}
}
std::vector<Point*> Astar::getSurPoints(const Point* curpoint, bool isIgnoreCorner) const {
std::vector<Point*> surPoints;
for (int i = curpoint->x - 1; i <= curpoint->x + 1; i++)
for (int j = curpoint->y - 1; j <= curpoint->y + 1; j++)
if (isCanSearch(curpoint, new Point(i, j), isIgnoreCorner)) {
surPoints.push_back(new Point(i, j));
}
return surPoints;
}
std::list<Point*> Astar::openlist() {
std::list<Point*> path;
for (auto p : openList)
path.push_back(p);
return path;
}
std::list<Point*> Astar::closelist() {
std::list<Point*> path;
for (auto c : closeList)
path.push_back(c);
return path;
}
main.cpp(测试文件):
#include <iostream>
#include "Astar.h"
using namespace std;
int main()
{
//初始化地图,用二维矩阵代表地图,1表示障碍物,0表示可通
vector<vector<int>> maze = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1 },
{ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },
{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
Astar astar;
astar.InitAstar(maze);
cout << "Initialization map:" << endl;
for (int i = 0; i < maze.size(); i++)
for (int j = 0; j < maze[0].size(); j++) {
if (j != maze[0].size() - 1)
cout << maze[i][j] << ",";
else
cout << maze[i][j] << endl;
}
//设置起始和结束点
Point start(1, 1);
Point end(6, 10);
//A*算法找寻路径
list<Point*> path = astar.GetPath(start, end, false);
//打印
list<Point*> openList = astar.openlist();
list<Point*> closeList = astar.closelist();
cout << "The search scope is: " << openList.size() + closeList.size() << endl;
cout << "The optimal path:" << endl;
for (auto& p : path) {
maze[p->x][p->y] = 3;
cout << '(' << p->x << ',' << p->y << ')';
}
cout << endl;
for (auto& o : openList) {
if (maze[o->x][o->y] == 3) {
maze[o->x][o->y] = 3;
}
else {
maze[o->x][o->y] = 4;
}
}
for (auto& c : closeList) {
if (maze[c->x][c->y] == 3) {
maze[c->x][c->y] = 3;
}
else {
maze[c->x][c->y] = 4;
}
}
cout << "The step numbers: " << path.size() << endl;
for (int i = 0; i < maze.size(); i++)
for (int j = 0; j < maze[0].size(); j++) {
if (j != maze[0].size() - 1)
cout << maze[i][j] << ",";
else
cout << maze[i][j] << endl;
}
system("pause");
return 0;
}
算法实现参考了其他博主的代码,本文主要目的是通过对算法的改进,讲诉Dijkstra与A*,以及A*取各种不同的启发式函数,所产生的区别。
G:只有对角线移动和平移,其中斜对角线移动的G值为14,平移为10
H:采用Euclidean距离函数
G:同上
H:采用Manhattan距离函数
G:同上
H:采用对角线距离
G:同上
H:0,即Dijkstra算法
通过结果可见,只需要更改H值,可以产生不同的路径和搜索范围。Dijkstra虽然达到了最优路径,但是其搜索的范围却是最大的,这样容易造成时间的浪费。而不同的启发式函数H,在减小搜索范围的同时,也将产生不同的路径,因此在应用时,需要对其进行一定的设计。为了打破对称性,可以设计一个tie_break值,该值可以设计为:
则H值可以更新为h=h+p。