Python - Ускорьте алгоритм поиска звездных путей - PullRequest
34 голосов
/ 12 ноября 2010

Я кодировал мой первый слегка сложный алгоритм, реализацию алгоритма A Star Pathfinding .Я следовал советам Python.org по реализации графиков, чтобы в словаре были все узлы, с которыми связан каждый узел.Теперь, поскольку это все для игры, каждый узел на самом деле является просто плиткой в ​​сетке узлов, отсюда и то, как я работаю над эвристикой и моей случайной ссылкой на них.что я могу успешно запустить эту функцию чуть более ста раз в секунду.Понятно, что это делает меня немного неловким, это без каких-либо других «игровых вещей», таких как графика или расчет игровой логики.Поэтому я хотел бы посмотреть, сможет ли кто-нибудь из вас ускорить мой алгоритм, я совершенно не знаком с Cython или его родом, я не могу написать строку кода C.Моя функция A Star.

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 ]

38 голосов
/ 12 ноября 2010

Простая оптимизация заключается в использовании наборов вместо списков для открытых и закрытых наборов.

openSet   = set()
closedSet = set()

Это сделает все тесты in и not in O (1) вместо O ( n ).

9 голосов
/ 12 ноября 2010

Я бы использовал наборы, как было сказано, но я также использовал бы кучу, чтобы найти минимальный элемент (тот, который будет следующим current). Это потребует сохранения как openSet, так и openHeap, но память не должна быть проблемой. Также устанавливает insert в O (1) и кучу в O (log N), поэтому они будут быстрыми. Единственная проблема заключается в том, что модуль heapq не предназначен для использования с ним ключей. Лично я бы просто изменил его, чтобы использовать ключи. Это не должно быть очень сложно. Кроме того, вы можете просто использовать кортежи (tile.H, tile) в куче.

Кроме того, я бы следовал идее 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 []
5 голосов
/ 12 ноября 2010

Как предложено выше, превратить 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 []
...