web-dev-qa-db-fra.com

Python - accélérez un algorithme de fiançailles d'étoile

J'ai codé mon premier algorithme légèrement complexe, une implémentation de l'algorithme n tracé d'étoile . J'ai suivi certains conseils Python.org sur la mise en œuvre de graphiques afin qu'un dictionnaire contienne tous les nœuds que chaque nœud est également lié. Maintenant, comme tout cela est pour une partie, chaque nœud n'est vraiment qu'un tuile dans une grille de nœuds, comment je travaille sur la heuristique et la référence occasionnelle.

Merci au TimeIt, je sais que je peux exécuter cette fonction avec succès un peu plus de cent fois par seconde. Naturellement, cela me rend un peu inquiet, c'est sans aucune autre "affaire de jeu", comme des graphiques ou du calcul de la logique de jeu. J'adorerais donc voir si l'un d'entre vous peut accélérer mon algorithme, je suis complètement inconnu avec Cython ou Kin, je ne peux pas coder une ligne de C.

Sans plus de randonnée, voici ma fonction d'étoile.

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
34
DizzyDoo

Une optimisation facile consiste à utiliser des ensembles au lieu de listes pour les ensembles ouverts et fermés.

openSet   = set()
closedSet = set()

Cela fera tout le tout le in et not in tests O(1) au lieu de O (n).

39
John Kugelman

J'utiliserais les ensembles comme on l'a dit, mais j'utiliserais également un tas pour trouver l'élément minimum (celui qui sera le suivant current). Cela nécessiterait de garder à la fois un Openset et un OpenHeap, mais la mémoire ne devrait pas vraiment être un problème. En outre, définit Insert In O(1) et des tas dans O (journal n) afin qu'ils soient rapides. Le seul problème est que le module HePQ n'est pas vraiment fait pour utiliser des clés avec elle . Personnellement, je voudrais simplement le modifier pour utiliser des clés. Cela ne devrait pas être très difficile. Alternativement, vous pouvez simplement utiliser des tules de (tuile.h, tuile) dans le tas.

En outre, je suivrais l'idée d'Aaronasterling d'utiliser l'itération au lieu de récursivité, mais aussi, je voudrais ajouter des éléments à la fin de path et inverse path à la fin. La raison en est que l'insertion d'un élément à la 0ème place dans une liste est très lente (O (n) je crois), tandis qu'appounfin est O(1) Si je me souviens bien. La finale Le code de cette partie serait:

def retracePath(c):
    path = [c]
    while c.parent is not None:
        c = c.parent
        path.append(c)
    path.reverse()
    return path

Je mettais le chemin de retour à la fin parce qu'il est apparu qu'il devrait de votre code.

Voici le code final en utilisant des ensembles, des tas et de la non:

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 []
10
Justin Peel

Comme suggéré ci-dessus, faites closedSet dans un ensemble.

J'ai essayé de coder openList comme un tas 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

Cependant, vous devez toujours rechercher sur if tile not in openList, alors je ferais ceci:

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 []
5
hughdbrown