在*搜索中跟踪切片

时间:2013-10-02 01:38:17

标签: python search a-star

我很难跟踪getAdjacentTiles(..)生成的切片。我已经确定下面我的A *实现的性能问题是我没有跟踪之前看过的磁贴,每次调用getAdjacentTiles都会返回新磁贴(Node)而不是任何openSetclosedSet中的图块。我决定使用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

1 个答案:

答案 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

因此节点已经关闭。怎么会发生这种情况?好吧,如果节点已添加到openSetopenHeap两次,则可能会发生这种情况。然后它将从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']

对评论的回复

  1. “你是怎么从口译员那里打电话给Search().aStar(...)的?”我运行了解释器,然后在解释器提示符下键入了该行代码。 See the tutorial

  2. “所以欧几里德的距离永远是一个。”是的,如果你在统一成本网格中搜索路径,那么邻居之间的欧几里德距离将始终相同。

  3. “现在我想起来了,curNode.cost - self.manHatDist(curNode.pos, end)总是等于零。”那是不对的。在您的实施中,搜索节点的cost是(i)从一开始就到达该节点的成本,加上(ii)从到达终点的成本的可接受估计那个节点。因此,如果你减去可接受的估计值,那么你应该再次回到(i)。