Apprentissage interactif de la théorie des graphes
Apprentissage interactif de la théorie des graphes
Guest User
Using app without sign in
Visualiseur interactif de recherche de chemin A*
Trouve le plus court chemin plus vite que Dijkstra en orientant la recherche avec une heuristique
Sélectionnez un algorithme et générez les étapes pour commencer la visualisation
A* trouve le chemin le moins coûteux entre deux points d'un graphe pondéré et c'est l'algorithme derrière la recherche de chemin dans la plupart des jeux et des robots. C'est Dijkstra avec un ajout : une estimation de la distance restante entre chaque nœud et l'arrivée, qui pousse la recherche vers la destination au lieu de s'étendre uniformément dans toutes les directions. Hart, Nilsson et Raphael l'ont publié en 1968.
Chaque nœud porte trois nombres : g, le coût confirmé depuis le départ ; h, le coût estimé restant ; et f = g + h, le total estimé. A* tient un ensemble ouvert de nœuds découverts et développe toujours celui dont le f est le plus petit. Développer signifie le faire passer dans l'ensemble fermé et relâcher ses arêtes exactement comme Dijkstra. La recherche s'arrête dès que l'arrivée est développée. Avec un tas binaire, cela s'exécute en O((V + E) log V), la même borne que Dijkstra, mais en touchant en général bien moins de nœuds.
A* est le chercheur de chemin par défaut dans les moteurs de jeu, les robots d'entrepôt et la navigation de drones, et il guide la planification d'itinéraires quand la distance à vol d'oiseau fournit une borne inférieure. Il résout aussi les taquins et d'autres recherches dans des espaces d'états dotés d'une bonne heuristique. En entretien, il suit naturellement Dijkstra : la question porte le plus souvent sur la propriété que doit avoir l'heuristique pour que la réponse reste optimale.
A* est Dijkstra avec un terme supplémentaire. Là où Dijkstra développe toujours le sommet au plus petit coût confirmé g, A* développe celui au plus petit f = g + h, où h estime le coût restant. Mettez h à zéro partout et le pseudocode ci-dessous devient exactement Dijkstra.
A*(graphe, depart, arrivee, h):
pour chaque sommet v: g[v] = infini
g[depart] = 0
f[depart] = h(depart)
ouverts = file de priorité contenant (f[depart], depart)
fermes = ensemble vide
tant que ouverts est non vide:
u = ouverts.extraireMin() // plus petit f
si u == arrivee: retourner reconstruire(u)
ajouter u à fermes
pour chaque arête (u, v, w):
si v dans fermes: continuer
tentative = g[u] + w
si tentative < g[v]:
precedent[v] = u
g[v] = tentative
f[v] = tentative + h(v)
ouverts.inserer(f[v], v)
retourner aucun cheminIgnorer les sommets fermés à la ligne 15 n'est sûr que si h est cohérente, c'est-à-dire h(u) <= w(u, v) + h(v) pour toute arête. Avec une heuristique seulement admissible, il faut autoriser les sommets à quitter l'ensemble fermé et à être rouverts, sinon A* peut renvoyer un chemin non optimal. Le visualiseur ci-dessus multiplie la distance à vol d'oiseau par le coût par unité le plus bas qu'offre une arête, ce qui rend h cohérente par l'inégalité triangulaire, et aucune réouverture n'est alors nécessaire.
Cinq sommets, S à l'origine et l'arrivée G cinq unités à sa droite. L'intérêt de la trace, c'est le sommet que A* ne touche jamais.
Graphe d'exemple: S(0,0), A(2,1), B(2,-1), C(1,4), G(5,0). Arêtes S-A = 3, S-B = 2, S-C = 4, A-B = 2, A-G = 4, B-G = 6, C-G = 7. En multipliant la distance à vol d'oiseau par le coût par unité le plus bas d'une arête (0,894), on obtient h(S) = 4,47, h(A) = 2,83, h(B) = 2,83, h(C) = 5,06, h(G) = 0.
A* renvoie S vers A vers G pour un coût de 7, après avoir développé 4 sommets. Dijkstra sur le même graphe renvoie le même chemin au même coût, mais en développe 5 : il traite C avant d'accepter de fixer l'arrivée. C ne méritait pas la visite, et c'est h qui a permis à A* de le savoir sans vérifier.
Temps: O((V + E) log V) au pire avec un tas binaire · Espace: O(V)
Le pire cas est celui de Dijkstra, et pour la même raison : chaque sommet peut entrer une fois dans la file de priorité et chaque arête peut déclencher une diminution de clé, soit V extractions et E mises à jour en O(log V) chacune. L'heuristique ne change rien à cette borne. Elle change la constante : les sommets dont le f dépasse le coût final de l'arrivée ne sont jamais développés. Avec h = 0, A* dégénère exactement en Dijkstra ; avec un h parfait, il suit directement le chemin optimal. Sur 4000 graphes pondérés générés aléatoirement, l'implémentation ci-dessus a développé 4,58 sommets en moyenne contre 5,52 pour Dijkstra, et a renvoyé le coût optimal à chaque fois.
A* ne vaut sa mécanique supplémentaire que si vous avez une arrivée et une estimation utilisable de la distance qui vous en sépare. À défaut de l'une ou l'autre, l'un de ces algorithmes est le meilleur outil.
| Alternative | À préférer quand | Coût |
|---|---|---|
| Algorithme de Dijkstra | Vous voulez les plus courts chemins vers tous les sommets, ou vous n'avez aucune heuristique pertinente. A* avec h = 0, c'est exactement cela. | O((V + E) log V) |
| Parcours en largeur | Toutes les arêtes coûtent la même chose. BFS trouve la même réponse sans aucune file de priorité. | O(V + E) |
| Bellman-Ford | Certains poids sont négatifs. A* hérite de l'hypothèse de poids positifs de Dijkstra et échoue ici. | O(V * E) |
| A* bidirectionnel | Très grands graphes avec un seul départ et une seule arrivée. Chercher des deux côtés divise à peu près par deux la région explorée. | O((V + E) log V) |
| A* pondéré (f = g + w*h) | Vous échangez l'optimalité contre la vitesse. Avec w > 1, les chemins arrivent plus vite mais le coût n'est garanti qu'à un facteur w près. | O((V + E) log V) |
Lire l'article complet: A* Search Algorithm: Step-by-Step Guide
Lire l'article complet: A* Search Algorithm in AI: The Complete Guide
Algorithmes associés: Algorithme de Dijkstra, Recherche en Largeur (BFS), Algorithme de Bellman-Ford