Carte
.
Vous devez compléter la représentation de la classe Carte
.
Pour le Lab12, il est suggéré de commencer par une représentation simple telle que celle ci-dessous.
Elle n'est pas parfaite, mais amplement suffisante pour le Lab12.
class Carte { public: ... private: struct Noeud { // Un noeud est un sommet dans le graphe std::string contenu; // variable inutile pour le Lab12. set<long> voisins; // ensemble des noeuds liés par un segment (tronçon) de route (arête). }; map<long, Noeud> noeuds; // dictionnaire identifiant de noeud --> objet Noeud };
Cette représentation n'est peut-être pas la plus efficace. Cependant, elle a pour avantage d'être simple. Visez d'abord un programme fonctionnel. Une fois cet objectif atteint, pensez à une représentation plus efficace.
Un fichier carte.txt est constitué de :
À titre d'exemple, voici un extrait du fichier (parc_isole.txt) :
n1 (45.47821,-73.66648); n2 (45.4781,-73.66658); n3 (45.48023,-73.6653); ... n124 (45.48005,-73.66538); n125 (45.48122,-73.66446); --- Davies_Avenue : n55 n58 n59 n60 n61 ; Davies_Avenue : n61 n60 n59 n58 n55 ; Palmer_Avenue : n67 n64 n66 n65 n57 n56 ; Palmer_Avenue : n56 n57 n65 n66 n64 n67 ; Wentworth_Avenue : n62 n36 ; ...
Le code pour lire un objet Carte
est fourni dans l'opérateur operator >> (istream& is, Carte& carte)
(voir fcarte.cpp
).
Cet opérateur appelle les fonctions Carte::ajouter_noeud(...)
et Carte::ajouter_route(...)
.
Vous devez compléter ces fonctions.
La fonction Carte::ajouter_noeud(...)
est plutôt facile à implémenter.
Il suffit d'ajouter un nœud dans le vecteur ou l'ensemble noeuds
.
La fonction Carte::ajouter_route(...)
demande un peu plus d'efforts.
Lorsqu'on reçoit une séquence d'identificateurs de noeud < n1, n2, n3, ..., nk >,
il faut ajouter les arêtes (n1, n2), (n2, n3), (n3, n4), ..., (nk-1, nk).
Pour le Lab12, vous n'avez pas besoin de conserver les noms de route.
Notez que si on voulait ignorer les sens uniques, il faudrait aussi ajouter les arêtes dans le sens inverse : (n2, n1), (n3, n2), (n4, n3), ..., (nk, nk-1).
Pour détecter les nœuds problématiques à exclure, il suffit de :
Pour se faire, complétez la fonction Carte::trouver_noeuds_problematiques()
.
Vous devrez probablement ajouter une fonction récursive pouvant se nommer Carte::tarjan_parcours_profondeur(...)
.
Le programme lab12
doit afficher les nœuds à exclure.
Le test :
./lab12 parc_isole.txt
doit afficher:
n62 n84 n85 n86 n87 n88 n89 n90 n91 n92 n93 n94 n95 n96 n97 n98 n99 n100 n101 n102 n103 n104 n105 n106 n107 n108 n109 n110 n111 n112 n113 n114 n115 n116 n117 n118 n119 n120 n121 n122 n123 Nombre de noeuds exclus : 41
Vous pouvez également tester avec les cartes suivantes :
Vous pouvez télécharger ces cartes en lançant le script ./telecharger_tests.bash
.
La carte du Canada est commentée dans le script. Vous pouvez la
décommenter pour la télécharger (le fichier fait 1 GB une fois décompressé).
parcoursProfondeur()
En théorie, les fonctions récursives et non récursives sont équivalentes en terme de complexité temporelle. Toutefois, en pratique, l'utilisation de fonctions récursives peut impliquer un surcoût (overhead). En effet, les appels de fonction implique des opérations additionnelles. De plus, selon l'environnement utilisée, la pile d'exécution peut avoir une taille fixe, donc limitée. Un grand nombre d'appels récursifs peut entraîner un débordement de la pile d'exécution (stack overflow).
L'exécution d'algorithmes récursifs dans un graphe, comme la recherche en profondeur dans une grosse carte, peut impliquer un très grand nombre d'appels récursifs imbriqués. Dans le pire cas, le nombre d'appels récursifs sur la pile d'exécution sera égal au nombre de nœuds de la carte! La carte de Montréal fournie contient plus de 100 000 nœuds. La carte du Québec contient plus de 3 000 000 nœuds. Et plus de 16 millions pour celle du Canada!
Sous Linux et Unix, vous pouvez vérifier la taille par défaut de la pile d'exécution avec la commande ulimit -a
ou ulimit -s
.
$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 63518 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 63518 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
Vous pouvez expérimenter l'effet de limiter la taille de la pile d'exécution en spécifiant une petite limite. Essayez :
$ ulimit -s 64 $ ./lab12 montreal-carte.txt Erreur de segmentation (core dumped)
Cela devrait vous convaincre qu'il peut être intéressant de convertir une fonction récursive en une fonction non récursive.
Le prochain exercice consiste à éliminer la récursivité dans la fonction Carte::tarjan_parcours_profondeur(...)
.
Cet exercice est évidemment plus difficile que les exercices précédents.
Ainsi, il est recommandé de commencer par un algorithme plus simple,
comme la fonction Carte::parcoursProfondeur(...)
que vous avez implémentée au Lab12.
Indice : certaines variables locales (incluant les paramètres) de l'implémentation récursive doivent être stockées dans un conteneur de type pile (std::stack
).
Une fois que vous avez réussi pour Carte::parcours_profondeur(...)
,
essayez de convertir la fonction Carte::tarjan_parcours_profondeur(...)
.
Pour mieux évaluer la différence entre les deux implémentations de Carte::tarjan_parcours_profondeur(...)
,
on peut faire quelques petits changements.
Par exemple, on peut:
Carte::trouver_noeuds_problematiques()
pour qu'elle ne retourne pas le résultat;Carte::trouver_noeuds_problematiques()
plusieurs fois pour amortir le temps de chargement de la carte;for(int i = 0; i < 50; i++) carte.trouver_noeuds_problematiques(); //for(set<string>::iterator iter = noeuds_problematiques.begin(); iter != noeuds_problematiques.end(); ++iter) { // cout << *iter << ' '; //} //cout << endl; // cout << "Nombre de noeuds exclus : " << noeuds_problematiques.size() << endl;
Ensuite, exécutez les deux implémentation du lab12 avec la commande time.
time ./lab12 montreal-carte.txt > /dev/null