python - 需要帮助破解python中的A*(A星)算法
问题描述
import math
import heapq
class Node:
def __init__(self, location, parent, pathCost, goalLocation):
self.location = location
self.parent = parent
self.g = pathCost
if not isinstance(parent, Node):
self.pathValue = pathCost
else:
self.pathValue = parent.g + pathCost
self.h = math.sqrt((location[0] - goalLocation[0]) ** 2 + (location[1] - goalLocation[1]) ** 2)
self.f = self.g + self.h
def __eq__(self, other):
if self.f == other.f:
return True
else:
return False
def __lt__(self, other):
if self.f <= other.f:
return True
else:
return False
def __str__(self):
return "Location: [" + str(self.location[0]) + "," + str(self.location[1]) + "] | MoveValue:" + str(self.g)
def __repr__(self):
return "Location: [" + str(self.location[0]) + "," + str(self.location[1]) + "] | MoveValue:" + str(self.g)
def getNeighbors(location, grid):
neighbors = []
if location[1] != 0 and grid[location[1] - 1][location[0]] != 0:
neighbors.append([location[0], location[1] - 1])
if location[0] != 0 and grid[location[1]][location[0] - 1] != 0:
neighbors.append([location[0] - 1, location[1]])
if location[1] != len(grid[0]) - 1 and grid[location[1] + 1][location[0]] != 0:
neighbors.append([location[0], location[1] + 1])
if location[0] != len(grid[0]) - 1 and grid[location[1]][location[0] + 1] != 0:
neighbors.append([location[0] + 1, location[1]])
return neighbors
def expandNode(node, grid, clist, olist, goalLocation):
neighbors = getNeighbors(node.location, grid)
#print(neighbors)
for n in neighbors:
child = Node(n, node, grid[n[1]][n[0]], goalLocation)
if child in olist:
if child.g < node.g:
heapq.heappush(olist, child)
if child in clist:
clist.remove(child)
elif not child in clist:
heapq.heappush(olist, child)
grid = [[0, 2, 1, 3], #the top right is [3,0] the starting location
[1, 1, 1, 0],
[0, 4, 1, 3],
[1, 1, 1, 1]] #the bottom left is [0,3] the goal location
goalLocation = [0, 3]
startLocation = [3, 0]
firstNode = Node(startLocation, 0, grid[startLocation[1]][startLocation[0]], goalLocation)
olist, clist = [], []
heapq.heappush(olist, firstNode)
for i in range(6):
node = heapq.heappop(olist)
print(node)
print(str(i) + "| Open list: " + str(olist))
print(str(i) + "| Closed list: " + str(clist))
if node.location == goalLocation:
print("Found Goal")
break
clist.append(node)
expandNode(node, grid, clist, olist, goalLocation)
我一直在尝试在 python 中创建一个可以遍历二维列表的简单版本的 A* 算法。我相信算法本身是有效的,但由于某种原因,它不会为我的网格中的位置 [2, 2] 创建 Node 对象。因此,可能无法找到最佳解决方案。如果有人可以看到我遗漏的逻辑错误,或者可以指出算法不正确的原因。
目前,它打印出:
Location: [3,0] | MoveValue:3
0| Open list: []
0| Closed list: []
Location: [2,0] | MoveValue:1
1| Open list: []
1| Closed list: [Location: [3,0] | MoveValue:3]
Location: [2,1] | MoveValue:1
2| Open list: [Location: [1,0] | MoveValue:2]
2| Closed list: [Location: [3,0] | MoveValue:3, Location: [2,0] | MoveValue:1]
Location: [1,1] | MoveValue:1
3| Open list: [Location: [1,0] | MoveValue:2]
3| Closed list: [Location: [3,0] | MoveValue:3, Location: [2,0] | MoveValue:1, Location: [2,1] | MoveValue:1]s
Location: [0,1] | MoveValue:1
4| Open list: [Location: [1,0] | MoveValue:2, Location: [1,2] | MoveValue:4]
4| Closed list: [Location: [3,0] | MoveValue:3, Location: [2,0] | MoveValue:1, Location: [2,1] | MoveValue:1, Location: [1,1] | MoveValue:1]
Location: [1,0] | MoveValue:2
5| Open list: [Location: [1,2] | MoveValue:4]
5| Closed list: [Location: [3,0] | MoveValue:3, Location: [2,0] | MoveValue:1, Location: [2,1] | MoveValue:1, Location: [1,1] | MoveValue:1, Location: [0,1] | MoveValue:1]
解决方案
代码和算法存在几个问题,我将尝试从最严重到最不严重的顺序列出它们:
self.h = math.sqrt((location[0] - goalLocation[0]) ** 2 + (location[1] - goalLocation[1]) ** 2)
- 这是大多数问题的原因。要使 A* 搜索产生正确的输出,您的启发式算法h
需要是可容许的,这意味着它永远不会高估达到目标的成本。您的成本函数是顶点权重的总和,因此列表索引上的欧几里德距离不是可接受的启发式方法。您可以设置h
为 0(这将导致 A* 成为 Dijkstra 算法)或找到图形的特殊属性以获得一致的启发式。
child = Node(n, node, grid[n[1]][n[0]], goalLocation)
if child in olist:
这里的主要问题是相等测试和in
语句。首先,节点的相等性(用于成员资格测试)应该基于图坐标,而不是现在的距离。您可能需要将元组传递(f_value, node)
到最小堆中,而不是以__eq__
一种搞乱区分测试的方式进行定义。此外,使用 aset
而不是列表,否则 A* 的运行时间会变得更糟。
for i in range(6):
应该是while olist
;目前尚不清楚为什么探索的最大节点数硬编码为 6。- 中的第四个 if 语句
getNeighbors
应该以 开头if location[0] != len(grid) - 1
,尽管一旦矩阵不是正方形并且得到IndexError
. 我还将再次建议找到一种方法来避免索引顺序与 Python 的默认顺序相反,因为这些类型的细微错误变得更加常见且更难捕获。 - 我不熟悉
A*
您尝试实现的版本或您正在使用的参考,但我会仔细检查循环是否expandNode
与该参考匹配。我不确定封闭列表的目的是什么,或者这些条件背后的逻辑,所以我无法评论它们的正确性。
推荐阅读
- wpf - 动画用户控件
- javascript - React 中的 Konva 无限网格
- ruby-on-rails - 更新 Amazon RDS SSL/TLS 证书 - Elastic Beanstalk
- kubernetes - 在 Kubernetes 中命名服务会阻止它启动
- c# - 在 Net Core 3.0 中同时使用 GRPC 通道
- javascript - 如何验证/阻止缩短字符串中的 URL
- python - Travis 收集...命令“pytest”以 1 退出
- excel - 范围内的 PowerQuery 范围
- linux - Linux 中给定命令的 CPU 利用率
- python - 创建知道每个列表长度的子列表