Changes between Initial Version and Version 1 of 2009/CaoCourseTme9


Ignore:
Timestamp:
Jan 11, 2010, 12:13:34 AM (15 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • 2009/CaoCourseTme9

    v1 v1  
     1{{{
     2#!html
     3<h1>TME 9 : Algorithme de routage A*</h1>
     4}}}
     5[[PageOutline]]
     6
     7= Objectif =
     8
     9Le but de ce TME est de programmer, en langage C, l'algorithme de routage A*. Cet algorithme recherche le chemin le plus court entre deux points (un point source et un point destination) dans un espace à deux dimensions contenant des obstacles qu'il faut contourner.  L'espace est discrétisé afin de travailler sur des coordonnées entières.  Un chemin est constitué de segments de droites.  On contraint ces segments à être parallèles aux axes des abscisses et des ordonnées. On utilisera donc une distance de Manhattan.
     10
     11= A) Principe Général =
     12
     13L'espace est représenté par un tableau à deux dimensions DS[X,Y]. Ce tableau est indexé par deux index (X,Y) représentant les coordonnées, et chaque case correspond donc à une position dans le plan.
     14Une case a quatre voisins (Nord, Sud, Est, Ouest), et deux positions i et j sont voisines si [ X(i) = X(j) +/- 1 et Y(i) = Y(j) ] ou si [ Y(i) = Y(j) +/- 1 et X(i) = X(j) ].
     15
     16Certaines cases (X,Y) jouent un rôle particulier :
     17 * position de la source S (Xs,Ys)
     18 * position de la destination T (Xt,Yt)
     19 * positions Z contenant un obstacle (il peut y en avoir plusieurs).
     20
     21Le tableau DS[X,Y] est un tableau d’entiers. La valeur contenue dans une case (X,Y) de ce tableau contient en principe la distance DS(P) entre la source S et le point P de coordonnées (X,Y). La fonction principale de l’algorithme consiste à calculer ces distances DS(P). Au début de l’algorithme, ces distances sont donc inconnues, et toutes les cases du tableaux ne contenant pas d’obstacle sont initialisées à une valeur « infinie » (en pratique MAXINT). La case S est initialisée à la valeur 0. On représente le fait qu’une case Z contient un obstacle, en initialisant DS(Z) avec la valeur -1.
     22
     23Le principe général de l’algorithme consiste à calculer progressivement les distances DS(P), en commençant par les points au voisinage de S, puis en élargissant peu à peu la zone autour de S dans laquelle les distances ont été calculées. Cette zone ''connue'' grossit peu à peu jusqu’à atteindre le point T. A un instant donné, il existe donc une zone connexe (autour du point S) regroupant les points pour lesquels la distance DS est connue et une autre zone (contenant le point T) regroupant les points pour lesquels la distance DS est inconnue. Ces deux zones sont séparées par une ''frontière'', qui contient les points dont la distance DS est connue, mais qui peuvent avoir des voisins dont la distance DS n’a pas encore été calculée. Cette frontière constitue le périmètre de la zone connue, et évolue au cours de l’algorithme. Pour représenter cette frontière, on utilise une structure de donnée  FRONTIER, qui est une liste chaînée ordonnée et dynamique, contenant tous les points P(X,Y) appartenant à la frontière à un instant donné.
     24
     25= B)  Algorithme de LEE et algorithme A* =
     26
     27Il y a différentes façons de faire évoluer la frontière entre la zone connue et la zone inconnue, correspondant à différentes stratégies d’exploration de l’espace autour de S, et nous étudierons successivement deux algorithmes dans le cadre de ce TME.
     28
     29== B1) Algorithme de Lee ==
     30
     31Publié en 1961 (C. Y. Lee, "An algorithm for path connection and its application", IRE Trans. Electronic Computer, EC-10, 1961) cet algorithme permet de trouver de façon certaine le chemin le plus court ente deux points, mais il est coûteux en temps de calcul. Il consiste à faire grossir la zone connue de façon isotrope, dans toutes les directions autour du point S. Plus précisément, cet algorithme garantit que tous les points situés à une distance D du point source S auront été calculés avant de commencer à calculer les points situés à une distance D+1.
     32
     33Cet algorithme travaille en trois étapes phases : une phase d’initialisation, une phase d'expansion, consistant à faire grossir la zone connue en stockant les résultats dans les tableaux DS[x,y], et une phase de retour, où on calcule effectivement le chemin le plus court pour revenir de T à S, en utilisant le tableau DS[x,y].
     34
     35'''Etape initialisation pour LEE :'''
     36
     37On initialise toutes les cases du tableau  DS[x,y] à la valeur MAXINT, sauf les cases contenant un obstacle, qui prennent la valeur -1, et la case S, qui prend la valeur 0. On initialise la liste ordonnée FRONTIER qui ne contient que le point source S.
     38
     39'''Etape expansion pour LEE :'''
     40{{{
     41        POUR tous les points P appartenant à la frontière
     42                Retrait du point P de la tête de la liste FRONTIER             
     43                POUR tous les voisins Q du point P
     44                        SI DS(Q) == MAXINT
     45                                DS(Q) = DS(P) + 1
     46                                Ajout du point Q en queue de la liste FRONTIER
     47                        FINSI
     48                        SI Q==T ALORS FINPOUR
     49                FINPOUR
     50        FINPOUR
     51        SI DS(T) != MAXINT
     52                RETOURNE succes
     53        ALORS
     54                RETOURNE echec
     55        FINSI
     56}}}
     57
     58'''Etape retour pour LEE :'''
     59
     60On reconstruit le chemin, de la cible T vers la source S, en recherchant pour chaque point P situé à une distance D un voisin situé à une distance (D-1). On place à chaque position faisant parti du chemin, le code -2. Il peut y avoir plusieurs solutions. Toutes les solutions sont équivalentes pour ce qui concerne la distance. Le choix d'une solution particulière peut être guidée par des contraintes du genre « éviter les changements de directions ».
     61
     62== B2) Algorithme A* ==
     63L'algorithme A* (prononcer A-star) est une variante de l’algorithme de Lee. Il est beaucoup plus rapide, car on ne déplace plus la frontière de façon isotrope dans toutes les directions, mais, on essaie de calculer en priorité les points qui permettent de se rapprocher de la destination T (la frontière est « attirée » par la destination T). Il y a un prix à payer pour cette optimisation : la solution trouvée n’est pas toujours optimale. L’algorithme A* travaille également en trois phases. Les deux phases d’initialisation et de retour sont inchangées. Seule la phase d’expansion est légèrement modifiée.
     64
     65Pour cela on définit, pour chaque point P de la frontière la quantité  COUT(P), qui est une estimation optimiste de la distance entre S et T, si on passe par le point P :
     66{{{
     67COUT(P) = DS(P) + ESTIME(P,T)
     68}}}
     69
     70Le premier terme est un calcul exact de la distance entre S et P, tenant compte des obstacles rencontrés. Le deuxième terme est une estimation (incertaine) de la distance entre P et T, qui est calculé comme la distance de Manhattan entre P et T, et ne prend pas en compte les obstacles qui n’ont pas encore été rencontrés.
     71
     72La principale différence entre les deux algorithmes LEE et A* consiste à ne faire bouger la frontière que pour le point P dont le coût est minimal, et qui est donc en principe le plus proche de T. Pour cela la liste des points appartenant à la frontière est ordonnée par coût croissant:
     73
     74'''Etape expansion pour A* :'''
     75{{{
     76        POUR le point P de coût minimal appartenant à la frontière
     77                Retrait du point P de la liste FRONTIER
     78                POUR tous les voisins Q du point P
     79                        SI DS(Q) == MAXINT
     80                                DS(Q) = DS(P) + 1
     81                                Ajout à sa place du point Q dans la liste FRONTIER
     82                        FINSI
     83                        SI Q==T ALORS FINPOUR 
     84                FINPOUR
     85        FINPOUR
     86        SI DS(T) != MAXINT
     87                RETOURNE succes
     88        ALORS
     89                RETOURNE echec
     90        FINSI
     91}}}
     92
     93= C) Structures de données et fonctions d’accès =
     94
     95
     96== C1) Gestion du tableau  ==
     97
     98Le tableau est géré par une bibliothèque de fonctions graphiques (cf. xmaze.h), qui va se charger de l'initialisation  et de l'affichage du tableau dans une fenêtre X11. Seules quelques fonctions sont externalisées pour pouvoir être utilisées par les fonctions de routage
     99{{{
     100#define MAZ_HAUT  40
     101#define MAZ_LARG  40
     102}}}
     103Ces directives définissent la taille du tableau.
     104{{{
     105#!c
     106enum {VIDE=MAXINT, OBSTACLE=-1, CHEMIN=-2};
     107
     108typedef struct maze_t
     109{
     110        int * MAZE;             /* pointeur vers le tableau */
     111        int HAUT, LARG;         /* hauteur et largeur du tableau */
     112} maze_t;
     113static inline int get_maze(maze_t * ds, int x, int y)
     114}}}
     115
     116Cette fonction rend le contenu la case DS[X,Y].
     117{{{
     118static inline set_maze(maze_t * ds, int x, int y, int val)
     119}}}
     120
     121Cette fonction initialise la case DS[X,Y] avec la valeur VAL.
     122{{{
     123extern void display_maze (maze_t * ds)
     124}}}
     125
     126Cette fonction affiche sur la fenêtre X11, l'état du tableau DS. Les cases VIDE (valeur=MAXINT) sont affichées en noir. Les cases OBSTACLE (valeur=-1) sont affichées en rouge. Les cases CHEMIN (valeur=-2) sont affichées en jaune. Les cases positives (mais non VIDE) sont affichées en bleu.
     127
     128== C2) Algorithme de Lee ==
     129
     130Les fonctions correspondantes sont déclarées dans le fichier ''maze.h''
     131{{{
     132typedef struct boundlee_t
     133{
     134        struct boundlee_t * NEXT;
     135        int X, Y;
     136} boundlee_t;
     137}}}
     138
     139Cette structure représente un élément de la liste ordonnée des points appartenant à la frontière dans le cas de l’algorithme de Lee.
     140{{{
     141typedef struct frontierlee_t
     142{
     143        boundlee_t * FIRST;  /* premier élément de la liste ordonnée*/
     144        boundlee_t * LAST;   /* dernier élément de la liste ordonnée*/
     145} frontierlee_t;
     146}}}
     147Cette structure contient les deux pointeurs permettant d’accéder aux éléments de la  liste ordonnée représentant la frontière dans le cas de Lee.
     148
     149Les fonctions d’accès sont les suivantes :
     150{{{
     151extern frontierlee_t *cons_frontierlee(); 
     152}}}
     153
     154Cette fonction construit une structure frontierlee_t.
     155{{{
     156extern void display_frontierlee(frontierlee_t *list);
     157}}}
     158
     159Cette fonction de debug affiche à l’écran tous les éléments de la frontière. Vous utiliserez la mise au point des fonctions de gestion de cette liste.
     160{{{
     161extern void free_frontierlee(frontierlee_t *list);
     162}}}
     163
     164Cette fonction permet de re-initialiser à « vide » la liste ordonnée représentant la frontière.
     165{{{       
     166extern void add_boundlee(frontierlee_t *list, int x, int y);
     167}}}       
     168
     169Cette fonction ajoute un point en fin de liste dans la liste ordonnée représentant la frontière.
     170{{{
     171extern boundlee_t * get_boundlee(frontierlee_t *list);
     172}}}
     173
     174Cette fonction renvoie un pointeur sur le point qui se trouve  entête de la liste ordonnée représentant la frontière, et retire ce point de la liste.
     175
     176
     177== C3) Algorithme A* ==
     178{{{
     179typedef struct boundastar_t
     180{
     181        struct boundastar_t     *NEXT;
     182        int                     X, Y;   /* coordonnées du point */
     183        int                     COUT ;  /* cout estimé          */
     184} boundastar_t;
     185}}}
     186
     187Cette structure représente un élément de la liste ordonnée des points appartenant à la frontière dans le cas de l’algorithme A*.
     188{{{
     189typedef struct frontierastar_t
     190{
     191        boundastar_t    *FIRST;  /* premier élément de la liste ordonnée */
     192        int XT, YT;              /* Coordonnées de la cible */
     193} frontierastar_t;
     194}}}
     195
     196Cette structure contient le pointeur permettant d’accéder aux éléments de la  liste ordonnée représentant la frontière dans le cas de A* et les coordonnées de la cible, ce qui est nécessaire pour calculer le coût de la position.
     197
     198Les fonctions d’accès sont les suivantes :
     199{{{
     200extern frontierastar_t *cons_frontierastar(int xt, int yt); 
     201}}}
     202
     203Cette fonction construit une structure frontierastar_t. Elle prend en paramètres les coordonnées de la cible.
     204{{{
     205extern void display_frontierastar(frontierastar_t *list);
     206}}}
     207
     208Cette fonction de debug affiche à l’écran tous les éléments de la frontière.
     209{{{
     210extern void free_frontierastar(frontierastar_t *list);
     211}}}
     212
     213Cette fonction permet de re-initialiser à « vide » la liste ordonnée représentant la frontière.
     214{{{       
     215extern void add_boundastar(frontierastar_t *list, int x,int y,int cout);
     216}}}
     217
     218Cette fonction ajoute un point  dans la liste dans la liste ordonnée représentant la frontière, à la place qui lui convient, de façon à respecter le classement par coût croissant.
     219{{{
     220extern boundastar_t * get_boundastar (frontierlastar_t *list);
     221}}}
     222
     223Cette fonction renvoie un pointeur sur le point qui se trouve  entête de la liste ordonnée représentant la frontière, et retire ce point de la liste.
     224
     225Les fonctions suivantes réalisent les phases d’expansion et de retour :
     226{{{
     227extern int expandlee(maze_t *ds,int xs,int ys,int xt,int yt);
     228extern int expandastar(maze_t *ds,int xs,int ys,int xt,int yt);
     229extern int findpath(maze_t *ds, int xt,int yt);
     230}}}
     231
     232Le pointeur ds pointe sur le tableau des distances. (xs,ys) et (xt,yt) sont les coordonnées du point source et du point destination.
     233
     234
     235= D)  Travail à effectuer =
     236
     237Créez un répertoire de travail '''tme9''', et copiez dans ce répertoire tous les fichiers et répertoires qui se trouvent dans
     238{{{
     239/users/enseig/encadr/cao/tme9
     240}}}
     241
     242Le Makefile fourni permet de générer un exécutable nommé xlee fonctionnel. Avec cet exécutable, vous pouvez créer un labyrinthe, choisir un point source, un point cible, et demander le calcul du chemin en utilisant l'algorithme de LEE ou de Astar. Vous allez devoir remplacer progressivement les fichiers objets fournis par vos propres fichiers objets.
     243
     244Q1) réécrire la fonction expandlee()
     245
     246Q2) réécrire les fonctions add_boundlee()
     247
     248Q3) réécrire la fonction findpath()
     249
     250Q4) réécrire la fonction expandastar()
     251
     252Q5) réécrire les foncions add_boundastar()
     253
     254= Compte-Rendu =
     255
     256Il ne vous est pas demandé de compte-rendu écrit pour ce TME, mais vous devrez faire une démonstration
     257de votre code au début du prochain TME.
     258