【问题标题】:Python - Speed up an A Star Pathfinding AlgorithmPython - 加速 A Star 寻路算法
【发布时间】:2011-05-08 17:49:56
【问题描述】:

我编写了我的第一个稍微复杂的算法,即A Star Pathfinding 算法的实现。我在实现图表时遵循了一些Python.org advice,因此字典包含每个节点也链接的所有节点。现在,由于这一切都是为了游戏,每个节点实际上只是节点网格中的一个图块,因此我正在制定启发式方法并偶尔引用它们。

感谢 timeit,我知道我可以每秒成功运行此功能一百多次。可以理解的是,这让我有点不安,因为没有任何其他“游戏内容”,比如图形或计算游戏逻辑。所以我很想看看你们中是否有人可以加快我的算法,我完全不熟悉 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

【问题讨论】:

  • while len(openList) is not 0: 让我畏缩......while openlist: 也一样。
  • return retracePath(current)这行不正确(我认为),你应该调用retracePath(current),然后return path当前如果找到结束节点,它返回None

标签: python algorithm performance a-star


【解决方案1】:

一个简单的优化是使用集合而不是列表来表示开集和闭集。

openSet   = set()
closedSet = set()

这将使所有 innot in 测试 O(1) 而不是 O(n)。

【讨论】:

  • 感谢约翰,您第二次快速提供了有用的答案。我已经实现了下面的 heapq 建议,但它使用的集合确实节省了最多的时间(几乎三分之一!)。
【解决方案2】:

我会使用前面所说的集合,但我也会使用堆来查找最小元素(即下一个current)。这需要同时保留一个 openSet 和一个 openHeap,但内存应该不是问题。此外,在 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 []

【讨论】:

  • @hughdbrown,是的,你不是唯一一个想到它的人(在寻路中非常标准)。我确实看到你也想过,但我并没有真正看你的实现。就个人而言,我认为应该使用堆,并且应该用属性标记图块,以指示每个图块是否已被访问,而不是使用集合。但是,如果不这样做,我使用集合和堆的实现是最有意义的。此外,如果他计划在这些图块上(不同的点之间)进行多次运行,那么他必须在每次之后重置访问的属性。
  • 我不明白堆是如何工作的:heapq.heappush((tile.H,tile)) 行不应该是heapq.heappush(openHeap,(tile.H,tile)) ?!
  • 是的,你说的很对。我不知道我是怎么错过的,但话说回来,距离我发布这个已经快一年了。
  • 是的,但我有这个«问题»,您的解决方案引起了我的注意。感谢您的回复。
  • 我确实修复了它,所以我希望答案有所帮助。
【解决方案3】:

如上所述,将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 []

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    相关资源
    最近更新 更多