Python-ускорить алгоритм поиска звезд
я закодировал свой первый слегка сложный алгоритм, реализацию Звезда Первопрохождения. Я последовал за некоторыми Python.org совет при реализации графиков, поэтому словарь содержит все узлы, каждый узел также связан. Теперь, поскольку все это для игры, каждый узел-это просто плитка в сетке узлов, поэтому я разрабатываю эвристику и мою случайную ссылку на них.
благодаря timeit я знаю, что могу запустить это успешно функционирует чуть более ста раз в секунду. Понятно, что это меня немного беспокоит, это без каких-либо других "игровых вещей", таких как графика или вычислительная логика игры. Поэтому я хотел бы посмотреть, может ли кто-нибудь из вас ускорить мой алгоритм, я совершенно не знаком с Cython или это kin, я не могу закодировать строку C.
без каких-либо больше бессвязных, вот моя звездная функция.
def aStar(self, graph, current, end):
openList = []
closedList = []
path = []
def retracePath(c):
path.insert(0,c)
if c.parent == None:
return
retracePath(c.parent)
openList.append(current)
while len(openList) is not 0:
current = min(openList, key=lambda inst:inst.H)
if current == end:
return retracePath(current)
openList.remove(current)
closedList.append(current)
for tile in graph[current]:
if tile not in closedList:
tile.H = (abs(end.x-tile.x)+abs(end.y-tile.y))*10
if tile not in openList:
openList.append(tile)
tile.parent = current
return path
3 ответов
простая оптимизация заключается в использовании наборов вместо списков для открытых и закрытых наборов.
openSet = set()
closedSet = set()
Это все in
и not in
тесты O (1) вместо O (n).
Я бы использовал наборы, как было сказано, но я бы также использовал кучу, чтобы найти минимальный элемент (тот, который будет следующим current
). Это потребует сохранения как openSet, так и openHeap, но память не должна быть проблемой. Кроме того, наборы вставляют в O(1) и кучи в O(log N), чтобы они были быстрыми. Единственная проблема заключается в том, что модуль heapq не использовать ключи с ним. Лично я бы просто изменил его, чтобы использовать ключи. Это не должно быть очень сложно. Кроме того, вы можно просто использовать кортежи (tile.H, плитка) в куче.
кроме того, я бы последовал идее aaronasterling об использовании итерации вместо рекурсии, но также я бы добавил элементы в конец path
и обратный path
В конце. Причина в том, что вставка элемента на 0-м месте в списке очень медленная (O(N), я считаю), а добавление-O(1), Если я правильно помню. Окончательный код для этой части будет:
def retracePath(c):
path = [c]
while c.parent is not None:
c = c.parent
path.append(c)
path.reverse()
return path
я поставил обратный путь в конце, потому что оказалось, что это должно быть из вашего кода.
вот окончательный код с использованием наборов, куч и чего нет:
import heapq
def aStar(graph, current, end):
openSet = set()
openHeap = []
closedSet = set()
def retracePath(c):
path = [c]
while c.parent is not None:
c = c.parent
path.append(c)
path.reverse()
return path
openSet.add(current)
openHeap.append((0, current))
while openSet:
current = heapq.heappop(openHeap)[1]
if current == end:
return retracePath(current)
openSet.remove(current)
closedSet.add(current)
for tile in graph[current]:
if tile not in closedSet:
tile.H = (abs(end.x - tile.x)+abs(end.y-tile.y))*10
if tile not in openSet:
openSet.add(tile)
heapq.heappush(openHeap, (tile.H, tile))
tile.parent = current
return []
как было предложено выше, сделайте closedSet
в набор.
Я пытался кодировать openList
вляпались import heapq
:
import heapq
def aStar(self, graph, current, end):
closedList = set()
path = []
def retracePath(c):
path.insert(0,c)
if c.parent == None:
return
retracePath(c.parent)
openList = [(-1, current)]
heapq.heapify(openList)
while openList:
score, current = openList.heappop()
if current == end:
return retracePath(current)
closedList.add(current)
for tile in graph[current]:
if tile not in closedList:
tile.H = (abs(end.x-tile.x)+abs(end.y-tile.y))*10
if tile not in openList:
openList.heappush((tile.H, tile))
tile.parent = current
return path
тем не менее, вам все равно нужно искать в if tile not in openList
, поэтому я бы сделал так:
def aStar(self, graph, current, end):
openList = set()
closedList = set()
def retracePath(c):
def parentgen(c):
while c:
yield c
c = c.parent
result = [element for element in parentgen(c)]
result.reverse()
return result
openList.add(current)
while openList:
current = sorted(openList, key=lambda inst:inst.H)[0]
if current == end:
return retracePath(current)
openList.remove(current)
closedList.add(current)
for tile in graph[current]:
if tile not in closedList:
tile.H = (abs(end.x-tile.x)+abs(end.y-tile.y))*10
openList.add(tile)
tile.parent = current
return []