accéder au rapport

publicité
Boravy CHIN
Baptiste DURAND
E3
.. Rapport de PR-3602
Approfondissement en informatique
Thème « Graphes et algorithmes »
http://perso.esiee.fr/~durandb/
1
Table des matières
0 - Introduction générale ............................................................................................ 3
I - Questions de préparation ....................................................................................... 4
II - Problèmes introductifs........................................................................................... 5
II - A - Problème d’affectation.................................................................................. 5
II - B - Problème de la carte routière ....................................................................... 7
III - Travail demandé .................................................................................................. 8
III – A – Enoncé ...................................................................................................... 8
III – B – Indications concernant la théorie ............................................................... 8
III – B – 1 – Le Graphe de résolution de problèmes ............................................ 9
III – B – 2 – Le Graphe de carte........................................................................... 9
III – C – Indications concernant la mise en pratique ............................................. 10
III – C – 1 – Le choix du langage JAVA ............................................................. 10
III – C – 2 – Le Graphe de résolution de problèmes .......................................... 10
III – C – 3 – Le Graphe de carte ........................................................................ 10
III – D – Heuristiques ............................................................................................ 11
III – D – 1 – Introduction .................................................................................... 11
III – D – 2 – Explication des différentes heuristiques ......................................... 11
III – D – 3 – Comparaison des différentes heuristiques ..................................... 12
III – D – 4 – Synthèse ........................................................................................ 13
III – E – Déroulement de notre programme ........................................................... 14
III – E – 1 – Déroulement ................................................................................... 14
III – E – 2 – Tests de débogage ......................................................................... 14
III – E – L’interface graphique ............................................................................... 15
III – E – 1 – Introduction ..................................................................................... 15
III – E – 2 – Principe d’utilisation ........................................................................ 15
IV – Liens utiles ........................................................................................................ 17
V – Remerciements .................................................................................................. 18
2
0 - Introduction générale
Le but du projet PR-3602 est de réaliser proposer et d'implémenter une méthode de
résolution au célèbre problème du voyageur de commerce. C'est un problème
d'optimisation fort simple à comprendre, mais dont on n'a pour l'instant pas encore
trouvé de solution de résolution en temps de complexité polynomial.
Qu'est-ce que le problème du voyageur de commerce ? Le problème du voyageur de
commerce consiste en un voyageur (de commerce) qui souhaite parcourir un certain
nombre de villes de manière à minimiser la distance totale parcourue et revenant au
point de départ. Le problème revient donc à trouver le plus court-circuit hamiltonien.
Pour ce faire, nous utilisons une implémentation commentée et expliquée de
l'algorithme A*, un algorithme informatique constituant une approche très utilisée
notamment dans l'intelligence artificielle et qui nous permet de résoudre ce problème
en un temps non factoriel.
Nous coderons cet algorithme en langage JAVA pour plus de commodité en évitant
ainsi les différents problèmes des langages de bas-niveaux (problèmes de mémoire
entre autres), tout en permettant la programmation orientée objet et l'utilisation de
classes toutes faites très intéressantes pour la gestion des tableaux à taille variable
(ArrayList).
Ce projet utilise quatre classes :
•Starter pour démarrer le projet
•Node le noeud du Graphe de résolution de problème
•Graph pour générer et afficher le graphe de carte sous forme de matrice carrée des
distances
•GUI notre interface graphique
Le site web du projet est accessible à l’adresse indiquée en fin de rapport.
3
I - Questions de préparation
Qu'est-ce qu'un Graphe de Résolution de Problème (GRP), relativement à un
problème donné ?
UN GRP est un graphe où :
● c’est un graphe connexe et sans cycle : une arborescence
● les sommets sont les états possibles du problème
● on distingue l’état initial et les états finaux
● le coût c(u) associé à l’arc u = (i, j) représente une règle permettant de passer
de l’état i à l’état j
● le chemin de l’état initial à un état final constitue une solution au problème
Quel GRP proposeriez-vous pour le problème du Voyageur de Commerce ?
Pour le problème du Voyageur de Commerce, nous proposerions un GRP dont les
nœuds sont les villes parcourues.
Quel est, schématiquement, le fonctionnement d'un algorithme A* ?
1. On initialise une liste OUVERTE on y ajoute le nœud initial et une liste FERMEE
vide.
2. On prend le 1er élément de la liste OUVERTE et on le met dans la liste FERME.
3. On rentre dans la grande boucle du programme A*.
4. On développe le dernier nœud de la liste FERMEE (celui que l’on vient d’ajouter
dans cette liste à l’instant, c’est-à-dire que l’on ajoute ses successeurs potentiels
du GRP dans la liste OUVERTE)
5. On prend le nœud de la liste OUVERTE qui minimise la fonction d’évaluation F =
G + H et on l’ajoute à la liste FERMEE.
6. Si le nœud ajouté à la liste fermée est le nœud final (correspond à la condition de
fin de la boucle), on continue en 7, sinon, on reboucle en 3.
7. On termine l’algorithme.
Ainsi, la liste OUVERT représente l’ensemble des nœuds étudiés et la liste FERME
représente l’ensemble des nœuds ayant été considérés comme faisant partie du
chemin solution mais qui n’en font pas partie forcément.
Que représentent les symboles g, h et f dans l'algorithme ?
Soit n, un sommet pris au hasard dans le GRP, dans l’algorithme, on a :
● Le symbole g représente le coût du sommet initial au sommet n,
● Le symbole h représente la fonction heuristique choisie,
● Le symbole f représente la fonction d’évaluation définie par f(n) = g(n) + h(n)
Quelle est la condition sur h pour que l'on parle d'algorithme A* ?
On parle d’algorithme A* si, pour tout sommet n, h(n) ⪯ h(n*) donc que l’algorithme A
trouve un chemin optimal du sommet initial à un but s’il en existe.
4
II - Problèmes introductifs
II - A - Problème d’affectation
On désire affecter n personnes à n travaux, chaque personne devant effectuer un
travail et un seul.
Le coût d’affectation de la personne i au travail j est d ij (il peut s’agir, par exemple,
d’un coût de formation).
On cherche à affecter les personnes aux travaux de sorte que la somme des coûts
soit minimale.
Proposez un graphe de résolution pour le problème de l’affectation.
Quelle est la complexité de calcul de l’algorithme qui analyse toutes les
solutions possibles ?
La complexité de calcul de l’algorithme qui analyse toutes les solutions possibles est
O(n!).
5
Proposez un algorithme A* pour résoudre le problème de l’affectation.
Appliquer cet algorithme dans le cas où la matrice dij est donnée par :
Nous avons choisi comme heuristique la somme des minimums des lignes de
la matrice même s’ils étaient sur la même colonne ce qui aurait signifié que l’on affecte
plusieurs personnes à une même tâche, or ce n’est pas possible d’après l’énoncé du
problème.
Proposer un autre graphe de résolution.
Discuter de l’intérêt de cette nouvelle formulation.
On peut en général associer plusieurs GRP à un même problème et l’un d’eux peut
permettre limiter le plus possible la partie explorée par la recherche heuristique mais
qui permet de trouver malgré tout le même résultat.
6
II - B - Problème de la carte routière
On cherche à résoudre de façon automatique le problème suivant : étant donnée une
carte routière, deux villes A et B, trouvez la route la plus courte (en nombre de
kilomètres) entre A et B.
Trouvez une bonne heuristique pour résoudre ce problème en utilisant un
algorithme A*.
Spécifier de façon précise les données qui sont nécessaires à l’algorithme.
Pour résoudre ce problème, on pourrait utiliser l’algorithme de Dijkstra avec la fonction
nulle comme fonction heuristique.
Cependant, il est possible d’avoir une plus grande optimalité avec une fonction
heuristique minorante et monotone, comme par exemple le vol d’oiseau, par rapport
à la distance réelle d’une route.
Indiquer le fonctionnement de l’algorithme sur un exemple simple.
A partir du graphe ci-dessus, nous obtenons en utilisant l’algorithme de Dijkstra avec
l’heuristique nulle le graphe suivant :
7
III - Travail demandé
III – A – Enoncé
Votre objectif sera d'implémenter la stratégie A* pour résoudre le problème du
voyageur de commerce. Vous proposerez et implémenterez plusieurs heuristiques A*,
et vous les testerez sur des cas générés manuellement (pour les plus petits) ou
aléatoirement.
Les heuristiques seront évaluées empiriquement en fonction du critère suivant : le
rapport entre le nombre de nœuds développés par l'algorithme et la taille totale de
l'arborescence de recherche.
Vous utiliserez le langage de programmation de votre choix. Voici pour vous aider, à
titre d'exemple, une trame pour une implémentation en langage C. Elle comprend :
● le fichier vdc.h, qui contient une structure de données pour représenter le Graphe
de Recherche, et plus précisément les éléments de la liste OUVERT ;
● le fichier vdc.c, qui contient un découpage du programme en fonctions plus ou
moins élémentaires à programmer.
Les commentaires servent à spécifier le rôle de chaque fonction ou champ de
structure, ainsi qu'à générer automatiquement une documentation du programme
grâce au logiciel Doxygen.
III – B – Indications concernant la théorie
IMPORTANT : Nous ne chercherons pas la solution optimale au problème.
Nous chercherons seulement à développer les nœuds de manière à obtenir un
chemin de taille égale à celle du graphe qui peut se révéler être de qualité et
proche de l’optimalité pour toutes les heuristiques sauf celle gloutonne. Ce qui
explique que les différentes heuristiques ne trouvent pas forcément le même
chemin hamiltonien. Cela permet diminuer le temps de calcul. Sachez
cependant que l’heuristique nulle garantis l’optimalité absolue jusqu’au nœud
n-1 (celui empreinte avant de revenir au point de départ). Pour obtenir la solution
optimale à chaque fois, il faudrait faire tourner l’algorithme suffisamment
longtemps (modifier uniquement la condition d’arrêt).
Nous avons comme données la liste des villes et la matrice des distances entre ces
villes. Nous commencerons par choisir quelques données particulières aux problèmes
afin de trouver une solution générale.
Nous utilisons ici deux graphes :
 le graphe de résolution de problèmes
 le graphe de carte.
