TD Graphes
Orientés , Pondérés , Non Étiquetés⚓︎
Introduction⚓︎
La classe Graphe
de ce TD modélise un graphe 0
à n-1
, où n
désigne le nombre total de sommets, au lieu de porter des étiquettes/noms).
Col
Col
\(mat=\begin{pmatrix} 0 & 0 & 2 & 0 & 0\\ 3 & 0 & 0 & 5 & 0\\ 0 & 1 & 0 & 0 & 0\\ 4 & 0 & 1 & 0 & 5\\ 0 & 6 & 0 & 0 & 0\\ \end{pmatrix} \)
Orienté, Pondéré, Non étiqueté :
La Matrice N'est PAS Symétrique
Les éléments de la matrice sont des flottants
Les Sommets sont des nombres entiers
Col
# Liste de Successeurs en Liste de Listes :
# pour graphes NON étiquetés exclusivement
successeurs = [[[0,2]],
[[1,0], [3,5]],
[[1,1]],
[[0,4], [2,1], [4,5]],
[[1, 6]]
# Liste de Successeurs en Dictionnaire V1 :
# pour des graphes NON étiquetés, mais
# Généralisable à des graphes Étiquetés
successeurs = {0 : {0:2},
1 : {1:0, 3:5},
2 : {1:1},
3 : {0:4, 2:1, 4:5},
4 : {1:6} }
Ex
Comment reconnaître, en ne regardant que la matrice d'adjacence, quels sont les sommets du graphe :
- ayant uniquement des arcs sortants?
- ayant uniquement des arcs entrants?
Notation Matrice d'Adjacence
Dans ce TD, les lignes de la matrice d'adjacence seront notées s
, les colonnes seront notées v
, les poids p
, ainsi :
mat[s][v] = p
par exemple
mat[0][2] = 5
signifie qu'il existe un arc0 --> 2
de poids5
, du sommet0
vers le sommet2
mat[2][4] = 0
signifie qu'il n'existe pas d'arc du sommet2
vers le sommet4
Classe Graphe
en POO⚓︎
On donne la classe incomplète Graphe
suivante (à compléter) :
class Graphe:
def __init__(self, mat=[]):
self.m = mat
# ... à compléter ...
if __name__ == "__main__":
m1 = [[0,2,3,0],
[0,0,0,1],
[0,4,0,5],
[0,0,1,0]]
g1 = Graphe(m1)
m2 = [[0,0,2,0,0],
[3,0,0,5,0],
[0,1,0,0,0],
[4,0,1,0,5],
[0,6,0,0,0]]
g2 = Graphe(m2)
Convention dans les questions du TD
On ne précisera jamais, dans les méthodes suivantes, s'il faut inclure l'instance self
, ou pas.
A vous de vous adapter. Seuls les paramètres autres que self
sont précisés.
Matrice d'Adjacence⚓︎
- Créer une méthode
afficher_matrice()
qui affiche la matrice d'adjacence dans un Terminal -
Créer une méthode
admet_mat_symetrique()
qui renvoie en sortie un booléen :True
si la matrice d'adjacence de l'instance courante (self
) est symétriqueFalse
sinon
-
Créer une méthode
check_symetrie_matrice()
qui :- lève une erreur (
raise
) si la matrice n'est pas symétrique - ne fait rien sinon
- lève une erreur (
-
Autoriser le constructeur
__init__(mat=[])
à instancier un objet graphe Orienté avec une matrice d'Adjacence non symétrique.
Sommets⚓︎
- Créer une méthode qui renvoie l'
ordre()->int
du graphe (son nombren
de sommets) - Créer une méthode
sommets()->list
qui renvoie la liste desn
sommets : une liste d'entiers de0
àn-1
- Créer une méthode
successeurs(s:int)->list
qui renvoie la liste de tous les sommetsv
successeurs des
, mais sans les poids (menant vers ces voisins) : c'est-à-dire la liste des sommetsv
pour lesquels il existe un arc sortant de s :s -> v
- Créer une méthode
predecesseurs(s:int)->list
qui renvoie la liste de tous les sommetsv
predecesseurs des
, mais sans les poids (menant vers ces voisins) : c'est-à-dire la liste des sommetsv
pour lesquels il existe un arc entrant vers s :v -> s
- Créer une méthode
ajouter_sommet()->None
qui permet de créer un nouveau sommet. Par défaut, ce nouveau sommet ne devra être relié à aucun autre. -
Créer une méthode
supprimer_sommet(s:int)->None
qui permet de supprimer un sommets
déjà existant.
Remarque :- Cette méthode supprime également tous les arcs, entrants ou sortants, vers le sommet
s
à supprimer - Cette méthode renumérote automatiquement tous les autres sommets (afin de pas créer de trous dans la numérotation des sommets).
-
En cas de non existence du sommet, on pourra choisir entre deux stratégies:
- ou bien lever une erreur (avec
raise
) - ou bien simplement ne rien faire...
- ou bien lever une erreur (avec
- Cette méthode supprime également tous les arcs, entrants ou sortants, vers le sommet
Arcs Orientés⚓︎
-
Créer une méthode
admet_arc(s:int, v:int)->bool
qui accepte en argument deux entiers/sommetss
(la ligne) etv
(la colonne), et qui renvoie un booléen :True
si le graphe admet un arc (orienté)s -> v
du sommets
versv
(quel que soit le poids)False
sinon
-
Créer une méthode
arcs()->list de tuples
qui renvoie la liste de tous lestuples
(s,v)
pour lesquelss -> v
est un arc qui existe - Créer une méthode
ajouter_arc(s:int, v:int)->None
qui permet de créer un nouvel arcs -> v
du sommets
versv
-
Créer une méthode
supprimer_arc(s:int, v:int)->None
qui permet de supprimer un arcs -> v
déjà existant du sommets
versv
.
Remarque : En cas de non existence de l'arc, on pourra choisir entre deux stratégies:- ou bien lever une erreur (avec
raise
) - ou bien ne rien faire...
- ou bien lever une erreur (avec
-
Créer une méthode
poids(s:int,v:int)->int
qui accepte en entrée deux sommetss
etv
, et qui renvoie en sortie le poids de l'arcs -> v
(en particulier : elle renvoie0
si l'arcs -> v
n'existe pas) -
Créer une méthode
max_poids()->int
(ou float..) qui renvoie le poids maximal d'un arc dans tout le graphe -
Degré(s) d'un sommet - Créer une méthode
degre_plus(s:int)->int
qui accepte en entrée un sommets
, et qui renvoie en sortie le degré positif des
: càd le nombre d'arcs sortants des
- Créer une méthode
degre_moins(s:int)->int
qui accepte en entrée un sommets
, et qui renvoie en sortie le degré négatif des
: càd le nombre d'arcs entrants verss
- Créer une méthode
degre(s:int)->int
qui accepte en entrée un sommets
, et qui renvoie en sortie sondegre = degré_plus + degre_moins
càd le nombre d'arcs sortants des
plus le nombre d'arcs entrants verss
- Créer une méthode
Liste de Successeurs⚓︎
-
Modifier le constructeur
__init__(mat=[], liste_successeurs=[])
, de sorte que :- un graphe puisse être également instancié par une
liste_successeurs=[]
. Syntaxeg1 = Graphe(liste_successeurs=lv1)
oùlv1
est une variable contenant la liste des successeurs deg1
-
Configurer ensuite le constructeur de sorte qu'on ne puisse instancier un objet graphe qu'avec un seul paramètre, mais pas les deux paramètres simultanément :
- soit par une matrice d'adjacence : Sytanxe
g = Graphe(mat = [...])
- soit par une liste de successeurs : Syntaxe
g = Graphe(liste_successeurs = [...])
- soit par une matrice d'adjacence : Sytanxe
- un graphe puisse être également instancié par une
-
Créer une méthode
afficher_liste_successeurs()
qui affiche la liste des successeurs dans un Terminal -
Créer une méthode
liste_vers_matrice(liste_successeurs=[])
qui reçoit en entrée la liste des successeurs de l'instanceself
, et qui est en charge de peupler la matrice d'adjacence de l'instanceself
. Configurer le contructeur__init__()
pour que la matrice d'adjacence soit automatiquement calculée, lorsque le paramètre choisi pour l'instanciation du graphe est laliste_successeurs()
. On en profite pour vérifier la symétrie de la matrice d'adjacence. -
Créer une méthode
mat_vers_liste_successeurs()
qui peuple la liste des successeurs de l'instance couranteself
, à partir de la matrice d'adjacence
Dictionnaires de Successeurs⚓︎
-
Modifier le constructeur
__init__(mat=[], liste_successeurs=[], dico_successeurs={})
, de sorte que :- un graphe puisse être également instancié par une
dico_successeurs={}
. Syntaxeg1 = Graphe(dico_successeurs=d1)
oùd1
est une variable contenant le dictionnaire des successeurs deg1
-
Configurer ensuite le constructeur de sorte qu'on ne puisse instancier un objet graphe qu'avec un seul paramètre, mais pas les trois paramètres simultanément :
- soit par une matrice d'adjacence : Sytanxe
g = Graphe(mat = m1)
- soit par une liste de successeurs : Syntaxe
g = Graphe(liste_successeurs = lv1)
- soit par un dictionnaire de successeurs : Syntaxe
g = Graphe(liste_successeurs = d1)
- soit par une matrice d'adjacence : Sytanxe
- un graphe puisse être également instancié par une
-
Créer une méthode
afficher_dico_successeurs()
qui affiche le dictionnaire des successeurs dans un Terminal -
Créer une méthode
dico_vers_matrice()
qui convertit le dictionnaire des successeurs de l'instanceself
, en la matrice d'adjacence de l'instanceself
-
Créer une méthode
mat_vers_dico_successeurs()
qui convertit la matrice d'adjacence de l'instanceself
, en le dictionnaire des successeurs de l'instanceself
Synchroniser toutes les modélisations⚓︎
-
Modifier le constructeur
__init__()
et de sorte que les 3 attributs suivants soient tous initialement calculés de manière automatique et synchronisée lors de l'instanciation d'un graphe (par n'importe laquelle des 3 méthodes)- matrice d'ajacence :
self.m
- liste des successeurs :
self.liste_successeurs
- dictionnaire des successeurs :
self.dico_successeurs
- matrice d'ajacence :
-
Modifier la classe
Graphe
, de sorte que les attributsself.m
,self.liste_successeurs
etself.dico_successeurs
soient toujours synchronisés, quelle que soit la modification (actuelle ou future) effectuée sur le graphe, en particulier :- à chaque fois qu'une arc est créée et/ou supprimée
- à chaque fois qu'un sommet est créé et/ou supprimé
Créer une image .png
d'un graphe, avec le Langage dot
⚓︎
-
Créer une méthode
toDot(filename="test.dot")
qui écrit/sauvegarde un graphe G dans un fichier texte{name}.dot
(par défauttest.dot
), en suivant la syntaxe du langage DOT.Exemple de Syntaxe avec le Langage DOT, et de Rendu associé, pour un graphe
Orienté ,Pondéré , Non Étiqueté:Col
digraph G { #size="3" --> commentaire node [shape=circle] # pour que les noeuds du graphe # soient entourés par des cercles 5; # pour afficher un sommet séparé 0 -> 1 [label=2]; # pour afficher une arête 0 -> 2 [label=3]; 1 -> 2 [style=bold,label=4]; # Syntaxe alternative 1 -> 2 [style=bold,color=red,label=5]; # Syntaxe alternative {rank=same; 1, 2} }
Code en langage dot Col
Rendu Image du code dot précédent
-
Créer une méthode
extraire_nom_sans_extension(filename="test.dot":str)->str
, qui accepte en entrée une chaîne de caractèrefilename
qui doit être sous la formenom_de_fichier.dot
et qui renvoie en sortie la même chaîne de caractèrenom_de_fichier
mais sans l'extension.dot
Cette méthodeextraire_nom_sans_extension(filename="test.dot":str)->str
devra en outre :- vérifier que le
filename
transmis en argument est bien un objet de typestr
, sinon lever une erreur (par exemple avec unassert
) - vérifier que l'extension de la chaîne est bien de la forme,
.dot
, ou bien renvoyer un message d'erreur adapté pour corriger cela
- vérifier que le
-
(aucun code à produire dans cette question) Cette question fait la supposition que PyQt5 est installé sur votre ordinateur (c'est le cas au Lycée)
La méthode suivante, nomméerenderPicture(filename="test.dot")
, est une méthode offerte gracieusement qui :- Transforme le fichier texte
filename={name}.dot
(par défautfilename=test.dot
) en une image{name}.png
(par défauttest.png
). - Affiche/Visualise le fichier image
test.png
du graphe, en lançant automatiquement la fenêtre graphique.
def renderPicture(self, filename="test.dot"): name = self.extraire_nom_sans_extension(filename) print("name = ",name) import os os.system(f"dot -Tpng {name}.dot -o {name}.png") from PyQt5 import QtGui, QtWidgets app = QtWidgets.QApplication([]) window = QtWidgets.QWidget() HLayout = QtWidgets.QHBoxLayout() pixmap = QtGui.QPixmap(name) label = QtWidgets.QLabel() label.setPixmap(pixmap) HLayout.addWidget(label) window.setLayout(HLayout) window.setWindowTitle("Mon Beau Graphe!") window.show() app.exec_()
- Transforme le fichier texte
-
Créer une méthode
show(name="test")
qui fait le-tout-en-un :- la méthode
show()
crée le fichier{name}.dot
(par défauttest.dot
), grâce à la méthodetoDot()
- puis, la méthode
show()
convertit le fichier{name}.dot
(par défauttest.dot
) en{name}.png
(par défauttest.dot
), grâce à la méthoderenderPicture()
- Elle affiche donc un graphe avec la syntaxe suivante:
m1 = [[0,2,3,0], [0,0,0,1], [0,4,0,5], [0,0,1,0]] g1 = Graphe(m1) g1.show() # va géénrer une image 'test.png' et la montre
- la méthode