TD Graphes
Non Orientés, Non 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 & 1 & 1 & 0 & 0\\ 1 & 0 & 1 & 1 & 1\\ 1 & 1 & 0 & 1 & 0\\ 0 & 1 & 1 & 0 & 1\\ 0 & 1 & 0 & 1 & 0\\ \end{pmatrix} \)
Non Orienté, Non Pondéré, Non étiqueté :
La Matrice est Symétrique
Les entiers de la matrice sont des 0
ou des 1
Les Sommets sont des nombres entiers
Col
# Liste de Voisins en Liste de Listes :
# pour graphes NON étiquetés exclusivement
voisins = [[1, 2],
[0, 2, 3, 4],
[0, 1, 3],
[1, 2, 4],
[1, 3]]
# Liste de Voisins en Dictionnaire :
# pour des graphes NON étiquetés, mais
# "Généralisable" à des graphes Étiquetés
# en changeant les listes par des dicos
voisins = {0 : [1, 2],
1 : [0, 2, 3, 4],
2 : [0, 1, 3],
3 : [1, 2, 4],
4 : [1, 3]}
Remarque
Notation Matrice d'Adjacence
Dans ce TD, les lignes de la matrice d'adjacence seront notées s
, les colonnes seront notées v
, ainsi :
mat[s][v] = 0 ou 1
par exemple :
mat[0][2] = 1
signifie qu'il existe une arête du sommet0
au sommet2
etmat[4][2] = 0
signifie qu'il n'existe pas d'arête du sommet4
au sommet2
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,1,1,0],
[1,0,1,1],
[1,1,0,1],
[0,1,1,0]]
g1 = Graphe(m1)
m2 = [[0,1,1,0,0],
[1,0,1,1,1],
[1,1,0,1,0],
[0,1,1,0,1],
[0,1,0,1,0]]
g2 = Graphe(m2)
Remarque
Convention dans les questions du TD
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 (
-
Modifier le constructeur
__init__(mat=[])
afin que celui-ci renvoie un message d'erreur lorsque l'utilisateur tente d'intancier un objet graphe (Non Orienté) dont la matrice d'Adjacence n'est pas 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
voisins(s:int)->list
qui renvoie la liste de tous les sommets voisins des
, mais sans les poids (menant vers ces voisins) -
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 toutes les arêtes menant 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 toutes les arêtes menant vers le sommet
Arêtes⚓︎
-
Créer une méthode
admet_arete(s:int, v:int)->bool
qui accepte en argument deux entiers/sommetss
etv
, et qui renvoie un booléen :True
si les sommetss
etv
sont reliés par une arêteFalse
sinon
-
Créer une méthode
aretes()->list de tuples
qui renvoie la liste de tous lestuples
(s,v)
pour lesquels l'arête(s,v)
existe (sans doublons) - Créer une méthode
ajouter_arete(s:int, v:int)->None
qui permet de créer une nouvelle arête entre les sommetss
etv
. -
Créer une méthode
supprimer_arete(s:int, v:int)->None
qui permet de supprimer une arête déjà existante entre les sommetss
etv
. Remarque : En cas de non existence de l'arête, 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'arêtes -- v
(en particulier : elle renvoie0
si l'arêtes -- v
n'existe pas) - Créer une méthode
degre(s:int)->int
qui accepte en entrée un sommets
, et qui renvoie en sortie son degré (le nombre d'arêtes reliées às
)
Liste de Voisins⚓︎
-
Modifier le constructeur
__init__(mat=[], liste_voisins=[])
, de sorte que :- un graphe puisse être également instancié par une
liste_voisins=[]
. Syntaxeg1 = Graphe(liste_voisins=lv1)
oùlv1
est une variable contenant la liste des voisins 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 : Syntaxe
g = Graphe(mat = [...])
- soit par une liste de voisins : Syntaxe
g = Graphe(liste_voisins = [...])
- soit par une matrice d'adjacence : Syntaxe
- un graphe puisse être également instancié par une
-
Créer une méthode
afficher_liste_voisins()
qui affiche la liste des voisins dans un Terminal -
Créer une méthode
liste_vers_matrice(liste_voisins=[])
qui reçoit en entrée la liste des voisins 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_voisins()
. On en profite pour vérifier la symétrie de la matrice d'adjacence. -
Créer une méthode
mat_vers_liste_voisins()
qui peuple la liste des voisins de l'instance couranteself
, à partir de la matrice d'adjacence
Dictionnaire de Voisins⚓︎
-
Modifier le constructeur
__init__(mat=[], liste_voisins=[], dico_voisins={})
, de sorte que :- un graphe puisse être également instancié par une
dico_voisins={}
. Syntaxeg1 = Graphe(dico_voisins=d1)
oùd1
est une variable contenant le dictionnaire des voisins 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 : Syntaxe
g = Graphe(mat = m1)
- soit par une liste de voisins : Syntaxe
g = Graphe(liste_voisins = lv1)
- soit par un dictionnaire de voisins : Syntaxe
g = Graphe(liste_voisins = d1)
- soit par une matrice d'adjacence : Syntaxe
- un graphe puisse être également instancié par une
-
Créer une méthode
afficher_dico_voisins()
qui affiche le dictionnaire des voisins dans un Terminal -
Créer une méthode
dico_vers_matrice()
qui convertit le dictionnaire des voisins de l'instanceself
, en la matrice d'adjacence de l'instanceself
-
Créer une méthode
mat_vers_dico_voisins()
qui convertit la matrice d'adjacence de l'instanceself
, en le dictionnaire des voisins 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 voisins :
self.liste_voisins
- dictionnaire des voisins :
self.dico_voisins
- matrice d'ajacence :
-
Modifier la classe
Graphe
, de sorte que les attributsself.m
,self.liste_voisins
etself.dico_voisins
soient toujours synchronisés, quelle que soit la modification (actuelle ou future) effectuée sur le graphe, en particulier :- à chaque fois qu'une arête 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
non orienté, non pondéré , non étiqueté:Col
graph G { node [shape=circle] # pour que les noeuds du graphe # soient entourés par des cercles 5; # pour afficher un sommet séparé 0 -- 1; # pour afficher une arête 0 -- 2; 1 -- {2; 4}; # Syntaxe alternative }
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 renvoyer un message d'erreur adpaté 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) 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.png
), grâce à la méthoderenderPicture()
- Elle affiche donc un graphe avec la syntaxe suivante:
m1 = [[0,1,1,0], [1,0,1,1], [1,1,0,1], [0,1,1,0]] g1 = Graphe(m1) g1.show() # va générer une image 'test.png' et la montre
- la méthode