8
III – B – 1 – Le Graphe de résolution de problèmes
Le GRP est une arborescence servant à résoudre un problème donnée. Il est
composé d’une liste OUVERTE représentant les nœuds analysés mais par encore
rajoutés à la liste FERMEE. Et d’une liste FERMEE composée de nœuds extraits
nécessairement de la liste OUVERTE. L’élaboration de la solution au problème va
s’effectuer à partir de la liste FERMEE depuis le nœud terminant l’exécution de
l’algorithme.
Dans le GRP, un nœud ajouté à la liste FERMEE depuis la liste OUVERTE est retiré
de la liste OUVERTE et ses successeurs depuis le graphe de carte, sont développés,
c’est à dire ajoutés à la liste OUVERTE.
Le GRP comporte des nœuds qui possèdent divers attributs tels que les chemins pour
les atteindre, leur numéros, leur cout G, leur heuristique H et leur fonction d’évaluation
F qui est égale à la somme de G et H. Il est possible d’estimer la taille du GRP.
III – B – 2 – Le Graphe de carte
Nous représenterons le graphe de carte sous forme de matrice dans notre programme
informatique (cf : III – C – 2).
Le graphe de carte est un graphe possédant propriétés suivantes :
 Il est connexe car il faut que le voyageur de commerce puisse circuler dans
