14.1- TP - Parcours de graphes
Ce TP a pour but de vous faire travailler les parcours de graphe. Il commence par une partie "préliminaire" sur l'implémentation et quelques questions classiques, puis enchaine sur un problème d'application à des données réelles.
Commencez par exécuter la cellule suivante :
I. Préliminaires
On considère des graphes simples, non-orientés, et pondérés. Les noeuds sont étiquetés par des chaines de caractères, et les arrêtes par des flottants.
On choisi pour représenter les graphes une représentation en "liste d'adjacence".
Question 1
Quel type python correspondrait à une telle représentation ici ?
Et non, car on étiquette les noeuds par des chaines de caractères. Ca aurait été la cas si on avait eu des noeuds entiers consécutifs.
Non plus !
C'est ça ! Le dictionnaire associe à chaque noeud la liste de ses voisins, et le poids de chaque lien.
Ca fait beaucoup de dictionnaires là non ?
Question 2
Déclarer une variable contenant le graphe suivant :
Loading editor...
graphe = {"Caen": [("Paris", 1.5), ("Rennes", 1.)],
"Paris": [("Caen", 1.5), ("Rennes", 1.5), ("Lille", 1.)],
"Rennes": [("Caen", 1.), ("Paris", 1.5)],
"Lille": [("Paris", 1.)]}Afficher un graphe
On vous fournit une fonction pour afficher un graphe : afficher_graphe(graphe). Le graphe est dessiné sous la cellule, une fois son exécution terminée.
On peut aussi colorer en rouge certains sommets ou arêtes : afficher_graphe(graphe, sommets_rouges=["Caen"], aretes_rouges=[("Caen", "Paris")]).
Question 3
Affichez le graphe ci-dessus. Est-ce le même ?
Loading editor...
II. Un graphe secret ?
Un graphe secret (simple, non pondéré, non orienté) (astucieusement nommé graphe_secret) s'est caché dans votre TP. Votre objectif ? Décoder les messages cachés à l'intérieur à l'aide de parcours de graphe.
Question 4
Je vous ai remis le squelette des deux parcours, à vous de les compléter, et de les exécuter sur le graphe en partant du sommet "" !
Loading editor...
def parcours_profondeur(G, d, deja_vu):
""""""
operation_secrete(d)
for voisin in #à compléter:
if voisin not in deja_vu:
deja_vu[voisin] = True
# à compléter
return
deja_vu = {"": True}
# à compléterLoading editor...
from collections import deque
def parcours_largeur(G, d):
file = deque()
file.appendleft #à compléter
deja_vu = {d: True}
while #à compléter
operation_secrete(x)
x = file #à compléter
for voisin in G[x]:
if voisin not in deja_vu:
file.appendleft(voisin)
#Evite d'ajouter les mêmes sommets à la pile plusieurs fois
#à compléter
#à compléterIII. Un graphe réel
On va maintenant s'intéresser à un vrai graphe de la vie réelle. Mais d'abord, un peu de SQL !
Vous avez à disposition dans ce TP une base de donnée de tronçons de chemin de fer de Normandie. Les attributs de la table Lignes sont les suivants :
departla ville de départarriveela ville d'arrivéelongueurla longueur du tronçon (en mètres)
A noter, pour les longueur des tronçons, qu'on ne compte que les rails et les gares qui sont effectivement en Normandie. Par exemple, la ville de Paris apparait avec des distances peu réalistes : c'est parce que on compte les rails jusqu'au changement de région, et plus après.
Pour accéder aux données en python, on va utiliser le module sqlite3.
Vous n'avez pas besoin de vous préoccuper de la syntaxe précise !
Question 5
Compléter le code suivant avec une requète SQL pour récupérer les colonnes depart, arrivee et longueur dans la table Lignes
Loading editor...
import sqlite3
con = sqlite3.connect("TrainNormandie.db")
cur = con.cursor()
res = cur.execute("Votre requète SQL ici")
donnees = res.fetchall()Vous disposez maintenant d'une liste donnees, contenant des triplets (ville de départ, ville d'arrivée, longueur).
Question 6
Écrivez une fonction pour convertir cette liste en un graphe au format de la première partie. Attention à bien placer les arrêtes dans les deux sens !
Vous pouvez ensuite afficher le graphe.
Loading editor...
On vous demande de répondre à toutes les questions qui suivent en écrivant une fonction permettant d'y répondre sur n'importe quel graphe.
Question 7
Le réseau ferré normand est-il connexe ?
Loading editor...
On s'interesse maintenant au trajet avec le moins d'arrêts intermédiaires possibles. Tous les tronçons de la base de donnée sont des trajets directs, sans arrêts.
Quelle parcours serait adapté à résoudre ce problème ?
Question 8
Écrivez une fonction qui prend un graphe, et deux villes, et qui renvoie une liste contenant le nombre minimal d'arrêts, la liste des arrêts en question, et la longueur en kilomètres du trajet correspondant.
Loading editor...
Visualiser un parcours
La fonction animer_parcours(graphe, ordre, aretes) affiche le graphe, et allume ses sommets un par un, dans l'ordre de la liste ordre. Le paramètre aretes (optionnel) donne les arêtes par lesquelles les sommets ont été découverts : soit une liste de couples (parent, sommet), soit un dictionnaire qui à chaque sommet associe son parent. Chaque arête s'affiche en rouge quand son sommet est visité.
Essayez-la avec votre parcours de la question précédente (ou, plus tard, avec l'algorithme de Dijkstra) !
Loading editor...
Question 9
Écrivez une fonction qui prend un graphe, et deux villes, et qui renvoie tous les trajets possibles (qui emprunte chaque sommet une fois au maximum) entre les deux villes. Ne testez pas sur le grand graphe, plutôt sur l'exemple du début de sujet ! Enfin, en fait vous faites comme vous voulez mais vous allez vite voir le problème.
Loading editor...
Question 10
En déduire une fonction, peu efficace, pour trouver le plus court chemin entre deux sommets.
Loading editor...
Question 11
En utilisant l'algorithme de Dijkstra, écrivez une version plus efficace de cette fonction. Vous pouvez vous aider du cours et de cette vidéo.
Loading editor...
Questions bonus (sans ordre particulier)
- Existe-il des chemins eulériens ou hamiltoniens sur les transports ferrés normands ? Et des cycles ?
- Le graphe est-il planaire ?
- Cherchez d'autres base de données de graphes sur data.gouv.fr, et exploitez les de la même façon. Pour information, je vous donne le code qui m'a permis de convertir la base de donnée normande en celle que vous avez pour ce TP.
Code de conversion (il utilise les modules pandas et geopandas, et ne fonctionne pas dans le navigateur) :
import pandas as pd
import geopandas as gpd
#lis le ficher de géométrie dbf avec geopandas
gdf = gpd.read_file("nom_du_fichier.dbf")
#créé une nouvelle colonne longueur, qui prend la longueur de chaque ligne
gdf["LONGUEUR"] = gdf["geometry"].map(lambda x: x.length)
#supprime les colonnes inutiles
gdf = gdf[["DEPART", "ARRIVEE", "LONGUEUR"]]
#créé la base de donnée
from sqlalchemy import create_engine
engine = create_engine('sqlite:///TrainNormandie.db', echo=False)
#sauvegarde la table dans la base de donnée
gdf.to_sql(name='Lignes', con=engine) À noter que, de base, la colonne geometry contient les coordonnées géographiques d'une ligne.
Loading editor...
Entrez le code donné en cours pour accéder à la solution :