1. 1
Le 28/11/2015
LA BALADE DU ROBOT
Rapport sur l’application réalisée et explication du problème
Travail réalisé par : SEHNOUNI Asmaa et KAID Belkacem
Encadré par : M ESCOFFIER Bruno
MOGPL
2. 2
I-Introduction :
Dans ce projet nous allons modéliser et essayer de résoudre un problème de recherche
opérationnelle en graphe cherchant à trouver un plus court chemin, à partir d’un
fichier d’entrée.
II-Exposition du Problème :
Nous avons une matrice de taille N x M ou N est le nombre de colonne et M le
nombre de ligne,
Un fichier donné en entrée est constitué de 4 parties :
-Première Partie :(première Ligne) contient deux chiffre représentant
respectivement le nombre de lignes M et le nombre de colonnes N.
-Deuxième Partie : (N x M cases) contient la matrice représentant les cases et
les obstacles que le robot doit parcourir pour atteindre son but.
-Troisième Partie : Contient la position (Nb ligne, Nb colonne) du robot et la
position du but et la position (orientation) du robot.
-Quatrième Partie : contient deux zéros représentants la fin du fichier.
A partir de cela on doit satisfaire le problème en manipulant le robot de sorte qu'il
atteigne le but en évitant tous les obstacles en prenant le moins de temps possible or
le moins d'instructions de déplacement possible, sachant que toutes les instructions de
déplacement sont de même coût (1). Par exemple, un déplacement à droite coûte 1,
tout comme un déplacement en avançant de 2 pas ou en avançant de 3 pas.
En résumé les contraintes au départ sont :
1. Des obstacles à éviter en se déplaçant sur les grilles.
2. Un déplacement limité à 3 pas maximum.
3. Contrainte temporelle sur le parcours (complexité)
Le sens de modélisation doit être fait dans le sens où dans le parcours reliant des
sommets séparés d’une distance d'au moins (1) jusqu'à atteindre le but mais comme
tout déplacement est de coût 1, nous avons donc pensé à implémenter un parcours en
largeur pour satisfaire cela.
Ainsi pour faire ce parcours le problème est formulé sous forme de graphe orienté.
Soit G= (V, E) un graphe orienté ou V et E les nœuds et les arcs les
constituants.
Les nœuds sont les positions où le robot peut se situer et qui peut atteindre sur les
intersections de rails avec les informations sur sa localisation et son orientation, Les
nœuds sont reliés par des arcs.
Remarque : Un nœud n'est jamais sur un obstacle.
3. 3
III-Algorithme implémenté :
Le choix de l'algorithme de parcours en largeur n'est pas fait par hasard, car ce
dernier est simple, connu, démontré, et pour notre cas les conditions de cet
algorithme sont satisfaits (coûts à 1).
A.Modélisation et Complexité :
Soit le graphe orienté G=(V,E) associé à une matrice donnée en fichier d'entrée.
Soit |V| le nombre de nœuds dans notre graphe et |O| le nombre d’obstacles présents
dans le graphe, ainsi |V|=4((M+1) (N+1)-|O|) car chaque nœud a 4 directions
possibles : Ouest, Est, Nord, Sud (resp : Droite, Gauche, Haut, Bas) ainsi q’un
déplacement à n pas avec n= {1, 2, 3} chacun de cout =1. Ainsi |V|≤4 (M+1) (N+1).
De plus |E|≤5|V| car Il peut y avoir au plus 5 arcs crées à partir d’un nœud.
La complexité du graphe est en ɵ (|E|+|V|) car la complexité de la création d’un
nœud et la complexité de la création d’un arc se fait en ɵ (1).
Complexité de la lecture du fichier donné en entrée ou sur l’interface : ɵ (n+m)
Complexité de l’algorithme :
L’algorithme implémenté est un parcours en Largeur qui est de complexité de base
égale à ɵ(E+V),dans le graphe G=(V,E).
Le chemin le plus court est retrouvé en parcourant le graphe en chemin inverse (en
backtrack), autrement dit en partant du nœud destination jusqu’au nœud de départ et
cela se fait en ɵ (|V|) donc en ɵ (M.N).
B. Programme :
Dans notre code nous avons utilisé des bibliothèques permettant de dessiner et gérer
les graphes.
La fonction lirefile : c’est une fonction prenant un nom de fichier en paramètre et
permettant de le lire pour notre cas des matrice (N x M).
La fonction isObstacle : C’est une fonction prenant une matrice en paramètre et
permettant de retourner la case que nous voulons tester si c’est un obstacle ou pas,
autrement dit si la case contient 0 la valeur retournée sera 0 et au test de cette dernière
on aura false.
4. 4
La fonction genererGraphe : C’est une fonction permettant de tester si le nœud
courant n’est pas un obstacle ainsi on ajoute 4 situations : (nord, est, sud, ouest) sous
forme de nœuds, puis des arcs répondant à chaque cas (ex : si le robot est sur un
nœud et dirigé nord et veut aller est alors ce dernier tourne à Droite).
La fonction CheminPlusCourt : C’est une fonction prenant un Nœud de début,
un nœud destination et un graphe en paramètre et permettant d’implémenter
l’algorithme du parcours en largeur sur des instances données.
La fonction AfficherGraphe : C’est une fonction permettant de dessiner le
graphe sur la console avec les nœuds et les obstacles, la position initiale du robot sa
destination et le chemin emprunté.
La fonction Fichier sortie : C’est une fonction prenant un nom et permettant
d’éditer sur un fichier le résultat du parcours.
La fonction Genererobstacles : C’est une fonction qui génère des obstacles
aléatoirement dans un graphe de taille N x M et qui retourne la matrice.
La fonction Genererledebutetfin : C’est une fonction qui génère deux nœuds
représentant le point de départ du robot et le point destination.
On a rajouté des fonctions permettant de gérer l’interface graphique de
sorte que l’utilisateur décide de la taille de la grille et le nombre
d’obstacle.
Exemple d’exécution avec sur une matrice 50 x 50 générée aléatoirement :
Fichier de sortie :
24 D D a3 a3 D a3 a3 a3 a3 a3
G a3 D a3 a3 a3 a3 a3 a3 a3 a3
G a3 a3
5. 5
C) Dans cette partie nous avons stocké nos fichiers dans un dossier nommé C
contenant des dossiers nommés selon les instances.
Tableau représentant les temps moyens pour une instance |N|=|M|=|O| en fonction du temps :
|N| 1 2 3 4 5 6 7 8 9 10 Temps Moy
10 1 0,15 0,76 0,94 0,78 0,78 0,87 0,63 0,7 0,8 0,742
20 2,5 2,6 2,2 2,5 2,6 2,6 2,2 2,6 2,6 2,7 2,51
30 4,2 4,1 4,6 4,1 4,1 4,6 4,3 9,8 4,6 4 4,84
40 7,1 7,2 7,1 7,4 7,1 7,1 7,1 7 7,1 7,2 7,14
50 12 11,7 11,4 19,4 14,8 14,8 15 14,9 15 15 14,36
D) Dans cette partie nous avons stocké nos fichiers dans un dossier nommé D
contenant des dossiers nommés selon les instances.
Tableau représentant les temps moyens pour une instance |N|=|M|=20 en fonction des |O|
|O| 1 2 3 4 5 6 7 8 9 10 Temps Moy
10 2.8 2.8 2.9 2.4 2.5 2.9 2.8 2.8 2.5 2.6 2.7
20 2.6 2.6 2.5 2.3 2.6 2.5 2.5 2.2 2.4 2.7 2.4
30 2.2 2.2 2.4 2.0 2.3 2.4 2.3 2.1 2.2 2.2 2.23
40 2.2 1.8 2.1 1.9 2.3 2.3 1.8 1.8 2.3 2.3 2.08
50 1.9 1.89 2.18 2.07 2.07 1.47 0.32 1.94 0.21 1.75 1.58
6. 6
AUTRE IDEES :
En dehors de notre implémentation nous avons pensé à présenter une autre manière
de faire.
Il est possible de résoudre ces problèmes de plusieurs manières,par exemple pour
utiliser la matrice initiale la transformer au préalable en appliquant l’algorithme
suivant :
initaliser Matrice_Beta[N][M+1] =0 ;
Pour chaque Ligne i et chaque colonne j faire :
Si (i !=0 et i !=N et j !=M)
Si (Matrice_initiale[i][j]==1)
Matrice_Beta[i][j] =1 ;
Matrice_Beta[i+1][j] =1 ;
Matrice_Beta[i][j+1] =1 ;
Si (i !=0 et j !=M)
Si (Matrice_initiale[i][j]==1)
Matrice_Beta[i][j] =1 ;
Matrice_Beta[i+1][j] =1 ;
Matrice_Beta[i][j+1] =1 ;
Si (i !=N et j !=M)
Si (Matrice_initiale[i][j]==1)
Matrice_Beta[i][j] =1 ;
Matrice_Beta[i+1][j] =1 ;
Matrice_Beta[i][j+1] =1 ;
Retourner Matrice_Beta[N][M] ;
Ainsi on obtient :
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 0
0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 0
0 0 1 0 0 0 0 0 0 0 => 0 0 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 1 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0
0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 1 0
Ainsi nous considérons les zéros comme des nœuds possibles et les 1 comme des obstacles.
7. 7
Utilisation de l’algorithme A* :
.L'algorithme A* est une extension de l'algorithme de Dijkstra
Cet algorithme consiste à utiliser une distance approchée qui est l’heuristique.
Une heuristique h(n) est une fonction d’estimation de coût restant entre un nœud n d'un graphe et le
but : Autrement dit c'est une estimation vol d’oiseau.
Nous aurons besoin de deux fonctions :
Open (etat,f,parent) : contenant les nœuds qui n'ont pas encore été traités, c'est à
dire à la frontière de la partie du graphe explorée jusqu'à maintenant. Autrement dit,
ce sont les nœuds ouverts.
Closed(etat,f,parent) : contenant les nœuds déjà traités, c'est à dire à l'intérieur de
la frontière délimitée par Open(etat,f,parent).Autrement dit ce sont les nœuds où les
voisins sont tous visités.
Dans A*, on sépare le calcul de f(n) en deux parties :
g(n) : coût du meilleur chemin ayant mené au nœud n depuis le nœud initial (le
meilleur coût trouvé jusqu'à maintenant qui se rend à n)
h(n) : cout estimé du reste du chemin optimal partant de n jusqu'au but, h(n) est
la fonction heuristique, (h(n)>0 et h(n)=0 si n est le nœud but).
Remarque : Un nœud fermé (CLOSED) peut être rouvert après être exploré par un autre père si le
coût d’exploration est plus faible.
Les nœuds n dans open sont triés selon l’estimé f(n) de leur valeur : sachant que f(n) est une
fonction d'évaluation, et représente le coût du meilleur chemin partant du nœud initial, passant par n
et arrivant au but.
8. 8
ALGORITHME :
Recherche_Dans_Graphe(noeudInitial) {
1. déclarer deux nœuds : n, n'
2. déclarer deux listes : open, closed //initialisées à vide au départ.
3. insérer noeudInitial dans open
4. tant que (1) //condition de sortie définie dans la boucle
5. si open est vide, sortir de la boucle avec échec
6. n=noeud au début de open
7. enlever n d’open et l'ajouter dans closed
8. si n est le but (goal(n) est true)
sortir de la boucle avec sucés en retournant le chemin
9. pour chaque successeur n' de n (chaque n’appartenant à transitions(n))
10. initialiser la valeur g(n') à g(n)+c(n, n')
11. mettre le parent de n' à n
12. si closed ou open contient un nœud n'' égal à n' avec f(n')<=f(n'')
13. enlever n'' de closed ou open et insérer n' dans open (ordre
croissant selon f(n))
11. si n' n'est ni dans open ni dans closed
15. insérer n' dans open (ordre croissant selon f(n))
}
Déroulement de A* à partir de l'instance suivante :
Open(etat,f ,parent)
1.(n0,9,void)
2. (n1,5,n0),(n2,6,n0),(n3,7,n0)
3.(n2,6,n0),(n3,7,n0),(n5,12,n1)
4.(n3,7,n0),(n4,9,n2),(n5,12,n1)
5.(n2,5,n3),(n4,6,n3),(n5,12,n1)
6.(n4,6,n3),(n5,12,n1)
7.(n6,7,n4),(n5,12,n1)
8.Solution : n0,n3,n2,n4,n6
Closed(etat,f ,parent)
1.vide
2.(n0,9,void)
3.(n0,9,void),(n1,5,n0)
4.(n0,9,void),(n1,5,n0),(n2,6,n0)
5.(n0,9,void),(n1,5,n0),(n3,7,n0)
6.(n0,9,void),(n1,5,n0),(n3,7,n0),(n2,5,n3)
7.(n0,9,void),(n1,5,n0),(n3,7,n0),(n2,5,n3),(n4,6,n3)
8.(n0,9,void),(n1,5,n0),(n3,7,n0),(n2,5,n3),(n4,6,n3),(n6,7,n4)
2
3
9. 9
Conclusion:
Pour finir, les difficultés qu’on peut rencontrer en recherche opérationnelle en
essayant d’automatiser un mécanisme est de trouver une méthode qui fait de
l’autoapprentissage en s’adaptant aux différents cas, cela est une idée pour généraliser
un problème ; trouver une solution peut conduire à réfléchir sur un cas NP-Difficile,
comme pour le cas de la résolution des arbres Steiner. Dans le cas abordé ci-dessus
cela est bien plus simple que ça.