toutes les villes → La matrice le représentant n'aura donc aucune ligne ni
aucune colonne entièrement nulle.
 Il est complet de manière à ce que le voyageur de commerce puisse passer
dans chaque ville de manière indépendante → La matrice le représentant
n'aura donc aucuns zéro hormis ceux de sa diagonale
 Il est non orienté car on considère que les routes reliant les différentes villes
sont à double-sens → La matrice le représentant sera donc symétrique par
rapport à sa diagonale
 Il est antiréflexif, c'est à dire que la distance d'une ville à elle-même est nulle →
La matrice le représentant aura donc une diagonale nulle
Comme le graphe des villes est un graphe complet, nous allons devoir choisir une ville
de départ pour le programme mais qui, concrètement, ne comptera pas car le
voyageur doit revenir à la ville de départ.
Voici
un
exemple
de
correspondance entre un graphe
complet et sa matrice qui décrit les
distances entre les sommets :
9
III – C – Indications concernant la mise en pratique
III – C – 1 – Le choix du langage JAVA
Nous avons choisi de programmer notre programme en JAVA pour plusieurs raisons :
 La gestion simple de la mémoire qui évite ainsi l’utilisation de fonctions de
gestions de mémoire telles que les pointeurs ou les allocations de mémoire
manuelles
 La possibilité d’utiliser la programmation orienté objet, ce qui nous permet de
