我很难跟踪getAdjacentTiles(..)
生成的切片。我已经确定下面我的A *实现的性能问题是我没有跟踪之前看过的磁贴,每次调用getAdjacentTiles
都会返回新磁贴(Node
)而不是任何openSet
或closedSet
中的图块。我决定使用Node对象列表作为到目前为止创建的所有切片,并将其传递给getAdjacentTiles
以确定它生成的切片是否已被访问过。
我的问题是,我似乎无法正确跟踪这些瓷砖。每当我的A *需要超过大约4个动作才能到达end
位置时,它就会变形。我肯定与我如何跟踪瓷砖(再次Node
已经访问过的瓷砖有关)我不得不怀疑问题是我对python的了解,我允许循环浏览.apped(tile)
时,像getAdjacentTiles(...)
一样allTiles
吗?
Here's a link to the question that led me to this one
产生错误(有时,仅当A *路径长于约3步时...)
File "P3.py", line 67, in aStar
openSet.remove(curNode)
KeyError: <__main__.Node instance at 0xa39edcc>
来源
#Perform an A* search to find the best path to the dirt
def aStar(self, current, end):
openSet = set()
openHeap = []
closedSet = set()
allTiles = set()
curNode = Node(0, current, self.manHatDist(current, end))
openSet.add(curNode)
allTiles.add(curNode)
openHeap.append((curNode.cost,curNode))
while openSet:
curNode = heapq.heappop(openHeap)[1]
if curNode.pos == end:
return self.getDirections(curNode)
openSet.remove(curNode)
closedSet.add(curNode)
adjNodes = self.getAdjacentNodes(curNode.pos, allTiles)
for tile in adjNodes:
t = tile
if t not in closedSet:
cost = (curNode.cost - self.manHatDist(curNode.pos, end)
+ self.euclidDist(curNode.pos, current)
+ self.manHatDist(t.pos, end))
if t not in openSet or cost < t.cost:
t.parent = curNode
t.cost = cost
openSet.add(t)
heapq.heappush(openHeap, (cost,t))
allTiles.add(t)
return []
#Get the moves made to get to this endNode
def getDirections(self, endNode):
moves = []
tmpNode = endNode
while tmpNode.parent is not None:
moves.append(tmpNode.value)
tmpNode = tmpNode.parent
moves.reverse()
return moves
#Return all possible moves from given tile as Node objects
def getAdjacentNodes(self, curPos, allTiles):
allMoves = ['North','South','East','West']
posMoves = []
for direction in allMoves:
if(self.canMove(direction, curPos)):
posMoves.append(Node(direction, self.getLocIfMove(curPos, direction)))
retNodes = []
for posLocNode in posMoves:
set = False
for tile in allTiles:
if(posLocNode.pos == tile.pos):
set = True
retNodes.append(tile)
if(not set):
retNodes.append(posLocNode)
return retNodes
答案 0 :(得分:0)
让我们启动交互式解释器,看看我们能找到什么。 (你没有在问题中给出你班级的名字,所以我称之为Search
。)
>>> Search().aStar((0,0), (2,2))
Traceback (most recent call last):
...
File "q19128695.py", line 25, in aStar
openSet.remove(curNode)
KeyError: <__main__.Node instance at 0x104895518>
好的,第一个问题是这些Node
实例不是不言自明的。我们无法对“节点实例位于0x104895518”做任何事情,所以让我们在__repr__
类中添加Node
方法:
def __repr__(self):
return 'Node({0.value}, {0.pos}, {0.cost})'.format(self)
再试一次:
>>> Search().aStar((0,0), (2,2))
Traceback (most recent call last):
...
File "q19128695.py", line 25, in aStar
openSet.remove(curNode)
KeyError: Node(East, (1, 2), 3.41421356237)
好的,这样更有用。让我们启动Python debugger并执行postmortem:
>>> import pdb
>>> pdb.pm()
> q19128695.py(25)aStar()
-> openSet.remove(curNode)
(Pdb) openSet
set([Node(North, (2, -1), 6.0), Node(East, (2, 2), 4.65028153987),
Node(West, (-1, 1), 5.0), Node(North, (0, -1), 5.0),
Node(South, (1, 3), 6.65028153987), Node(South, (0, 3), 6.0),
Node(East, (3, 0), 6.0), Node(West, (-1, 0), 5.0),
Node(North, (1, -1), 5.0), Node(East, (3, 1), 6.65028153987),
Node(West, (-1, 2), 6.0)])
(Pdb) closedSet
set([Node(0, (0, 0), 4), Node(South, (2, 1), 3.41421356237),
Node(East, (1, 1), 3.0), Node(South, (0, 1), 3.0),
Node(East, (2, 0), 3.0), Node(East, (1, 0), 3.0),
Node(East, (1, 2), 3.41421356237), Node(South, (0, 2), 3.0)])
(Pdb) curNode
Node(East, (1, 2), 3.41421356237)
(Pdb) curNode in closedSet
True
因此节点已经关闭。怎么会发生这种情况?好吧,如果节点已添加到openSet
和openHeap
两次,则可能会发生这种情况。然后它将从openHeap
弹出两次(因为堆可以有多个相同的项),但只能从openSet
删除一次。有问题的代码如下所示:
if t not in openSet or cost < t.cost:
t.parent = curNode
t.cost = cost
openSet.add(t)
heapq.heappush(openHeap, (cost,t))
问题的第一个问题是,即使您遇到麻烦而无法提供(cost, t)
个对象Node
和__lt__
方法,也可以推送对__gt__
。相反,只需将t
推入堆:
heapq.heappush(openHeap, t)
这需要在其他地方进行一些更改:而不是
openHeap.append((curNode.cost,curNode))
while openSet:
curNode = heapq.heappop(openHeap)[1]
你必须写
openHeap = [curNode]
while openSet:
curNode = heapq.heappop(openHeap)
现在,第二个问题(我的错 - 很抱歉)是,如果t
已经在openSet
中,那么我们不应该再次将它添加到堆中。相反,我们应该重新堆积:
t_open = t in openSet
if not t_open or cost < t.cost:
t.parent = curNode
t.cost = cost
if t_open:
heapq.heapify(openHeap)
else:
openSet.add(t)
heapq.heappush(openHeap, t)
回到调试器输出,回想一下:
(Pdb) curNode
Node(East, (1, 2), 3.41421356237)
那3.41421356237
应该让你担心:成本总是不应该是整数吗?看起来成本计算仍然是错误的。它说:
cost = (curNode.cost
- self.manHatDist(curNode.pos, end)
+ self.euclidDist(curNode.pos, current)
+ self.manHatDist(t.pos, end))
但第三行应该说:
+ self.euclidDist(curNode.pos, t.pos)
所以,有了所有这些修复,让我们再试一次:
>>> Search().aStar((0,0), (2,2))
['North', 'North', 'East', 'East']
“你是怎么从口译员那里打电话给Search().aStar(...)
的?”我运行了解释器,然后在解释器提示符下键入了该行代码。 See the tutorial
“所以欧几里德的距离永远是一个。”是的,如果你在统一成本网格中搜索路径,那么邻居之间的欧几里德距离将始终相同。
“现在我想起来了,curNode.cost - self.manHatDist(curNode.pos, end)
总是等于零。”那是不对的。在您的实施中,搜索节点的cost
是(i)从一开始就到达该节点的成本,加上(ii)从到达终点的成本的可接受估计那个节点。因此,如果你减去可接受的估计值,那么你应该再次回到(i)。