Après toutes ces notions de base, place à la manipulation de graphes en Python.
Quelles sont les primitives dont nous avons besoin pour le TAD graphe ?
On peut aussi envisager :
Une structure très riche ! Nous n'aurons pas toujours besoin de toutes ces fonctionnalités, mais nous allons réfléchir à comment implémenter les principales.
GRAPHE
Vous allez implémenter les primitives du TAD Graphe.
L'objectif sera à la fin de ce chapitre de disposer d'un module graphe
prêt à l'importation dans un script, et qui permettra de manipuler les graphes.
Vous modéliserez un graphe par un dictionnaire des listes d'adjacence de ce graphe.
Pour simplifier, on considérera tous les graphes comme orientés, et non pondérés.
Vous pourrez utiliser des assertions pour tester les préconditions des primitives.
Le but est d'écrire une classe Graphe
, dont le seul attribut sera le dictionnaire des listes d'adjacence du graphe.
Les seules primitives à implémenter seront :
Vous trouverez ci-dessous la signature des différentes méthodes à implémenter; à vous de compléter le code :
Avant de passer à la suite, réaliser quelques tests unitaires sur le code que vous venez d'écrire pour vous assurer de son bon fonctionnement.
Une fois le code de la classe GrapheD
mis au point, enregistrez-le seul ( sans les tests ) dans un fichier nommé graphe.py
, que l'on pourra ensuite importer dans un
nouveau script.
Vous allez maintenant utiliser le code que vous venez d'écrire en écrivant quelques fonctions qui utiliseront la classe Graphe
.
Ces fonctions pourraient éventuellement être codées comme méthodes de cette classe car elles lui apportent des fonctionnalités plus étendues, mais nous choisirons de laisser la classe réduite à ses primitives de base car ces fonctionnalités ne sont pas toujours nécessaires.
Les fonctions suivantes devront donc être écrites dans un nouveau script, qui importera le module graphe
. Attention donc à ce que le fichier graphe.py
soit bien dans le même répertoire
que votre nouveau script, ou alors pensez à l'importer au préalable dans l'éditeur si vous travaillez en ligne.
instancier un objet Graphe
, et donner lui quelques sommets et quelques relations ( vous pouvez prendre un exemple aléatoire des exercices précédents 😏 ).
Écrire une fonction vers_matrice
qui :
Graphe
Bien entendu, le graphe ne devra pas être vide...
Ce problème n'est pas si évident...voici un algorithme qui devrait vous aider un peu :
sommets ← liste des sommets ( comment la déterminer ? )
adjacence ← liste de listes de n x n éléments tous égaux à 0 ( combien de lignes ? de colonnes ? comment le déterminer ? )
pour chaque ligne de adjacence :
id1 ← sommet correspondant à la ligne ( comment le trouver ? )
pour chaque colonne de adjacence :
id2 ← sommet correspondant à la colonne ( comment le trouver ? )
si id2 est adjacent à id1 :
adjacence[ligne][colonne] = 1
Oui, c'est du O(n2) ! Pour une très grande matrice, il faudrait sans doute trouver mieux ( ça existe, mais les maths là-derrière ne sont pas au programme...).
L'exercice inverse : écrire une fonction vers_graphe
qui :
Graphe
créé à partir de ces paramètres graphviz
Ce module fournit deux classes : Graph
pour un graphe non orienté, et Digraph
pour un graphe orienté.
from graphviz import Graph # ou Digraph
On peut alors créer un objet Graph ( ou Digraph ) :
g = Graph() # ou : g = Digraph()
Ces deux classes ont de nombreux attributs qui gèrent les propriétés du graphe, notamment le modèle de rendu du graphe qui affectera son aspect ( certains modèles donnent un
meilleur résultat visuel sur certains graphes ) ou alors le type de sortie ( format PDF ( par défaut ), PNG ou SVG par exemple avec Pyzo ).
vous pouvez consulter l'API de ces deux classes sur cette page pour plus d'informations sur les deux classes, ainsi que
celle-ci pour la présentation des différents modèles de rendu.
On peut ensuite définir la liste des sommets du graphe, passée en argument à la méthode nodes()
, ou alors les définir un par un avec la méthode node()
:
g.nodes(['A', 'B', 'C', 'D'])
g.node('E')
chaque sommet doit avoir un identifiant unique dans le graphe sous forme d'une chaîne de caractère(s), qui constituera de plus l'étiquette par défaut du sommet lors de l'affichage du graphe.
De la même manière, on définira la liste des arêtes/des arcs avec les méthodes edges()
ou edge()
:
g.edges(['AB', 'CD']) # crée un arc A->B et un arc C->D ( des arêtes pour un objet Graph bien sûr...)
g.edge('E', 'A') # pour cette méthode, les sommets doivent être séparés.
L'ordre d'écriture des identifiants des nœuds indique le sens de l'arc dans le cas d'un graphe orienté.
Il se trouve que la seule liste des arêtes/des arcs peut suffire pour définir le graphe dans graphviz
!
Pour afficher le graphe, on utilise alors la méthode view()
:
g.view()
Cela créera le graphe et lancera le programme adapté au format de sortie choisi ( pour Pyzo, le lecteur PDF par défaut, ou en ligne, un affichage à droite de l'éditeur ).
Vous pouvez essayer ce petit code et le modifier pour prendre en main le module :
Le modèle de disposition peut être spécifié par l'argument engine de la méthode view()
; par défaut, c'est le moteur dot qui est utilisé
si l'on ne précise rien.
Pour Pyzo, utilisez help()
pour connaître les autres modèles; en ligne, les modèles disponibles sont circo, dot, fdp, neato, osage et twopi.
Compléter la classe Graphe
avec une méthode dessine
qui utilise le module Graphviz pour visualiser une instance de la classe.