LorisVogue08-00

publicité
Recherche de chemins
de coût minimal
avec l’algorithme A*
Mise en œuvre pratique
Olivier NOCENT
IUT de Reims-Châlons-Charleville
rue des crayères, BP 1035
51687 Reims Cedex 2
Introduction
Objectif : Déterminer, pour un agent* donné, un
chemin de coût minimum depuis un sommet
source vers un sommet destination au sein d’un
graphe orienté.
Un agent est un objet informatique autonome
utilisé pour représenter une entité mobile dotée
d’un comportement (humain, animal, véhicule,
…)
Applications
 Jeux vidéo
 Animation des personnages non joueurs (RPG, FPS)
 Déplacement réaliste d’un personnage contrôlé par le
joueur vers un objectif désigné par le joueur (RTS)
 Simulation – vie artificielle
 Etude du comportement d’une foule, du traffic
automobile, …
 Effets spéciaux (scènes de bataille, …)
Représentation du graphe à partir
d’informations topographiques
Relations d’adjacence : grille carrée
Prairie
Rivière
Pont
Relations d’adjacence : grille hexagonale
Prairie
Rivière
Pont
Relations d’adjacence : points visibles
Obstacles
Couloirs
Coût des arcs
Signification du coût d’un arc :
 Distance kilométrique
 Recherche de chemins de longueur minimale
 Temps (nécessaire au franchissement de l’arc)
 Recherche de chemins en temps minimum
 Consommation de carburant
 Rechercher de chemins « économes »
Coût des arcs : grille carrée
10
10
10
10
Coût des arcs : grille hexagonale
10
10
10
10
10
10
Triangle équilatéral
10
10
10
10
10
10
Coût des arcs : pondération en fonction
de la nature de l’environnement
10
10
10
10
80
40
10
Montagne
80
80
10
10
40
80
40
Prairie
40
40
40
10
10
40
40
10
10
10
Coût des arcs : pondération en fonction
de la nature de l’agent
Coût du franchissement d’un pont
C
C
C = 10
pour un humain.
C = 50
pour une voiture.
C = 500
pour un semi-remorque.
Algorithme A*
Principe général : évaluation du coût
total d’un sommet
Coût total (F) = Coût depuis la source (G) +
Coût vers la destination (H)
 G : Coût depuis la source
 Algorithmes classiques (Ford, Bellman, Dijkstra)
 Gi = min Gj + Cij / i prédecesseur de j
