Vous êtes sur la page 1sur 5

I.T.C.

: Les graphes : graphes pondérés et recherche des distances


minimales.

Rappels sur la modélisation


On rappelle qu’un graphe pondéré (orienté par exemple) est représenté en python de 3 manières différentes :
1. Une liste contenant deux listes, une liste contenant les sommets du graphes, et une liste contenant les arcs (ou arrêtes) du
graphes, elle même représentées par une liste de taille 3 la troisième valeur représentant la pondération).
2. Un dictionnaire dont les clefs sont les sommets et les valeurs la liste des voisins du sommet avec la pondération du chemin
associé (donc une liste de liste). On parle de liste d’adjacence.
3. Une liste des sommets et une liste de liste représentant la matrice d’adjacence.
On peut écrire une fonction qui prend en entrée une liste de sommets et une liste d’arrêtes (une liste de liste donc) d’un graphe non
orienté et renvoie le dictionnaire associé à la liste d’adjacence.

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

Rappels des parcours pour les graphes pondérés


On rappelle ici les algorithmes de parcours en profondeur et en largeur, dans le cas d’un graphe pondéré.

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).

Quelques remarques sur le fonctionnement


1. Chercher le plus petit nombre d’arrête revient juste à associé à chaque arrête un poid de 1 et aux autres 0.
2. Pour chercher le chemin le plus court entre deux sommets Depart et Fin, nous allons devoir chercher le plus court chemin
de Depart à tous les autres sommets du graphe.
3. Nous nous autoriserons l’usage de certaines commandes : une, essentielle, est del dico[sommet] qui supprime la clef
sommet (et la valeur associée) du dictionnaire dico et min(a,b) qui renvoie le minimum entre deux flottants ou entiers.
4. Néanmoins, nous devrons construire une fonction qui prend en entrée un dictionnaire dont les clefs sont les sommets et les
valeurs la distance cumulée de ces sommets, et renvoie le sommet ayant la plus petite distance cumulée.

Le fonctionnement sur un exemple


Réalisons l’algorithme "à la main" sur une feuille avec le graphe suivant, nous stockerons les distances dans un tableau.

2 2
R1 R2 R3 R4

3 1
1 2 5 2
2 3

4 1
R5 R6 R7 R8

De même avec le graphe orienté suivant :

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]

Réalisons l’exemple suivant : on considère la graphe donnée par sa liste d’adjacence :


4
G={}
G[’S’]=[[’A’,7],[’B’,2],[’C’,3]]
G[’A’]=[[’S’,7],[’B’,3],[’D’,4]]
G[’B’]=[[’S’,2],[’A’,3],[’D’,1],[’H’,1]]
G[’C’]=[[’S’,3],[’L’,2]]
G[’D’]=[[’A’,4],[’B’,4],[’F’,5]]
G[’E’]=[[’G’,2],[’K’,5]]
G[’F’]=[[’D’,5],[’H’,3]]
G[’G’]=[[’H’,2],[’E’,2]]
G[’H’]=[[’B’,1],[’F’,3],[’G’,2]]
G[’I’]=[[’J’,6],[’K’,4],[’L’,4]]
G[’J’]=[[’I’,6],[’K’,4],[’L’,4]]
G[’K’]=[[’I’,4],[’J’,4],[’E’,5]]
G[’L’]=[[’C’,2],[’I’,4],[’J’,4]]

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 :

G={ ’R1’ : [[’R2’,2],[’R3’,2],[’R4’,3]],


’R2’ : [[’R1’,1],[’R4’,4],[’R7’,4]],
’R3’ : [[’R1’,2],[’R5’,5],[’R4’,2]],
’R4’ : [[’R1’,3],[’R2’,4],[’R3’,2],[’R6’,4],[’R7’,5]],
’R5’ : [[’R3’,5],[’R6’,4]],
’R6’ : [[’R4’,4],[’R4’,4],[’R7’,3]],
’R7’ : [[’R2’,4],[’R5’,5],[’R6’,3]],
}

Vous aimerez peut-être aussi