Отслеживание тайлов в * поиске

1

Мне сложно отслеживать фрагменты, созданные getAdjacentTiles(..). Я определил, что проблема производительности моей реализации A * ниже заключается в том, что я не отслеживаю фрагменты, которые видели раньше, каждый вызов getAdjacentTiles возвращает новые фрагменты (Node), а не любые фрагменты в openSet или closedSet. Я решил использовать список объектов Node, как и все созданные до сих пор плитки, и передать это getAdjacentTiles чтобы определить, была ли уже getAdjacentTiles плитка.

Моя проблема в том, что я не могу правильно отслеживать эти плитки. Всякий раз, когда мой A * должен принимать более 4 движений, чтобы добраться до end местоположения, он дергается. Который я уверен, имеет отношение к тому, как я пытаюсь отслеживать плитки (снова Node который был посещен). Я должен был бы заподозрить, что проблема связана с моими знаниями о python, мне разрешено делать .apped(tile) как я делаю в getAdjacentTiles(...) при allTiles через набор allTiles?

Вот ссылка на вопрос, который привел меня к этому

Ошибка генерируется (иногда, только когда путь 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
    Можете ли вы распечатать openSet на каждом шаге?
  • 0
    Почему в manHatDist есть заглавная manHatDist H?
Показать ещё 1 комментарий
Теги:
search
a-star

1 ответ

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 не являются самоочевидными. Мы ничего не можем сделать с "экземпляром 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 и сделать посмертное:

>>> 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 из openHeap дважды (потому что кучи могут иметь несколько одинаковых элементов), но его можно удалить openSet один раз из 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(...) от переводчика?" Я запустил интерпретатор и набрал эту строку кода в командной строке интерпретатора. См. Учебник.

  2. "Таким образом, евклидово расстояние всегда будет одним". Да, если вы ищете пути в сетке с равномерной стоимостью, то евклидово расстояние между соседями всегда будет одинаковым.

  3. "Теперь, когда я думаю об этом, curNode.cost - self.manHatDist(curNode.pos, end) всегда равен нулю". Это не так. В вашей реализации cost поискового узла: (i) стоимость достижения этого узла с самого начала, плюс (ii) допустимая оценка стоимости достижения конца от этого узла. Поэтому, если вы вычтите допустимую оценку, вы должны вернуться к (i) снова.

  • 0
    Спасибо, как вы Search().aStar(..) от переводчика? // Вы только что научили меня отладке в python, спасибо. // Теперь я вижу, что вы имели в виду в связанном посте в моем вопросе выше, self.euclidDist(curNode.pos, t.pos) всегда будет равно единице, t всегда и только узел либо NSEW из curNode ; так что евклидово расстояние всегда будет одним. // Теперь я думаю, что думаю об этом curNode.cost - self.manHatDist(curNode.pos, end) всегда равен нулю. Я думаю, что я был сбит с толку эвристикой и допустимостью. //
  • 0
    Смотрите обновленный ответ.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню