A-star算法自学

搜索过程

广度优先搜索(BFS)算法与Dijsktra算法的结合,可以得出最短的路径。
将地图信息通过划分为方形或者其他多边形格子的方法进行表示,便于利用二维数组存放地图信息,每个格子有可行和不可行两种状态;每个方格用节点的形式表示(节点可以放置在形状内的任何位置 - 在中心或沿边缘),以便于用于任何形状的格子中的情况。
如上图所示,绿色格子为起点,红色格子为终点,蓝色格子为障碍物
如上图所示,绿色格子为起点,红色格子为终点,蓝色格子为障碍物

评价函数

移动代价
横向/纵向移动代价设为 10
对角线移动代价设为14
主要计算三个值F、G及H
F = G + H
F:从初始块经由n个块后到目标块的最小代价估计
G:表示从起点s到点n的实际成本
G(s) = G(s的父节点) + 10或14(横向移动或者纵向移动)
H:表示从节点n到目标T的估算成本(试探法)
利用曼哈顿方法计算: 计算当前方格n通过横向或者纵向移动到T的节点数 * 10
d = |x1 - x2| + |y1 - y2| * 10
欧式距离计算: 计算n与T的欧式距离

步骤(参考)

设置一个 open list(待检查列表) 及 close list(无需检查列表

  1. 从起点开始,将其存入open list中,open list中的方格可能是路径会通过的,也可能是路径不会通过的方格。

  2. 寻找起点周围所有可以到达的节点(相邻节点),忽略障碍方格;并将这些相邻节点存入open list中并且这些节点的父节点均为起点。

  3. 从open list中将起点取出,存入 close list中,如下图所示:![[Pasted image 20230426120659.png]]图中绿色方块为起点,其边框为蓝色,表示该方块已存入close list中,并且其周围的8个方块均在open list中,且指针指向中间的绿色方块,表示父节点为绿色方块

  4. 继续遍历open list中的节点,并重复下述过程。

    1. 计算每个节点的F值,选择F值的节点n作为当前处理的节点
    2. 对于当前节点n,与其相邻的所有节点(b,c,d等)如下操作:
      1. 如果节点b是不可到达(unreachable)或者在close list中,则不进行任务操作
      2. 如果节点c不在open list中,则将其加入open list中,并将当前节点n设为c 的父代,计算节点b的F、G、H值
      3. 若节点d在open list中,则检查节点n到节点d的路径是否更优(若该路径的g值更小,则说明其更优);若该路径更优,则将节点d的父节点(假设为e)设置为当前节点,并重新计算节点e的F、G值
    3. 将节点n存入 close list中
      在这里插入图片描述
  5. 程序运行结束条件(二者满足其一)

    1. 终点在close list中
    2. 无法找到重点,且open list为空
  6. 得到最优路径:从终点开始,每个节点沿着父节点移动,直达到达起点
    在这里插入图片描述

流程图

在这里插入图片描述

代码

A-star.h

#pragma once
#include<vector>
#include<list>

using namespace std;

const int kCost1 = 10; // 横向、纵向移动代价
const int kCost2 = 14; // 对角线移动代价

struct Point
{
	int x, y; // 点坐标,x为横,y为纵坐标
	int F, G, H; // F = G + H
	Point* parent; // 父代坐标
	Point(int _x, int _y) : x(_x), y(_y), F(0), G(0), H(0), parent(nullptr) {} // 初始
};

class Astar
{
public:
	void InitAstar(vector<vector<int>>& _maze);
	list<Point*> GetPath(Point &starPoint, Point &endPoint, bool isIgnoreCorner);
private:
	Point* findPath(Point &startPoint, Point &endPoint, bool isIgnoreCorner);
	vector<Point*> getSurrentPoints(const Point* point, bool isIgnoreCorner) const;
	bool isCanreach(const Point *point, const Point *target, bool isIgnoreCorner) const;  // 判断某点是否可以用于下一步判断
	Point *isInList(const list<Point *> &list, const Point *point) const; // 判断open_list/close_list中是否包含某点
	Point* getLeastFpoint(); // 从open_list中返回F值最小的节点

	// 计算 F G H
	int calG(Point *temp_start, Point *point);
	int calH(Point *point, Point *end);
	int calF(Point *point);

	vector<vector<int>> maze; // 存放地图的二维数组
	list<Point*> openList; // open_list 列表
	list<Point*> closeList; // close_list 列表

};

A-star.cpp

#include "A-star.h"

void Astar::InitAstar(vector<vector<int>>& _maze)
{
	maze = _maze;
}

list<Point*> Astar::GetPath(Point& starPoint, Point& endPoint, bool isIgnoreCorner)
{
	Point* res = findPath(starPoint, endPoint, isIgnoreCorner);
	list<Point*> path;
	// 返回路径
	while (res)
	{
		path.push_back(res);
		res = res->parent;
	}

	// 情况open list和close list
	openList.clear();
	closeList.clear();

	return path;
}

Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner)
{
	openList.push_back(new Point(startPoint.x, startPoint.y)); // open_list中存入七点,并开辟一个新的下一个节点
	while (!openList.empty())
	{
		auto curPoint = getLeastFpoint(); // 找到最小F值的节点
		openList.remove(curPoint); // 从open list中将当前节点移出
		closeList.push_back(curPoint); // 将当前节点存入close list中
		// 1.找到当前节点相邻的可以通过的格子
		auto surroundPoints = getSurrentPoints(curPoint, isIgnoreCorner);
		for (auto& target : surroundPoints)
		{
			// 2. 对某一个格子进行判断, 若其不在open list中,则加入到open list中, 并设其为父节点 计算其 F G H值
			if (!isInList(openList, target))
			{
				target->parent = curPoint;
				target->G = calG(curPoint, target);
				target->H = calH(target, &endPoint);
				target->F = calF(target);

				openList.push_back(target);
			}
			// 3. 若其在open list中,则计算其G值并 与当前节点进行比较,若其G值大,则不进行任何操作, 否则设置它的父节点为当前节点,并更新G F 
			else
			{
				int tempG = calG(curPoint, target);
				if (tempG < target->G)
				{
					target->parent = curPoint;
					target->G = tempG;
					target->F = calF(target);
				}
			}
			Point* resPoint = isInList(openList, &endPoint);
			if (resPoint)
				return resPoint;
		}
	}
	return nullptr;
}