créer des objets de type « nœud » ou de type « graphe » et donc de nous
faciliter la représentation des entités
 La possibilité d’utiliser des tableaux à taille variable : les ArrayList
 La simplicité de programmation et le fait d’avoir des outils de gestions des
ArrayList préprogrammées tels que les littérateurs de liste
III – C – 2 – Le Graphe de résolution de problèmes
Nous avons choisi de toujours partir du sommet 1 du graphe de carte dans le GRP
pour des facilités de programmation.
Pour représenter les différents nœuds du GRP, c’est-à-dire les nœuds des listes
OUVERTE et FERMEE, nous créés une classe dédiée : Node. La classe Node
contiendra aussi toutes les méthodes permettant de gérer le GRP tel que le
développement d’un nœud, l’extraction d’un nœud, la sélection d’un noeud …
Nous avons choisi de représenter les listes OUVERTE et FERMEE avec des objets
de type ArrayList. Les ArrayList sont des tableaux à taille variable que l’on peut
facilement étendre et qui possède de nombreuses méthodes préprogrammée pour les
parcourir ou ajouter des objets. Les ArrayList des deux listes seront donc des listes
de nœuds.
III – C – 3 – Le Graphe de carte
Nous avons choisi de prendre des distances entières entre chaque ville comprises
entre 1 et 10. Si la distance est égale à 0 alors le chemin est trivial, c’est-à-dire qu’il
correspond au chemin d’une ville à elle-même. Le chemin trivial n’est pas pris en
compte par notre algorithme.
Nous avons choisi de représenter le graphe des distances entre les villes sous la
forme d’une matrice carrée symétrique par rapport à sa diagonale en tant que tableau
10
à deux dimensions pour des facilités de représentation et de programmation. (cf : III –
B – 2)
Pour simplifier la programmation, nous avons créé une classe dédiée pour représenter
ce graphe de carte : la classe Graph.
III – D – Heuristiques
III – D – 1 – Introduction
Le problème du voyageur de commerce peut être résolut de plusieurs manières avec
des taux de fiabilité et de rapidité pouvant varier sensiblement. Ces différentes façons
de procéder dépendent essentiellement de l’heuristique choisie. Pour notre
programme, nous avons choisi trois heuristiques :
 Heuristique nulle
 Heuristique minimums restants
 Heuristique gloutonne
 Heuristique nulle améliorée
III – D – 2 – Explication des différentes heuristiques
Des exemples d’exécutions graphiques des différentes heuristiques sont disponibles
sur le site web via l’adresse :
http://perso.esiee.fr/~durandb/
L’heuristique nulle est, comme son nom l’indique, une heuristique qui est
systématiquement égale à 0. Concrètement, notre algorithme A* va à chaque fois
s’efforcer de rechercher le plus court chemin depuis la ville de départ jusqu’à une ville
v. Notre algorithme A* devient ainsi un algorithme de Dijkstra ce lui qui garantis
d’obtenir à coup sûr le plus court chemin jusqu’à la valeur n-1 (ce n’est pas le cas au
rang n). Cela a cependant le principal défaut de consommer beaucoup de ressources
et de nécessiter un temps de calcul pouvant rapidement devenir inabordable (à partir
d’une taille de graphe de 7 dans notre cas à nous). Dans notre algorithme, cela revient
à parcourir la liste OUVERTE et à prendre le nœud de coût G minimal.
L’heuristique des minimums restants va elle en revanche utiliser une fonction
d’heuristique égale à la somme des minimums des villes du graphe de carte encore
non parcourues depuis un nœud du GRO donné. Si dans notre cas à nous, cela ne
garantis pas à coup sûr d’obtenir la solution optimale au problème, cela représente
néanmoins une solution convenable pour le problème, c’est-à-dire une solution de
11
qualité qui se rapproche de l’optimalité sans pour autant l’atteindre. De plus, cette
heuristique permet d’économiser des ressources et du temps de calcul.
L’heuristique gloutonne est une heuristique qui va se contenter de rajouter à la liste
FERMEE, à chaque tour de boucle, le nœud minimisant la valeur de l’arc entre un
nœud non compris dans la liste FERMEE nœud et le dernier nœud rajouté à la liste
FERMEE. Elle ne prend à chaque fois en considération que le minimum local. Ce qui
signifie qu’elle peut trouver, notamment pour les graphes de carte de grande taille,
une solution au problème inconvenable, c’est-à-dire très éloignée de l’optimalité.
L’heuristique nulle améliorée ressemble à l’heuristique nulle à la seule différence
qu’elle prend en compte la longueur du chemin (le nombre de sommets parcourue
jusqu’au nœud). Dans notre cas à nous, on utilise la valeur 100-longeur du chemin,
valeur qui diminue quand le chemin s’allonge.
Elle permet, quand deux nœuds du GRP ont le même coût minimum de la liste
OUVERTE, de choisir le nœud ayant le chemin le plus long contrairement à
l’heuristique nulle qui prendrai le systématiquement le premier nœud de la liste
ouverte.
III – D – 3 – Comparaison des différentes heuristiques
Lors des différentes exécutions des algorithmes, nous avons pu mesurer un certain
nombre de différences entre les différentes heuristiques :
 L’heuristique nulle avec l’algorithme de Dijkstra met toujours beaucoup plus