Cij coût de l’arc (i,j)
 H : Coût vers la destination
 Difficile puisque le reste du chemin (vers la
destination) est encore inconnu.
Coût vers la destination
Pourquoi évaluer un coût vers la destination ?
Afin de resserrer l’ensemble des sommets à explorer en
privilégiant les sommets « qui semblent » nous
rapprocher de la destination.
Remarque
Dans le cas d’un algorithme de recherche plus classique
(Dijsktra), on effectue une recherche exhaustive parmi
TOUS les sommets.
Conséquence
l’algorithme A* est plus performant que n’importe quel
autre algorithme puisqu’il diminue l’ensemble des
sommets à explorer.
Coût vers la destination
Comment évaluer un coût vers la destination ?
En utilisant des heuristiques (prédictions) afin d’évaluer
un coût vers la destination INFERIEUR au coût réel
(encore inconnu).
A ce titre, A* est un algorithme optimiste.
Remarque
Si l’heuristique était supérieur au coût réel, on risquerait
de générer un chemin qui ne soit pas minimal.
Distance euclidienne
S
Théorème de Pythagore
40
H
H 2 = (Coté oppose) 2 +
20
D
(Coté adjacent) 2
H 2 = 40 2 + 20 2
= 2000
H = 20 x (5) 1/2
Distance de Manhattan
Nombre de cellules, en horizontal
et en vertical entre la source et la
destination.
S
D
Plus conforme à la nature des
déplacements autorisés (haut, bas,
gauche, droite)
Algorithme A*
Initialisation
Sommet source (S)
Sommet destination (D)
Liste des sommets à explorer (E) : sommet source S
Liste des sommets visités (V) : vide
Tant que (la liste E est non vide) et (D n’est pas dans E) Faire
+ Récupérer le sommet X de coût total F minimum.
+ Ajouter X à la liste V
+ Ajouter les successeurs de X (non déjà visités) à la liste E en
évaluant leur coût total F et en identifiant leur prédécesseur.
+ Si (un successeur est déjà présent dans E) et
(nouveau coût est inférieur à l’ancien) Alors
Changer son coût total
Changer son prédécesseur
FinSi
FinFaire
Exemple 1
S Sommet source
D
D
Sommet destination
Obstacle
S
Exemple 1
Sommet déjà visité
10 + 30
10 + 50
S
Sommet à explorer
D
Coût depuis
la source
Coût vers
la destination
G+H
10 + 50
Référence au
prédécesseur
Exemple 1
20 + 40
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
10 + 50
Référence au
prédécesseur
Exemple 1
20 + 40
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
Référence au
prédécesseur
Exemple 1
20 + 40
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
20 + 60
Référence au
prédécesseur
Exemple 1
30 + 50 20 + 40
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
20 + 60
Référence au
prédécesseur
Exemple 1
30 + 50 20 + 40 30 + 30
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
20 + 60
Référence au
prédécesseur
Exemple 1
30 + 50 20 + 40 30 + 30 40 + 20
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
20 + 60
Référence au
prédécesseur
Exemple 1
30 + 50 20 + 40 30 + 30 40 + 20 50 + 10
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
50 + 10
10 + 50
S
D
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
20 + 60
Référence au
prédécesseur
Exemple 1
30 + 50 20 + 40 30 + 30 40 + 20 50 + 10
Sommet déjà visité
20 + 40 10 + 30
Sommet à explorer
60 + 0
50 + 10
10 + 50
S
60 + 20
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60 10 + 50
20 + 60
Référence au
prédécesseur
Exemple 1
D
S
Exemple 2
S Sommet source
D
D
Sommet destination
Obstacle
S
Exemple 2
Sommet déjà visité
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
Référence au
prédécesseur
Exemple 2
Sommet déjà visité
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
Référence au
prédécesseur
Exemple 2
Sommet déjà visité
Sommet à explorer
20 + 40
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
30 + 70
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60
30 + 70
40 + 80
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60 50 + 50
30 + 70
40 + 80 50 + 70
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60 50 + 50 60 + 40
30 + 70
40 + 80 50 + 70 60 + 60
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60 50 + 50 60 + 40 70 + 30
30 + 70
40 + 80 50 + 70 60 + 60 70 + 50
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
80 + 20
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60 50 + 50 60 + 40 70 + 30
30 + 70
40 + 80 50 + 70 60 + 60 70 + 50 80 + 40
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
Sommet à explorer
D
10 + 50
S
10 + 30
90 + 10
80 + 20
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60 50 + 50 60 + 40 70 + 30
30 + 70
40 + 80 50 + 70 60 + 60 70 + 50 80 + 40
Référence au
prédécesseur
Exemple 2
30 + 50
Sommet déjà visité
20 + 40
10 + 50
100 + 0
S
10 + 30
90 + 10
80 + 20
Sommet à explorer
Coût depuis
la source
Coût vers
la destination
G+H
20 + 60
40 + 60 50 + 50 60 + 40 70 + 30
30 + 70
40 + 80 50 + 70 60 + 60 70 + 50 80 + 40
Référence au
prédécesseur
Exemple 2
D
S
Structure des données : détail
d’implémentation
Initialisation
Sommet source (S)
Sommet destination (D)
Liste des sommets à explorer (E) : sommet source S
Liste des sommets visités (V) : vide
Tant que (la liste E est non vide) et (D n’est pas dans E) Faire
+ Récupérer le sommet X de coût total F minimum.
+ Ajouter X à la liste V
+ Ajouter les successeurs de X (non déjà visités) à la liste E en
évaluant leur coût total F et en identifiant leur prédécesseur.
+ Si (un successeur est déjà présent dans E) et
(nouveau coût est inférieur à l’ancien) Alors
Changer son coût total
Changer son prédécesseur
FinSi
FinFaire
Structure des données : détail
d’implémentation
Nécessité de mettre en œuvre un conteneur permettant de :
 Récupérer un élément de coût total minimum.
 Insérer un nouvel élément et trier le conteneur.
 Mettre à jour le coût total d’un élément déjà présent dans le
conteneur.
 Déterminer si le conteneur est vide.
Solution « élégante » : files
Template <class T>
class std::queue
{
public:
…
bool empty();
T pop() {return pop_front();}
void push(T t) { push_back(t);}
};
t
push(t)
pop()
Solution « élégante » : files à priorité
Le type T doit surcharger l’opérateur de comparaison <
Template <class T>
class std::priority_queue
{
public:
…
bool empty();
T pop() {return pop_front();}
void push(T t) { /*insertion triée*/ }
};
t
push(t)
pop()
Insertion triée « efficace »
Utilisation d’un arbre binaire d’éléments
 Le fils gauche est strictement inférieur au nœud courant.
 Le fils droit est supérieur ou égal au nœud courant.
5
3
1
12
4
7
20
15
25
Structure des données :
std::priority_queue
Nécessité de mettre en œuvre un conteneur permettant de :
 Récupérer un élément de coût total minimum : OUI
 Insérer un nouvel élément et trier le conteneur : OUI
 Mettre à jour le coût total d’un élément déjà présent dans le
conteneur : NON
 Déterminer si le conteneur est vide : OUI
Structure de données personnalisée :
MyPriorityQueue
template<class T>
class MyPriorityQueue
{
public :
T pop();
void push();
private:
std::vector<T> heap;
};
Structure de données personnalisée :
MyPriorityQueue
template<class T>
T MyPriorityQueue::pop()
{
// L’élément le plus grand est au début
// du conteneur heap : position 0.
T value = heap.front();
// 1. Déplace le premier élément à la position N-1.
// 2. Trie les éléments de la position 0 à N-2
std::pop_heap(heap.begin(), heap.end(), Inf());
// Supprime l’élément en position N-1
// c’est à dire, l’ancien premier.
heap.pop_back();
return value;
}
Structure de données personnalisée :
MyPriorityQueue
template<class T>
T MyPriorityQueue::push(T value)
{
// Ajout de la valeur en queue du conteneur
// position N.
heap.push_back(value);
// Trie les éléments de la position 0 à N.
std::push_heap(heap.begin(), heap.end(), Inf());
return value;
}
Un peu de lecture
Game Programming Gems 1
by Mark de DeLoura
(Charles River Media )
August, 2000
http://www.gamedev.net
http://www.gamasutra.com
Téléchargement