当前位置:   article > 正文

A*算法的实现(Python)_python a*

python a*

前言

关于A*算法的实现是很早之前的一次开发中的成果,并做了一些改进。当然,在这里就不记录改进部分了,因为其中还有一些争议。这里仅是对A*算法的理解和使用Python实现。


参考链接

之所以放在前面,是因为这些链接的参考价值特别高,如果希望获得更多的了解,可以通过以下链接进行学习。

英文网站

A* Pathfinding for Beginners - Artificial Intelligence - Tutorials - GameDev.net

redblobgames(红色斑点游戏)

Red Blob Games

中文网站

csdn:A星算法详解(个人认为最详细,最通俗易懂的一个版本)|模块 A*算法总结 非常有参考价值

A星算法详解(个人认为最详细,最通俗易懂的一个版本)_Colin丶-CSDN博客_a星算法

知乎:路线规划之A*算法

路径规划之 A* 算法 - 知乎


时间线

2021.03.25 优化

2021.11.03权重优化

定义

(百度百科)A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快

一个真正的A*算法必须包含以下对象:开启列表、关闭列表、G、H


分析

估价函数F(n)=G(n)+H(n)

  • G(n)

G(n)表示的是从起始节点到当前节点的距离代价。

在A*算法中,这是一个确切但可变的数值

可变是因为从起始节点到当前节点的不同移动方案,其距离代价也是不同的

确切是一旦移动方案的确定,其距离代价也确定了

例如以下的

如果从节点A出发,那么选择方案1的话,G(n)=30,这是确切的

如果有更好的移动方案,如这里的方案3的话,则G(n)=/2*10,这是可变的

(假设一个正方形的大小为10)

  • H(n)

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种距离计算方式


步骤

详细步骤

  1. 创建开启列表、关闭列表
  2. 将起始点加入开放列表中
  3. 弹出开启列表的第一个节点,将该元素作为当前节点
  4. while(当前节点不为终点时):
  5. 获取并处理当前节点的周围可通过的节点,
  6. 为这些节点设置G值、H值,
  7. 添加当前节点为这些可通过节点的父节点
  8. 遍历当前节点的周围节点
  9. if(该节点可通过且不在关闭列表):
  10. if(该节点在开启列表):
  11. 根据 估价函数F(n)进行比较
  12. if(开启列表的节点的估价函数大于该节点的估价函数):
  13. 将开启列表的节点进行覆盖
  14. else:
  15. 将该节点加入开启列表
  16. 将当前节点添加到关闭列表中
  17. 对开启列表进行排序
  18. 当前节点<-获得开启列表的第一个元素
  19. 创建一个路径结果集
  20. while(当前节点的父节点不为空):
  21. 获得当前节点的索引,存入路径结果集中
  22. 当前节点<-当前节点的父节点
  23. 返回路径结果集
  24. 结束

细节处理

  • 关键字概念

父节点、邻接节点

  • 对象

地图(地图数据,起点,终点)

节点(包含H、G,父节点)

开启列表

关闭列表

  • 主要处理

1.获得当前节点的可用的邻居节点

2.将邻接节点插入开启列表中

3.对开启列表进行排序处理(根据G(n)值)

4.查找一个节点是否在开启列表中

注:可以将2与3进行整合,在插入时同时实现排序处理