de temps à s’exécuter que les autres heuristiques. Les temps de calcul
explosent à partir d’une taille de 7 mais elle garantit en revanche de trouver la
solution optimale au problème jusqu’à un rang taille du graphe de carte -1. Si
la condition de fin d’algorithme A* garantissait l’optimalité de la solution, ce
serai une heuristique intéressante en terme de temps de calcul.
 L’heuristique gloutonne s’exécute en un temps proportionnel à la longueur
du graphe de carte et est donc de loin l’heuristique la plus rapide de toutes. En
revanche, il lui arrive souvent de trouver une solution très éloignée de la
solution optimale. Si la condition de fin d’algorithme A* garantissait l’optimalité
de la solution, ce ne serai pas une heuristique intéressante en terme de temps
de calcul car elle ferait développer beaucoup de nœuds dans le GRP.
 L’heuristique des minimums restants est à mi-chemin entre les deux
précédentes heuristiques. Elle possède une rapidité inférieure à l’heuristique
gloutonne, mais largement supérieure à celle de l’heuristique nulle. Elle trouve
des solutions de plus grande taille que l’heuristique nulle, mais celles-ci restent
tout à fait convenables contrairement à celles obtenues par l’heuristique
gloutonne. Si la condition de fin d’algorithme A* garantissait l’optimalité de la
12

solution, ce ne serai l’heuristique la plus intéressante en terme de temps de
calcul car elle minimiserait le nombre de nœuds développés.
L’heuristique nulle améliorée est légèrement plus rapide que l’heuristique
nulle car elle prend en compte la taille du chemin (pas le cout) ce qui réduit le
nombre de nœuds développés. Si la condition de fin d’algorithme A*
garantissait l’optimalité de la solution, ce serai une heuristique assez peu
intéressante en terme de temps de calcul.
III – D – 4 – Synthèse
Au regard des différents constats effectués précédemment, il apparaît clairement que
ni l’heuristique nulle, ni l’heuristique gloutonne, ni la nulle améliorée ne sont vraiment
adaptés pour résoudre le problème quand le graphe de carte dépasse une certaine
taille. Diverses raisons de temps de calcul ou d’optimalité de la solution trouvée
nuisent à leur mise en pratique.
L’heuristique des minimums restants semble être un bon compromis entre ces trois
heuristiques car elle possède un temps de calcul tout à fait raisonnable pour des
graphes de grande taille tout en donnant une solution convenable au problème. C’est
donc plutôt à partir de celle-ci que nous recommandons l’exécution de l’algorithme.
Elle serait même la meilleure des heuristiques en termes de performances (nombre
de nœuds du GRP développés …) si l’on avait une condition finale garantissant
l’optimalité de la solution.
Tableau synthétique selon la condition de fin : chemin de taille égale à celle du graphe
de carte :
Nom heuristique
Nulle
Min restants
Gloutonne
Nulle +
Temps de calcul
X
~
V
~
Optimalité de la solution
V (→ n-1)
~
X
~
Efficacité (grands graphes)
X
V
X
X
13
III – E – Déroulement de notre programme
III – E – 1 – Déroulement
L’appui sur le bouton de lancement permet d’exécuter l’algorithme. Il démarre et
boucle tant que la condition de fin n’est pas vérifiée.
III – E – 2 – Tests de débogage
Les tests de débogage sont inclus dans l’interface graphique. Leur utilisation
s’effectue comme ceci (façon que nous avons utilisée pour déboguer notre code) :
Pour comparer les différentes heuristiques entre elles :
 Sélectionner une taille du graphe comprise entre 4 et 7
 Sélectionner l’indicateur de conservation de graphe pour éviter de régénérer
