Graphe Python
Graphe Python
Graphe Python
3. Etre capable d'ajouter des arêtes/arcs à un graphe existant, avec une éventuelle pondération.
Une fois ces opérations élémentaires implémentées, nous pourrons rajouter des possibilités supplémentaires, comme par exemple
Pour commencer, nous allons donc dé nir une classe Graph , dont l'interface minimale sera la suivante (des ajustements seront
possibles selon si le graphe est orienté ou non, pondéré ou non) :
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 1/13
03/02/2023
Matrice d'adjacence
Exemples
la matrice d'adjacence M
alphabétique
0
2
3
la matrice d'adjacence M
la matrice d'adjacence M
B
10
20
C
A
40
30
D
5
=
⎜⎟
⎛
⎝
0
40
5
1
10
0
0
⎞
1
0
⎠
1
20
0
⎞
30
0
⎞
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html
Les graphes en Python - ZoneNSI
Une des possibilités pour représenter un graphe est d'utiliser ce qu'on appelle une matrice d'adjacence. Dans ce type de
représentation, les sommets sont ordonnés, et considérés comme étiquetés par des entiers de 0 à n − 1, où n est l'ordre du
graphe.
2/13
03/02/2023 Les graphes en Python - ZoneNSI
Enoncé
3 1
0
1. Déterminer la matrice d'adjacence associée à ce graphe
3
1
2. Déterminer la matrice d'adjacence associée à ce graphe
3
1
3. Déterminer la matrice d'adjacence associée à ce graphe
42
24
12
0 1
12
2 20
4. Déterminer la matrice d'adjacence associée à ce graphe
⎛ 1 0 1
⎞
5. Tracer un graphe pouvant correspondre à la matrice 0 0 0 Est-ce un graphe orienté ou non-orienté ?
⎝ 1 1 1
⎠
⎛ 1 0 1 1 1
⎞
0 0 1 0 1
⎝ 1 1 1 1 0
⎠
⎛ 1 2 3
⎞
7. Tracer un graphe pouvant correspondre à la matrice 10 20 30 Est-ce un graphe orienté ou non-orienté ?
⎝ 100 200 300
⎠
8. Quelle propriété semble posséder les matrices d'adjacence d'un graphe non-orienté ?
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 3/13
03/02/2023 Les graphes en Python - ZoneNSI
Implémentation en Python
Enoncé
Le code suivant permet d'implémenter en partie l'interface voulue d'un graphe avec une matrice d'adjacence :
class Graph :
def __init__(self, n=0) :
self.n = n
self.adj = [[0]*n for _ in range(n)]
def add_vertice(self) :
self.n +=1
for l in self.adj :
l.append(0)
self.adj.append([0]*(self.n))
1. Ajouter une méthode DUNDERS __repr__ a n qu'elle renvoie la chaîne de caractère correspondant à la matrice d'adjacence
(et donc directement utilisable par l'instruction print(G) ). Pour des raisons de facilités d'écritures, on pourra utiliser le
formatage automatique des chaînes de caractères, comme par exemple : `python
>>> e = 15
>>> f"BOB{e: >3}"
BOB 15 où l'expression e: >3 signi e d'écrire e sous la forme d'une chaîne de caractères alignée à droite d'au moins 3
caractères, des espaces étant insérés si nécessaire.
2. Compléter la méthode exist_edge de la classe Graph pour qu'elle corresponde aux spéci cations de l'interface.
3. Compléter la méthode get_neighbours de la classe Graph pour qu'elle corresponde aux spéci cations de l'interface.
4. Ajouter une méthode get_order à la classe Graph pour qu'elle renvoie l'ordre du graphe.
5. Ajouter une méthode get_degree à la classe Graph pour qu'elle renvoie le degré d'un sommet passé en argument.
6. Ajouter une méthode get_vertices à la classe Graph qui renvoie la liste des sommets.
7. Ajouter une méthode is_directed à la classe Graph pour qu'elle renvoie True si le graphe est orienté et False sinon.
False si le graphe est non-orienté et qu'il n'existe pas de parcours eulérien du graphe
un tuple (s,e) donnant les sommlets de départ et d'arrivée d'un éventuel chemin eulérien.
9. Ajouter une méthode delete_edge à la classe Graph pour qu'elle supprime l'arc situé entre les sommets s et e passés en
argument.
Réponses
class Graph :
def __init__(self, n=0) :
self.n = n
self.adj = [[0]*n for _ in range(n)]
def add_vertice(self) :
self.n +=1
for l in self.adj :
l.append(0)
self.adj.append([0]*(self.n))
def exist_edge(self, s, e) :
return self.adj[s][e] !=0
def get_order(self) :
return self.n
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 4/13
03/02/2023 Les graphes en Python - ZoneNSI
def get_neighbours(self,s) :
neighbours = []
for i in range(self.n) :
if self.adj[s][i] !=0 :
neighbours.append(i)
return neighbours
def get_vertices(self) :
return [i for i in range(self.n)]
def is_directed(self) :
for i in range(self.n) :
for j in range(i,self.n) :
if self.adj[i][j] != self.adj[j][i] :
return True
return False
def is_undirected_and_eulerian(self) :
if self.is_directed() :
return False
degrees=[]
for i in range(self.n) :
# ATTENTION ! La méthode get_degree renvoie le double
# du degré réel dans le cas d'un graphe non-orienté
# Pour que le théorème d'Euler fonctionne
# il faut donc diviser par 2 la valeur obtenue !
degrees.append((self.get_degree(i)//2)%2)
print(degrees)
if sum(degrees) == 0 :
return True
elif sum(degrees) == 2 :
return degrees.index(1), self.n-1-degrees[::-1].index(1)
return False
def delete(self,s, e) :
self.adj[s][e] = 0
def __repr__(self) :
rep =""
for l in self.adj :
for e in l :
rep+=f'{e: >3} '
rep += "\n"
return rep
Limites du modèle
Si l'utilisation d'une matrice d'adjacence sous la forme présentée est très utile d'un point de vue mathématique (voir par exemple
la propriété de l'itérée sur la page wikipedia), et très facile à mettre en oeuvre, elle possède néanmoins ceryaines limites :
Elle utilise une place en mémoire proportionnelle à n2. Pour un graphe de 1000 sommets, il faudra plus d'un million d'entiers
pour stocker cette matrice d'adjacence, ce qui commence à être considérable;
Pour connaître les voisins d'un sommet, il faut parcourir la totalité de la lign,e correspondant à ce sommet, ce qui peut
rapidement être trop long .
Les sommets sont limités à des entiers, ou à un ordre dé ni, et il est di cile d'intégrer de nouveau sommets qui ne
respecteraient pas la convention xée.
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 5/13
03/02/2023 Les graphes en Python - ZoneNSI
Pour réduire la taille prise en mémoire par la matrice d'adjacence, il est possible d'utiliser un dictionnaire de voisinage. Ce dictionnaire
apportera en plus la capacité d'avoir des chaînes des caractères comme identi ant des sommets.
Selon que le graphe soit pondéré ou non, on aura quelques différences dans l'implémentation.
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 6/13
03/02/2023 Les graphes en Python - ZoneNSI
Implémentation en Python
Enoncé
Le code suivant permet d'implémenter en partie l'interface voulue d'un graphe avec un dictionnaire de voisinage :
class Graph :
def __init__(self) :
self.vertice = {}
def add_vertice(self,s) :
if s not in self.vertice :
self.vertice[s] = set() # crée un objet set vide, et graranti l'unicité de chaque élément
def add_edge(self, s, e) :
self.add_vertice(s)
self.add_vertice(e)
self.vertice[s].add(e) # La méthode add des objets de type set fonctionne comme append
1. Ajouter une méthode DUNDERS __repr__ a n qu'elle renvoie la chaîne de caractère correspondant à la matrice d'adjacence
(et donc directement utilisable par l'instruction print(G) ).
2. Compléter la méthode exist_edge de la classe Graph pour qu'elle corresponde aux spéci cations de l'interface.
3. Compléter la méthode get_neighbours de la classe Graph pour qu'elle corresponde aux spéci cations de l'interface.
4. Ajouter une méthode get_order à la classe Graph pour qu'elle renvoie l'ordre du graphe.
5. Ajouter une méthode get_degree à la classe Graph pour qu'elle renvoie le degré d'un sommet passé en argument.
6. Ajouter une méthode get_vertices à la classe Graph qui renvoie la liste des sommets.
7. Ajouter une méthode is_directed à la classe Graph pour qu'elle renvoie True si le graphe est orienté et False sinon.
False si le graphe est non-orienté et qu'il n'existe pas de parcours eulérien du graphe
un tuple (s,e) donnant les sommlets de départ et d'arrivée d'un éventuel chemin eulérien.
9. Ajouter une méthode delete_edge à la classe Graph pour qu'elle supprime l'arc situé entre les sommets s et e passés en
argument.
Solution
1 class Graph :
2 def __init__(self) :
3 self.vertice = {}
4
5 def add_vertice(self,s) :
6 if s not in self.vertice :
7 self.vertice[s] = set() # crée un objet set vide, et graranti l'unicité de chaque élément
8
9 def add_edge(self, s, e) :
10 self.add_vertice(s)
11 self.add_vertice(e)
12 self.vertice[s].add(e) # La méthode add des objets de type set fonctionne comme append
13
14 def exist_edge(self, s, e) :
15 return e in self.vertice[s]
16
17 def get_vertices(self) :
18 return self.vertice.keys()
19
20 def get_order(self) :
21 return len(self.vertice.keys())
22
23 def get_degree(self, s) :
24 return len(self.vertice[s])
25
26 def get_neighbours(self,s) :
27 return list(self.vertice[s])
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 7/13
03/02/2023 Les graphes en Python - ZoneNSI
28
29 def is_directed(self) :
30 for s in self.get_vertices() :
31 for t in self.get_neighbours(s) :
32 if s not in self.get_neighbours(t) :
33 return True
34 return False
35
36 def is_undirected_and_eulerian(self) :
37 if self.is_directed() :
38 return False
39 degrees=[]
40 for s in self.get_vertices() :
41 degrees.append(self.get_degree(s)%2)
42 if sum(degrees) == 0 :
43 return True
44 elif sum(degrees) == 2 :
45 return degrees.index(1), self.get_order()-1-degrees[::-1].index(1)
46 return False
47
48 def delete_edge(self, s, e) :
49 self.vertices[s].remove(e)
50
51
52
53 def __repr__(self) :
54 rep = ""
55 for s in self.get_vertices() :
56 rep += f"{s} :\n"
57 for t in self.vertice[s] :
58 rep += f" ->{t}\n"
59 return rep
3.2. Graphes pondérés
Il existe plusieurs méthodes permettant d'ajouter une pondération sur chaque arc :
on peut ajouter dans le dictionnaire de voisinage un tuple contenant à la fois le nom et le poids de l'arc considéré ;
on peut aussi ajouter un autre dictionnaire parallèle à self.adj dont les clés sont les couples de sommets et les valeurs le poids
de l'arc considéré.
Enoncé
Le code suivant permet d'implémenter en partie l'interface voulue d'un graphe avec un dictionnaire de voisinage et une
pondération :
class Graph :
def __init__(self) :
self.vertice = {}
def add_vertice(self,s) :
if s not in self.vertice :
self.vertice[s] = set() # crée un objet set vide, et graranti l'unicité de chaque élément
Quels sont les changements à apporter aux autres méthodes par rapport à la situation sans pondération ?
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 8/13
03/02/2023 Les graphes en Python - ZoneNSI
Enoncé
Le code suivant permet d'implémenter en partie l'interface voulue d'un graphe avec un dictionnaire de voisinage et une
pondération :
class Graph :
def __init__(self) :
self.vertice = {}
self.edges= {}
def add_vertice(self,s) :
if s not in self.vertice :
self.vertice[s] = set()
Quels sont les changements à apporter aux autres méthodes par rapport à la situation précédente ?
réponses
A venir
Bien entendu, il est possible de créer de nouveau une nouvelle classe qui implémenterait cette possibilité immédiatement. Mais il
exsite aussi en POO une notion fondamentale qui va nous permettre, sans changer le code de notre classe Graph actuelle et en
effectuant qu'un codage minimal, de créer une nouvelle classe permettant d'implémenter spéci quement des graphes non orientés.
Il s'agit de le notion d'héritage de classe. Sans rentrer dans les détails(que vous pouvez par exemple trouver ici), il s'agira de créer
une classe lle héritant de toutes les capacités de la classe mère - attributs et méthodes, mais dans laquelle on pourra rajouter ou
modi er des caractéristiques spéci ques.
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 9/13
03/02/2023 Les graphes en Python - ZoneNSI
Ainsi, en simpli ant, on peut dire qu'un graphe non-orienté est un graphe orienté dans lequel les arcs sont doublés. Les graphes non-
orientés sont donc une sous-classe des graphes orientés, d'un point de vue codage.
1 class UndirectedGraph(Graph) :
2 def __init__(self) :
3 super().__init__()
4
5 def add_edge(self, s, e, p=1) :
6 super().add_edge(s,e,p)
7 super().add_edge(e,s,p)
En ligne 1, on crée une classe UndirectedGraph , qui dérive, ou hérite de la classe Graph .
En ligne 2, on dé nit la méthode constructeur des objets de classe UndirectedGraph . Celle-ci se compose d'une unique ligne,
disant simplement que l'initialisation d'un objet de classe UndirectedGraph se fait de la même manière qu'un objet de la classe
mère Graph , par l'intermédiaire du mot-clé super() . On fait ainsi appel à la méthode constructeur de la classe Graph pour
construire un objet de classe UndirectedGraph .
En ligne 5, on va redé nir la méthode add_edge . On appelle une telle redé nition un surcharge de méthode. Il s'agira en fait
d'appeler deux fois la méthode add_edge de la classe Graph , encore une fois en utilisant le mot-clé super() .
Une fois cette nouvelle classe créé, elle peut être directement utilisée tout en conservant toutes les méthodes de la classe mère :
1 G = UndirectedGraph()
2 G.add_edge(0, 1, 20)
3 G.add_edge(0, 2, 128)
4 G.add_edge(2, 1, 42)
5 G.get_neighbours(1)
6 G.get_order()
7 print(G)
Dans le code ci-dessus, même si nous n'avons pas surchargé les méthodes get_neighbours , get_order et la méthode DUNDERS
__repr__ , elles demeurent accessible à tout objet de classe UndirectedGraph , puisque directement héritées de la classe mère
Graph .
Objets en Python
En Python, tout est objet. Cette phrase n'est pas qu'une déclaration de principe, puisqu'il existe une classe générique (appelée
classe abstraite) Object , dont dérivent toutes les autres classes, comme int , float , tuple ,...
Polymorphisme
Il est tout à fait possible de faire hériter une classe de plusieurs autres classes. Cette notion s'appelle le polymorphisme, mais
est complètement hors du programme de terminale...
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 10/13
03/02/2023 Les graphes en Python - ZoneNSI
Le théorème des 4 couleurs est un théorème classique de théorie des graphes. D'après wikipedia :
« Le théorème des quatre couleurs indique qu'il est possible, en n'utilisant que quatre couleurs différentes, de colorier n'importe
quelle carte découpée en régions connexes, de sorte que deux régions adjacentes (ou limitrophes), c'est-à-dire ayant toutes une
frontière (et non simplement un point) en commun reçoivent toujours deux couleurs distinctes. L'énoncé peut varier et
concerner, de manière tout à fait équivalente, la coloration des faces d'un polyèdre ou celle des sommets d'un graphe planaire,
en remplaçant la carte par un graphe dont les sommets sont les régions et les arêtes sont les frontières entre régions. [...] Même
si l'énoncé de ce théorème est élémentaire, on n'en connaît pas de preuve simple. Les démonstrations connues décomposent le
problème en un nombre de sous-cas tellement important qu'elles nécessitent l'assistance d'un ordinateur pour être véri ées.
Le théorème se généralise à certaines classes de graphes non planaires. Cependant, lorsqu'on généralise le problème à un
graphe quelconque, il devient NP-complet de déterminer s'il est coloriable avec seulement quatre couleurs (ou même trois). »
Si cette possibilité de colorier avec au maximum 4 couleurs existe, il est par contre parfois di cile de trouver un coloriage qui
corresponde réellement.
Il est cependant possible d'utiliser un algorithme glouton qui trouvera un coloriage « presque » optimal.
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 11/13
03/02/2023 Les graphes en Python - ZoneNSI
Enoncé
et le code suivant permettant de créér un graphe non-orienté correspondant à cette carte dans le chier suivant (en utilisant la
classe UndirectedGraph ).
on prend un sommet du graphe au hasard, on regarde les couleurs déjà données à ses voisins, et on lui donnera comme
couleur la plus petite valeur non-affectée à un de ses voisins.
couleur : un dictionnaire associant à des noms de régions le numéro de couleur qui lui est associé.
et qui renvoie la valeur de couleur la plus petite non associées aux voisins.
Par exemple :
2. Construire en n une fonction color_graph qui prend en argument un graphe et une région de départ et renvoie un tuple
contenant :
Réponses
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 12/13
03/02/2023 Les graphes en Python - ZoneNSI
A venir !
file:///home/fabien/Documents/GitHub/ZoneNSI.md/site/NSI/Terminale/C09/graphe_python/tmpva7p1cl6.html 13/13