Cours Graphes 3
Cours Graphes 3
Cours Graphes 3
def adja(S,A):
’’’ fonction qui prend en entrée une liste de sommets et une liste de listes (arrêtes du graphe) et r
(A : list, S : list)--> (dico : dict)’’’
dico={}
for sommet in S :
dico[sommet]=[]
for arrete in A:
s1,s2,pond=arrete[0],arrete[1],arrete[2]
dico[s1].append([s2,pond])
#dico[s2].append([s1,pond]) à rajouter si le graphe est non orienté)
return dico
On peut aussi écrire une fonction qui prend en entrée deux listes S et A tel que (S, A) représente un graphe non orienté et renvoie
la matrice d’adjacence associée. Et faire de même dans le cas où le graphe est représenté par un dictionnaire (qui représente sa liste
d’adjacence).
def matriceadja(S,A) :
n=len(S)
mat=[[0 for _ in range(n) ] for _ in range(n) ]
trad={}
k=0
for sommet in S :
trad[sommet]=k
k=k+1
for arrete in A:
s1,s2=trad[arrete[0]],trad[arrete[1]]
mat[s1][s2]=arrete[2]
#mat[s2][s1]=arrete[2]
return mat
def matriceadja(D) :
key=D.keys()
n=len(key)
mat=[[0 for _ in range(n) ] for _ in range(n) ]
trad={}
k=0
for sommet in key :
1
trad[sommet]=k
k=k+1
for sommet in key:
for voisin in D[sommet]:
s1=trad[sommet]
s2=trad[voisin[0]]
mat[s1][s2]=voisin[1]
#mat[s2][s1]=voisin[1]
return mat
Parcours en profondeur
On utilise la structure de pile :
from collections import deque
def parcoursprof_it(graphe,sommet):
visite=[]
marque={}
attente=deque()
attente.append(sommet)
while len(attente)>0:
sommet=attente.pop()
if sommet not in marque :
visite.append(sommet)
marque[sommet]=1
for vois in graphe[sommet]:
if vois[0] not in marque:
attente.append(vois[0])
return visite
Parcours en largeur
On utilise la structure de file :
from collections import deque
def parcourslargeur_it(graphe,sommet):
visite=[]
marque={}
attente=deque()
attente.append(sommet)
while len(attente)>0:
sommet=attente.popleft()
if sommet not in marque :
visite.append(sommet)
marque[sommet]=1
for vois in graphe[sommet]:
if vois[0] not in marque:
attente.append(vois[0])
return visite
Algorithme de Dijkstra
on suppose dans cette partie que le graphe est connexe.
L’objectif de cette partie est de construire un algorithme pour trouver le plus court chemin entre deux sommets dans un graphe
pondéré orienté (ou non). Pour cela, on utilise un algorithme glouton qui repose sur le principe suivant : si dans un graphe pondéré,
2
le chemin i0 i1 ...ik ...i p est un plus court chemin entre i0 et i p alors i0 ...ik est un plus court chemin entre i0 et ik et ik ...i p est un plus
court chemin entre ik et i p .
Ainsi on cherche "localement" le plus court chemin à chaque étape, en partant du premier sommet. Pour cela, nous utiliserons une
file de priorité : c’est à dire que le sommet selectionné à chaque étape dans la file est celui prioritaire (ici celui ayant la plus petite
distance cumulée depuis le départ).
2 2
R1 R2 R3 R4
3 1
1 2 5 2
2 3
4 1
R5 R6 R7 R8
10
A B
2
1
4
6 1
F 6 C
1
35 4
E D
3
L’algorithme
Donnons maintenant l’algorithme :
def mini(dico):
’’’ au moins une des valeurs du dico ne doit pas être inf ’’’
m=float(’inf’)
for s in dico.keys() :
if dico[s] < m :
m=dico[s]
minimum=s
return minimum
def dijkstra(G,s):
’’’G est un graphe pondéré sous la forme d’un dictionnaire représentant sa liste d’adjacence, et s un
D={}
3
d={k : float(’inf’) for k in G}
d[s]=0
while len(d)>0:
som=mini(d)
for vois in G[som]:
voisin,dist=vois[0],vois[1]
if voisin not in D :
d[voisin] = min(d[voisin] , d[som] + dist)
D[som]=d[som]
del d[som]
return D
Analyse rapide : La terminaison est évidente, un variant de boucle étant tk la taille du dictionnaire d à l’étape k de la boucle
while.
La complexité est dans le pire des cas en O(n2 ) où n est le nombre de sommet (n étapes dans la boucle while et moins de n étapes
dans la boucle for).
Remarque : il suffit de modifier la condition d’arrêt par while fin in d pour que l’algorithme s’arrête quand l’on sait
la distance minimal de s à f in
A-star
Nous allons chercher un algorithme qui permet de trouver "un court chemin" entre deux sommets par une méthode heuristique.
L’idée d’une méthode heuristique est de donner un algorithme qui renvoie une solution non optimale mais convenable et qui est
obtenu en un temps raisonnable (ce qui est très utile dans les cas où aucun algorithme optimal n’est de complexité raisonnable).
Nous avons déjà mis en oeuvre ce principe dans le cadre des algorithmes "gloutons".
Pour cela, nous allons utiliser une fonction h, dont les données sont issues d’informations extérieures, qui prend en entrée un
sommet s du graphe et renvoie un flottant (ou un entier) positif. Cette fonction "rajoutera" à chaque étape une pondération pour
le choix du sommet prioritaire : l’idée étant d’éliminer des sommets qui semble (d’après les données extérieurs) inutile : on peut
pour cela utiliser une "distance à vol d’oiseau" comme dans l’exemple ci-dessous, ou un nombre correspondant à une estimation
du nombre de sommets à parcourir (comme dans l’exercice de fin), etc ....
def mini2(dico):
’’’ au moins une des valeurs du dico ne doit pas être inf ’’’
m=float(’inf’)
for s in dico.keys() :
d=dico[s][0]+dico[s][1]
if d < m :
m=d
minimum=s
return minimum
def astar(G,deb,fin,h):
’’’G est un graphe pondéré sous la forme d’un dictionnaire représentant sa liste d’adjacence, et s un
D={}
d={k : [float(’inf’),h(k)] for k in G}
d[deb]=[0,h(deb)]
while fin in d:
print(d)
som=mini2(d)
for vois in G[som]:
voisin,dist=vois[0],vois[1]
if voisin not in D :
d[voisin][0] = min(d[voisin][0] , d[som][0] + dist)
D[som]=d[som]
del d[som]
return D[fin][0]
def heur(s):
val_heur={’A’:9,’B’:7,’C’:8,’D’:8,’E’:0,’F’:6,’G’:3,’H’:6,’I’:4 , ’J’:4 , ’K’:3 , ’L’:6 , ’S’:10}
return val_heur[s]
print(dijkstra(G,’S’),astar(G,’S’,’E’,heur))
Exercices :
Exercice 1 : Avec un de vos voisins, donnez un graphe orienté pondéré de au moins 6 sommets et de au moins 10 arcs. Réaliser
chacun de votre côté l’algorithme de Dijkstra et comparer vos résultats.
Exercice 2 : En utilisant la fonction dijkstra, écrire une fonction qui prend en entrée un graphe non pondéré connexe et un
sommet s et renvoie un dictionnaire dont les clefs sont les sommets k du graphe et les valeurs le nombre minimal d’arcs à parcourir
pour aller de s à k.
Exercice 3 : Pour le graphe qui suit, appliquer à la main l’algorithme de Dijkstra pour obtenir le chemin le plus court reliant les
sommets R1 et R6. Faire de même avec l’algorithme A star en utilisant l’heuristique : la valeur associée à un sommet est 3 fois le
nombre d’arrêtes nécessaires pour atteindre ce sommet (on suppose ce nombre connu et on le multiplie par trois car on suppose
que la longueur moyenne d’une arrête est 3).
Le graphe est donné par sa liste d’adjacence :