赞
踩
关于A*算法的实现是很早之前的一次开发中的成果,并做了一些改进。当然,在这里就不记录改进部分了,因为其中还有一些争议。这里仅是对A*算法的理解和使用Python实现。
之所以放在前面,是因为这些链接的参考价值特别高,如果希望获得更多的了解,可以通过以下链接进行学习。
英文网站
A* Pathfinding for Beginners - Artificial Intelligence - Tutorials - GameDev.net
redblobgames(红色斑点游戏)
中文网站
csdn:A星算法详解(个人认为最详细,最通俗易懂的一个版本)|模块 A*算法总结 非常有参考价值
A星算法详解(个人认为最详细,最通俗易懂的一个版本)_Colin丶-CSDN博客_a星算法
知乎:路线规划之A*算法
2021.03.25 优化
2021.11.03权重优化
(百度百科)A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快
一个真正的A*算法必须包含以下对象:开启列表、关闭列表、G、H
G(n)表示的是从起始节点到当前节点的距离代价。
在A*算法中,这是一个确切但可变的数值
可变是因为从起始节点到当前节点的不同移动方案,其距离代价也是不同的
确切是一旦移动方案的确定,其距离代价也确定了
例如以下的
如果从节点A出发,那么选择方案1的话,G(n)=30,这是确切的
如果有更好的移动方案,如这里的方案3的话,则G(n)=/2*10,这是可变的
(假设一个正方形的大小为10)
H(n)表示的是从当前节点到目标节点的估计代价,也就是说只是一个估计值。
也是A*算法的启发函数
有3种处理方式:
方式1:如果从当前点出发,可以向上下左右四个方向移动,则使用曼哈顿距离
方式2:如果从当前点出发,可以向上下左右四个方向移动外,还能斜方向运行,共八个方向,则使用对角距离
方式3:允许向任何方向移动,则使用欧几里得距离
曼哈顿距离、对角距离、欧氏距离
思考:3种距离对A*算法的影响
曼哈顿距离可以说是最简单粗暴的计算距离的方法
其实对角距离是将欧氏距离的处理方法局部化,局限于当前节点的当前移动。
而欧氏距离是对角距离的整体化。
思考:G(n)和H(n)的区别
1.G(n)是个可变但确切的实际值,H(n)是个确切的估计值
一旦当前点的确定,H(n)将是一个定值.
而G(n)会随着移动方案的改变而改变
2.G(n)是无法通过欧氏距离计算的。
因为每一个静态路网中,如果不存在不可达的点,那么A*算法将没有存在的意义。
每一次从起点到达终点,只需要从起点出发,向终点方向移动即可。
而当存在不可达点,那么如果使用欧氏距离计算G(n),如果起点到当前点的欧氏距离线上有一个障碍物,
则实际的移动代价将会大于G(n)
而H(n)可以使用3种距离计算方式
- 创建开启列表、关闭列表
- 将起始点加入开放列表中
- 弹出开启列表的第一个节点,将该元素作为当前节点
- while(当前节点不为终点时):
- 获取并处理当前节点的周围可通过的节点,
- 为这些节点设置G值、H值,
- 添加当前节点为这些可通过节点的父节点
- 遍历当前节点的周围节点
- if(该节点可通过且不在关闭列表):
- if(该节点在开启列表):
- 根据 估价函数F(n)进行比较
- if(开启列表的节点的估价函数大于该节点的估价函数):
- 将开启列表的节点进行覆盖
- else:
- 将该节点加入开启列表
- 将当前节点添加到关闭列表中
- 对开启列表进行排序
- 当前节点<-获得开启列表的第一个元素
- 创建一个路径结果集
- while(当前节点的父节点不为空):
- 获得当前节点的索引,存入路径结果集中
- 当前节点<-当前节点的父节点
- 返回路径结果集
- 结束
父节点、邻接节点
地图(地图数据,起点,终点)
节点(包含H、G,父节点)
开启列表
关闭列表
1.获得当前节点的可用的邻居节点
2.将邻接节点插入开启列表中
3.对开启列表进行排序处理(根据G(n)值)
4.查找一个节点是否在开启列表中
注:可以将2与3进行整合,在插入时同时实现排序处理
- import math
- '''
- 对象Map,主要有地图数据、起点和终点
- '''
- class Map(object):
- def __init__(self,mapdata,startx,starty,endx,endy):
- self.data = mapdata
- self.startx = startx
- self.starty = starty
- self.endx = endx
- self.endy = endy
- '''
- Node.py主要是描述对象Node
- '''
- class Node(object):
- '''
- 初始化节点信息
- '''
- def __init__(self,x,y,g,h,father):
- self.x = x
- self.y = y
- self.g = g
- self.h = h
- self.father = father
- '''
- 处理边界和障碍点
- '''
- def getNeighbor(self,mapdata,endx,endy):
- x = self.x
- y = self.y
- result = []
- #先判断是否在上下边界
- #if(x!=0 or x!=len(mapdata)-1):
- #上
- #Node(x,y,g,h,father)
- if(x!=0 and mapdata[x-1][y]!=0):
- upNode = Node(x-1,y,self.g+10,(abs(x-1-endx)+abs(y-endy))*10,self)
- result.append(upNode)
- #下
- if(x!=len(mapdata)-1 and mapdata[x+1][y]!=0):
- downNode = Node(x+1,y,self.g+10,(abs(x+1-endx)+abs(y-endy))*10,self)
- result.append(downNode)
- #左
- if(y!=0 and mapdata[x][y-1]!=0):
- leftNode = Node(x,y-1,self.g+10,(abs(x-endx)+abs(y-1-endy))*10,self)
- result.append(leftNode)
- #右
- if(y!=len(mapdata[0])-1 and mapdata[x][y+1]!=0):
- rightNode = Node(x,y+1,self.g+10,(abs(x-endx)+abs(y+1-endy))*10,self)
- result.append(rightNode)
- #西北 14
- if(x!=0 and y!=0 and mapdata[x-1][y-1]!=0 ):
- wnNode = Node(x-1,y-1,self.g+14,(abs(x-1-endx)+abs(y-1-endy))*10,self)
- result.append(wnNode)
- #东北
- if(x!=0 and y!=len(mapdata[0])-1 and mapdata[x-1][y+1]!=0 ):
- enNode = Node(x-1,y+1,self.g+14,(abs(x-1-endx)+abs(y+1-endy))*10,self)
- result.append(enNode)
- #西南
- if(x!=len(mapdata)-1 and y!=0 and mapdata[x+1][y-1]!=0 ):
- wsNode = Node(x+1,y-1,self.g+14,(abs(x+1-endx)+abs(y-1-endy))*10,self)
- result.append(wsNode)
- #东南
- if(x!=len(mapdata)-1 and y!=len(mapdata[0])-1 and mapdata[x+1][y+1]!=0 ):
- esNode = Node(x+1,y+1,self.g+14,(abs(x+1-endx)+abs(y+1-endy))*10,self)
- result.append(esNode)
- # #如果节点在关闭节点 则不进行处理
- # finaResult = []
- # for i in result:
- # if(i not in lockList):
- # finaResult.append(i)
- # result = finaResult
- return result
- def hasNode(self,worklist):
- for i in worklist:
- if(i.x==self.x and i.y ==self.y):
- return True
- return False
- #在存在的前提下
- def changeG(self,worklist):
- for i in worklist:
- if(i.x==self.x and i.y ==self.y):
- if(i.g>self.g):
- i.g = self.g
- from Node import Node
- def getKeyforSort(element:Node):
- return element.g #element#不应该+element.h,否则会穿墙
- def astar(workMap):
- startx,starty = workMap.startx,workMap.starty
- endx,endy = workMap.endx,workMap.endy
- startNode = Node(startx, starty, 0, 0, None)
- openList = []
- lockList = []
- lockList.append(startNode)
- currNode = startNode
- while((endx,endy) != (currNode.x,currNode.y)):
- workList = currNode.getNeighbor(workMap.data,endx,endy)
- for i in workList:
- if (i not in lockList):
- if(i.hasNode(openList)):
- i.changeG(openList)
- else:
- openList.append(i)
- openList.sort(key=getKeyforSort)#关键步骤
- currNode = openList.pop(0)
- lockList.append(currNode)
- result = []
- while(currNode.father!=None):
- result.append((currNode.x,currNode.y))
- currNode = currNode.father
- result.append((currNode.x,currNode.y))
- return result
- from astar import astar
- from Map import Map
- mymap = [
- [1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1],
- [1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1]
- ]
- map = Map(mymap,0,1,5,5)
- result = astar(map)
- result.reverse()
- print(result)
A*算法相比于同系列的其它两个算法,有它的优点,但也有其缺点。在一些情况下会出现。
1.当目标不可达时,程序会一直执行。
(这是因为其逻辑,当目标点被处理时,才会停止,否则,一直执行。
所以,当目标不可达时,程序会一直执行,无法跳出。)
2.当起点到终点有障碍物时,A*算法得到的可能不是最佳路径
AStar算法广泛运用于游戏寻路模块中,初看时觉得很神奇,但是经过学习后,其实会发现其实现逻辑并不复杂。
也能感受到其中的魔力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。