vector<Point*> Astar::getSurrentPoints(const Point* point, bool isIgnoreCorner) const
{
	vector<Point*> surroundPoints;
	for (int x = point->x - 1; x <= point->x + 1; ++x)
		for (int y = point->y - 1; y <= point->y; ++y)
			if (isCanreach(point, new Point(x, y), isIgnoreCorner))
				surroundPoints.push_back(new Point(x, y));
	return surroundPoints;
}

bool Astar::isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const
{
	/*if (target->x < 0 || target->x > maze.size() - 1 || target->y < 0 || target->y > maze[0].size() - 1 || maze[target->x][target->y] == 1 || target->x == point->x && target->y == point->y || isInList(closeList, target))
		return false;*/
	if (target->x<0 || target->x>maze.size() - 1
		|| target->y<0 || target->y>maze[0].size() - 1
		|| maze[target->x][target->y] == 1
		|| target->x == point->x && target->y == point->y
		|| isInList(closeList, target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false
		return false;
	else
	{
		 只有对角线才能进行搜索
		//if (abs(point->x - target->x) + abs(point->y - target->y) == 1)
		//	return true;
		//else
		//{
		//	// 对角线需要判断是否会遇到障碍
		//	if (maze[point->x][target->y] == 0 && maze[target->x][point->y] == 0)
		//		return true;
		//	else
		//		return isIgnoreCorner;
		//}
		return true;
	}
}

// 判断某个节点是否在列表中
Point* Astar::isInList(const list<Point*>& list, const Point* point) const
{
	for (auto p : list)
		if (p->x == point->x && p->y == point->y)
			return p;
	return nullptr;
}

Point* Astar::getLeastFpoint()
{
	if (!openList.empty())
	{
		auto resPoint = openList.front();
		for (auto& point : openList)
		{
			if (point->F < resPoint->F)
			{
				resPoint = point;
			}
		}
		return resPoint;
	}
	return nullptr;
}

int Astar::calG(Point* temp_start, Point* point)
{
	int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2;
	int parentG = point->parent == nullptr ? 0 : point->parent->G; // 如果为初始点, 则其父节点为空
	return parentG + extraG;
}

int Astar::calH(Point* point, Point* end)
{
	// 根据曼哈顿算法进行计算
	return (abs(point->x - end->x) + abs(point->y - end->y)) * kCost1;

	//return sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1;
}

int Astar::calF(Point* point)
{
	return point->G + point->H;
}

main.cpp

#include<iostream>
#include "A-star.h"
#include "A-star2.h"

bool InPath(const int &row, const int &col, const list<Point *> &path)
{
	for (const auto& p : path)
	{
		if (row == p->x || col == p->y)
		{
			return true;
		}
	}
	return false;
}

int main()
{
	// 初始化地图,用二维矩阵代表地图,1表示障碍物,0表示可通
	vector<std::vector<int>> map = { {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(map);

	// 设置起点和目标点
	Point start(1, 1);
	Point end(6, 10);

	// 寻找路径
	list<Point*> path = astar.GetPath(start, end, false);

	// 打印路径
	for (auto& p : path)
	{
		cout << "(" << p->x << ", " << p->y << ")";
	}
	cout << endl;

	for (int row = 0; row < map.size(); ++row) {
		for (int col = 0; col < map[0].size(); ++col) {
			if (InPath(row, col, path)) {
				if (map[row][col] != 0) {
					cout << "e ";
				}
				else {
					cout << "* ";
				}
			}
			else {
				cout << map[row][col] << " ";
			}
		}
		cout << endl;
	}

}

参考链接:
https://www.gamedev.net/reference/articles/article2003.asp
https://blog.csdn.net/A_L_A_N/article/details/81392212
https://zhuanlan.zhihu.com/p/590786438

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xxwl.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值