un graphe aléatoire à chaque fois.
 Sélectionner l’affichage de la liste FERMEE
 Lancer l’algorithme à plusieurs reprises (environ 10 fois) par heuristique choisie
 Regardez le déroulement de l’affichage des solutions et accessoirement le
déroulement de l’algorithme.
 Vous pouvez évaluer les performances de l’heuristique en comparant le
nombre de nœuds développées à la taille du GRP (si vous avez sélectionné
l’option d’affichage), le temps d’exécution (ms) et le nombre de tours de
l’algorithme.
 Pour changer de graphe de carte, désélectionner l’indicateur de conservation
de graphe, lancer l’algorithme, resélectionner cet indicateur.
Pour vérifier une heuristique en particulier :
 Regarder le code source disponible sur le site web
 Regarder les exemples d’exécution disponibles sur le site web
 Regarder la JAVADOC du projet disponible sur le site web.
 Sélectionner l’affichage des listes OUVERTE et FERMEE (ralentis le
programme mais permet un affichage détaillé)
 Lancer une heuristique pour un graphe de 4 (facile à analyser visuellement)
 Analyser le déroulement du programme
Notez que l’affichage des listes ralentis fortement la progression de l’algorithme. Il ne
faut donc les afficher que lors du débogage.
14
III – E – L’interface graphique
III – E – 1 – Introduction
Pour plus de commodité, nous avons ajoutés une interface graphique de manière à
rendre l’utilisation de cet algorithme plus simple et surtout plus intuitive. En effet, d’une
part, notre algorithme peut ainsi être utilisé directement en exécutable au format .jar
sans effectuer de manipulation particulière. D’autre part, cela permet de faciliter la
lecture des différentes composantes (graphe généré de manière aléatoire,
déroulement de l’algorithme, solution …). Enfin, l’utilisation d’une interface graphique
permet de modifier le fonctionnement de l’algorithme (choix de l’heuristique par
exemple), sans avoir à en modifier le code source du programme.
III – E – 2 – Principe d’utilisation
Notre interface graphique est composée comme ceci :
1. Un écran affiche en haut à gauche la matrice représentant les arcs des
distances entres les villes (graphe de carte)
2. Un écran situé en bas à gauche affiche la solution de l’algorithme (nb : si
l’algorithme réussis à la trouver cf : JAVADOC). Elle affiche aussi le temps
d’exécution ainsi que le nombre de tours de l’algorithme A*.
3. L’écran de droite se charge d’afficher le déroulement de l’algorithme tel que les
nœuds composants les différentes listes
4. La barre glissante en bas à gauche permet de choisir la taille du graphe de
carte
5. Ce bouton permet de conserver le même graphe de carte entre deux
exécutions consécutives (débogage)
6. Les boutons en bas au centre servent à choisir l’heuristique de calcul parmi les
différents choix proposés
7. Les boutons en bas à droite permettent à l’utilisateur de choisir l’affichage des
listes ouvertes ou fermées à sa guise (débogage)
8. Le bouton en bas à droite sert à exécuter l’algorithme selon les critères choisis
par l’utilisateur
L’interface graphique utilise des bibliothèques incluses par défaut lors du
téléchargement du JDK. Une version à jour de JAVA est cependant fortement
recommandée.
L’interface graphique permet donc un débogage aisé du programme A*.
15
16
IV – Liens utiles
La JAVADOC, le code source, le programme en JAVA, le rapport PDF et les exemples
d’exécution sont disponibles sur le site web de notre projet à l’adresse suivante :
http://perso.esiee.fr/~durandb/
Lien vers le site web de M.Couprie :
http://perso.esiee.fr/~coupriem/PR3602/
17
V – Remerciements
Nous tenons à remercier M.Couprie pour nous avoir aidé, encadré et conseillé dans
notre travail sur l’algorithme A*.
18
Téléchargement