代码

  • 地图对象Map

  1. import math
  2. '''
  3. 对象Map,主要有地图数据、起点和终点
  4. '''
  5. class Map(object):
  6. def __init__(self,mapdata,startx,starty,endx,endy):
  7. self.data = mapdata
  8. self.startx = startx
  9. self.starty = starty
  10. self.endx = endx
  11. self.endy = endy
  • 节点Node

  1. '''
  2. Node.py主要是描述对象Node
  3. '''
  4. class Node(object):
  5. '''
  6. 初始化节点信息
  7. '''
  8. def __init__(self,x,y,g,h,father):
  9. self.x = x
  10. self.y = y
  11. self.g = g
  12. self.h = h
  13. self.father = father
  14. '''
  15. 处理边界和障碍点
  16. '''
  17. def getNeighbor(self,mapdata,endx,endy):
  18. x = self.x
  19. y = self.y
  20. result = []
  21. #先判断是否在上下边界
  22. #if(x!=0 or x!=len(mapdata)-1):
  23. #上
  24. #Node(x,y,g,h,father)
  25. if(x!=0 and mapdata[x-1][y]!=0):
  26. upNode = Node(x-1,y,self.g+10,(abs(x-1-endx)+abs(y-endy))*10,self)
  27. result.append(upNode)
  28. #下
  29. if(x!=len(mapdata)-1 and mapdata[x+1][y]!=0):
  30. downNode = Node(x+1,y,self.g+10,(abs(x+1-endx)+abs(y-endy))*10,self)
  31. result.append(downNode)
  32. #左
  33. if(y!=0 and mapdata[x][y-1]!=0):
  34. leftNode = Node(x,y-1,self.g+10,(abs(x-endx)+abs(y-1-endy))*10,self)
  35. result.append(leftNode)
  36. #右
  37. if(y!=len(mapdata[0])-1 and mapdata[x][y+1]!=0):
  38. rightNode = Node(x,y+1,self.g+10,(abs(x-endx)+abs(y+1-endy))*10,self)
  39. result.append(rightNode)
  40. #西北 14
  41. if(x!=0 and y!=0 and mapdata[x-1][y-1]!=0 ):
  42. wnNode = Node(x-1,y-1,self.g+14,(abs(x-1-endx)+abs(y-1-endy))*10,self)
  43. result.append(wnNode)
  44. #东北
  45. if(x!=0 and y!=len(mapdata[0])-1 and mapdata[x-1][y+1]!=0 ):
  46. enNode = Node(x-1,y+1,self.g+14,(abs(x-1-endx)+abs(y+1-endy))*10,self)
  47. result.append(enNode)
  48. #西南
  49. if(x!=len(mapdata)-1 and y!=0 and mapdata[x+1][y-1]!=0 ):
  50. wsNode = Node(x+1,y-1,self.g+14,(abs(x+1-endx)+abs(y-1-endy))*10,self)
  51. result.append(wsNode)
  52. #东南
  53. if(x!=len(mapdata)-1 and y!=len(mapdata[0])-1 and mapdata[x+1][y+1]!=0 ):
  54. esNode = Node(x+1,y+1,self.g+14,(abs(x+1-endx)+abs(y+1-endy))*10,self)
  55. result.append(esNode)
  56. # #如果节点在关闭节点 则不进行处理
  57. # finaResult = []
  58. # for i in result:
  59. # if(i not in lockList):
  60. # finaResult.append(i)
  61. # result = finaResult
  62. return result
  63. def hasNode(self,worklist):
  64. for i in worklist:
  65. if(i.x==self.x and i.y ==self.y):
  66. return True
  67. return False
  68. #在存在的前提下
  69. def changeG(self,worklist):
  70. for i in worklist:
  71. if(i.x==self.x and i.y ==self.y):
  72. if(i.g>self.g):
  73. i.g = self.g
  • 核心逻辑astar

  1. from Node import Node
  2. def getKeyforSort(element:Node):
  3. return element.g #element#不应该+element.h,否则会穿墙
  4. def astar(workMap):
  5. startx,starty = workMap.startx,workMap.starty
  6. endx,endy = workMap.endx,workMap.endy
  7. startNode = Node(startx, starty, 0, 0, None)
  8. openList = []
  9. lockList = []
  10. lockList.append(startNode)
  11. currNode = startNode
  12. while((endx,endy) != (currNode.x,currNode.y)):
  13. workList = currNode.getNeighbor(workMap.data,endx,endy)
  14. for i in workList:
  15. if (i not in lockList):
  16. if(i.hasNode(openList)):
  17. i.changeG(openList)
  18. else:
  19. openList.append(i)
  20. openList.sort(key=getKeyforSort)#关键步骤
  21. currNode = openList.pop(0)
  22. lockList.append(currNode)
  23. result = []
  24. while(currNode.father!=None):
  25. result.append((currNode.x,currNode.y))
  26. currNode = currNode.father
  27. result.append((currNode.x,currNode.y))
  28. return result
  • 运行run

  1. from astar import astar
  2. from Map import Map
  3. mymap = [
  4. [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],
  5. [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],
  6. [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],
  7. [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],
  8. [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],
  9. [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],
  10. [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],
  11. [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],
  12. [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]
  13. ]
  14. map = Map(mymap,0,1,5,5)
  15. result = astar(map)
  16. result.reverse()
  17. print(result)

缺陷

A*算法相比于同系列的其它两个算法,有它的优点,但也有其缺点。在一些情况下会出现。

1.当目标不可达时,程序会一直执行。

(这是因为其逻辑,当目标点被处理时,才会停止,否则,一直执行。

所以,当目标不可达时,程序会一直执行,无法跳出。)

2.当起点到终点有障碍物时,A*算法得到的可能不是最佳路径

总结

AStar算法广泛运用于游戏寻路模块中,初看时觉得很神奇,但是经过学习后,其实会发现其实现逻辑并不复杂。

也能感受到其中的魔力。

补充

  • 有位读者说怎么运行,可以像我这样保存文件,最后直接运行即可。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/561086
推荐阅读
相关标签
  

闽ICP备14008679号