Structures de données IFT-2000 Abder Alikacem Les graphes Semaine 5, suite et fin… Département...
-
Upload
roxanne-cheron -
Category
Documents
-
view
105 -
download
1
Transcript of Structures de données IFT-2000 Abder Alikacem Les graphes Semaine 5, suite et fin… Département...
Structures de donnéesIFT-2000
Abder AlikacemAbder Alikacem
Les graphes
Semaine 5, suite et fin…
Département d’informatique et de génie logiciel
Édition Septembre 2009
Deuxième partie
• Parcours d’un graphe par contagion (ou largeur)• Parcours d’un graphe par sondage (ou profondeur)• Description d’un graphe en terme de type abstrait
• Implantation
4
2
5
3
1
A
BCF
D
J K
GE
Rappel
Défilement d’un graphe
Deux manières standard d'exécuter cette opération: Parcours par contagion ou par largeur Breadth-First Search
ou BFS Parcours par sondage ou en profondeur(Depth-First Search
ou DFS)
Question: Est-ce que tout graphe peut être parcouru par ces 2
algorithmes ?
Spécifications de l’interface du type Graphe
Description en termes de types abstraits
1. ajouter un sommet à un graphe 2. ajouter un arc/arête à un graphe3. ôter un sommet du graphe et tous les arcs/arêtes associés4. ôter un arc/arête 5. consulter un sommet 6. voir si un chemin existe entre 2 sommets7. trouver le chemin le plus court (# arêtes/arcs, distance)8. trouver tous les chemins entre 2 sommets• Etc..
Implantation
Il existe deux principales méthodes de modélisation des graphes en tant que type abstrait:
les matrices et les listes dites de connectivité ou d'adjacence.
Le choix entre elles se fera en fonction de la densité du graphe. Par densité, on entend la proportion d'arcs effectifs par rapport au nombre maximum d'arcs ou d’arêtes possibles:
NbArcs / NbSommets2 dans le cas de graphe orienté. NbAretes / (NbSommets(NbSommets-1)/2) dans le cas d’un graphe non orienté
Interface
template <typename T>class Graphe {public:/** Constructeurs (graphe vide)*/ Graphe(); Graphe (const Graphe&); ~Graphe (); Graphe& operator = (const Graphe&);
void ajouterSommet(T s); void ajouterArc (T s1, T S2); void enleverArc (T s1, T s2); void enleverSommet ( T s); bool sommetExiste(T s) const; int nbSommets() const; // etc... private: //…};
class Sommet{public: …getData(); void setData(..); //etc..
private: … data; int no; int tag; bool pres;}
Classe Graphe
template <typename T>class Graphe {public: //..private:
int nbSommets;T* sommets;
int **mat; //implémentation dans une matrice} // de sommets adjacents
4
25
3
1
Cas d’un graphe orienté
Implantation dans une matrice d’adjacence
Classe Graphe
template <typename T>class Graphe {public: //..private:
int nbSommets;T* sommets;
int *mat; // tableau à une dimension
}
Cas d’un graphe non orienté
4
25
3
1
Implantation dans une matrice d’adjacence
1
1 1
1 1
1
0
1
2
3
4 1
1 1
1
1
1
11 11 11
( i + 1 ) * i / 2 + j
Linéariser la matrice
STL : Standard Template Library
Conteneurs: En C++, une classe contenant un ensemble d’éléments d'un certain type est appelée conteneur. Ce sont donc eux qui contiendront les informations que l’on veut stocker.
Itérateurs: utilisés pour parcourir les items qui se retrouvent dans les conteneurs, ils jouent un peu le rôle de pointeurs sur des éléments d’un conteneur.
Algorithmes: utilisés pour faire des traitements sur les éléments qui se retrouvent dans les conteneurs.
http://www.sgi.com/tech/stl/
Conteneurs Séquentiels
vector: tableau dynamique - extensible
- accès direct en O(1)- insertions et retraits à la fin en O(1)- insertions et retraits lents au milieu
list: liste doublement chaînée- insertions et retraits n’importe où en O(1)
- accès au début et à la fin en O(1)- accès lent aux autres éléments
STL : Standard Template Library
http://www.sgi.com/tech/stl/
Liste (list) et vecteur (vector)
Méthodes communes à vector et listpush_back(): Ajoute à la fin du conteneur. pop_back(): Enlève le dernier objet du conteneurback(): Retourne l'objet situé à la fin du conteneurfront(): Retourne l'objet situé au début du conteneur
Pour les conteneurs de type list seulementpush_front(): Ajoute au début du conteneurpop_front(): Enlève le premier objet du conteneur
Pour les conteneurs de type vector seulement
operator[] : Retourne l'objet situé à la position i du conteneurat (): Comme le précédent mais déclenche une exception out of range lorsqu'un indice est incorrect.resize(): Redéfini la taille du conteneurcapacity(): Retourne la capacité du conteneurreserve(): Redéfini la capacité du conteneur
Les itérateurs
#include <list>#include <iostream> using namespace std;
int main( ) { list <int> list1;
for (int i = 1; i<=40; i++) list1.push_back(i+i);
list <int> :: iterator i; //reverse_iterator //const_iterator
for (i = list1.begin( ); i!=list1.end( ); i++) cout <<*i << “ “ ; return 0 ;}
Les principaux opérateurs sont * donnant accès à la valeur, ++ et -- pour incrémenter et décrémenter une valeur.
-Opérateur * : Retourne l’élément de la position courante
-Opérateur ++ : Fait pointer l’itérateur à l’élément suivant
-Opérateur == : Indique si 2 itérateurs pointent sur le même élément
-Opérateur = : Assigne un itérateur
Pile (stack) et file (file)
L’adapteur de conteneurs
http://www.sgi.com/tech/stl/
Stack ( la pile )Peut être implémentée avec vector, list, (*)dequePrincipe LIFO : push et pop à la même extrémité
Queue ( la file )Dérive d’un (*)deque. Principe FIFO
(*)deque: tableau dynamique qui peut s’étendre par les deux extrémités.C’est un des conteneurs séquentiels de la STL
Pile (stack)
#include <stack>using namespace std;
stack<int> s;
s.push(1);
s.pop();
int n = s.size();
if(s.empty()){…}
int i = s.top();
Lib « stack » stack est générique
Déclaration (pile d’entiers)
Empiler
Dépiler
Taille de la pile
Pile vide?
Sommet de la pile
File (queue)
#include <queue>using namespace std;
queue<int > f;
int n =f.size();
f.push(10);
f.pop();
int tete =f.front();
int fin = f.back();
Lib « queue » queue est générique
Déclaration
Taille
Ajouter en tête
Supprimer en queue
Tête de la file
Fin de la file
Retour à la classe Graphe
template <typename T>class Graphe {public: //..private:
vector<T> sommets; // Les sommets vector< vector<int> > voisins; // Les sommets adjacents
}
Implantation dans une matrice d’adjacence
Utilisation de vector de la STL
Classe Graphe
template <typename T>class Graphe { public: //.. private:
private:class Noeud{ public:
T data; /* au lieu de T data, on peut avoir T* ou int */Noeud * suivant; /* chaînage des sommets adjacents */
};/* le noeud typique du graphe */int nbSommet; /* nombre de sommets dans le graphe */int nbSommetMax; /* nombre total possible de sommets */T * sommet; /* tableau représentant les sommets*/Noeud** listeSommet; /* les listes de sommets adjacents*/
}
Implantation dans une liste de sommets adjacents
2 3123
1
1
2
1
2 3Laboratoire#5
Classe Graphe
template <typename T>class Graphe { public: //.. private:
class Noeud { public: T data; //données
// dans un sommet list<int> voisins; };
vector<Noeud> listeSommets;}
Implantation dans une liste de sommets adjacents
Utilisation de vector et list de la STL
Classe Graphe
Implantation dans une liste de sommets adjacents
Cas d’un graphe non orienté
La liste des sommets adjacents
soufre de la même redondance que
nous avons rencontrer avec les matrices de sommets adjacents.
Classe Graphe
template <typename T>class Graphe { public: //.. private:
class AreteNode { public:
int sommet[2]; AreteNode * lien[2];
}; typedef AreteNode * AretePtr;
class Noeud { public: T data; AretePtr first; };vector<Noeud> listeSommets;
}
Cas d’un graphe non orienté
Solution: liste des arêtes
BB
4
Structures de donnéesIFT-2000
Département d’informatique et de génie logiciel
Édition Septembre 2009
Abder AlikacemAbder Alikacem
Les graphes
Semaine 6
Plan
Tri topologique Retour sur la connexité des graphes Algorithme de Dijkstra Algorithme de Bellman-FordAlgorithme A*
On cherche à déterminer dans quel ordre on enfiler ses vêtements pour s'habiller de la tête aux pieds. Sachant que :
Il faut d’abord enfiler son caleçon pour mettre ensuite ses chaussures, son pantalon et sa ceinture.
Pour mettre ses chaussures il faut avoir mis ses chaussettes et son pantalon.
Pour mettre sa ceinture il faut avoir enfile sa chemise. Pour mettre sa veste il faut avoir enfilé sa cravate et sa
ceinture. Pour mettre sa cravate il faut avoir mis sa chemise. On peut mettre sa montre n'importe quand!
Tri topologique : exemple
chettes slip pant. montre ch se ce cr vech res
slip chaussettes
pantalon chaussures
chemise
cravate
veste
ceinture montre
Graphe de dépendance
Graphe trié topologiquement
Tri topologique : exemple
Tri topologique
Idée : ordonner les sommets selon la préséance indiquée par les arcs
But : comparer deux sommets s1 et s2 pour savoir si l’un vient avant
l’autre
Précondition S est un graphe orienté et acyclique
Postcondition s1 vient avant s2 il y a un chemin de s1 à s2 et : s1 < s2
Tri topologique : trace de l’algorithme
(S)A
B
C
D
E
F
G
(a)
AB CD EFG
(b)
AB CDE FG
Retour sur la connexité des graphes
Un graphe non orienté G est connexe ssi il existe un chemin entre n’importe quelle paire de sommets distincts du graphe
si le graphe G n'est pas connexe, il apparaîtra comme un ensemble de sous-graphes connexes.
Chacun de ces sous-graphes forme une composante connexe maximale
Connexité des graphes orientés
Un graphe G est fortement connexe ssi pour n’importe quelle paire de sommets distincts (a,b) de G, il existe un chemin du sommet a au sommet
b et un autre chemin du sommet b au sommet a.
graphe G
a b
d c
e
graphe H
a b
d c
e
graphe I
a b
d c
e
Un graphe orienté G est faiblement connexe ssi son graphe non orienté sous-jacent G’ est connexe.
Problématique des plus courts chemins
Plus cours chemin pour tout couple de sommets (coûteux dans notre cas) Algorithmes de Floyd-Warshall.
Les plus courts chemins à origine unique Parcours exhaustif des nœuds Algorithme de parcours par largeur (BFS) (graphes non pondérés) Algorithme de Dijkstra (pondération positive). Algorithme de Bellman-Ford (pondération négative).
Comment trouver le plus court chemin entre 2 sommets s et t d'un graphe G ?
Algorithme de Dijkstra
3s
dc
b
a3
5
3
1 1 1
5
5
5s
dc
b
a3
5
3
1 1 1 3
5
5
5
3 8
4 9
0
Exemple. Plus court chemin entre s et i v (v : l’ensemble des sommets)
Pour chaque sommet i v , on maintient à jour un attribut yi : une estimation de la pondération d’un plus court chemin:
État initial : ys = 0 (s étant la source) yi = + avec i s
À chaque étape : essayer de minimiser les yi
État final : yi = lpcc(s,i) (lpcc: le plus court chemin)
L’algorithme est basée sur la technique de relâchement
RELÂCHER (a,b, c (a,b)) Si yb > ya + c (a,b) Alors yb ya + c (a,b)
ba
b
a
2
5 9
5 7
2
Relâcher(a,b, c(a,b))
ba
b
a
2
5 6
5 6
2
Relâcher(a,b, c(a,b))
Dans l’algorithme de Dijkstra, chaque arc est relâché exactement une
fois.
Algorithme de Dijkstra
Algorithme de Dijkstra
Soit V l’ensemble des sommets d’un graphes
Initialiser yi = + pour tous les sommets i
Initialiser ys = 0.
Initialiser S à l'ensemble vide, T = V.
Tant que T n'est pas vide
1.Sélectionner le sommet j de T de plus petite valeur yi
2.Faire T = T \ j et S = S j
3.Pour tous les sommets k de T adjacents à j, faire RELÂCHER(j, k, c(j,k))
Fin Tant que.Complexité ?
V :ensemble des sommets d’un graphe S : sommets dont les poids finaux de plus court
chemin à partir de la source ont été calculés. T: file pour gérer les sommets de v – S suivant
leur attribut yi .
RELÂCHER (a,b, c (a,b)) Si yb > ya + c (a,b) Alors
yb ya + c (a,b) P[b] a
Tous les P[i] sont initialisés à NIL au départ (i v )
Algorithme de Dijkstra et le chemin
f
dd
e
g
h
e
Pour aller d’un sommet A à un sommet B appliquer l’algorithme de Dijkstra avec le sommet A comme
source, en conservant pour chaque nœud le nœud origine à partir duquel sa plus petite distance de la source a été calculée.
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
a b c d e f
0
- - - - - -
V, S, T
Y
P
V: ensemble des sommetsS: les sommets solutionnésT: une file d’attente (suivant le coût)Y: tableau des coûtsP: tableau des sommets précédents
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
a b c d e f
0
- - - - - -
V, S, T
Y
P
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
2 (a)
4 (a)
a c b d e f
0 2 4
- a a - - -
V, S, T
Y
P
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
2 (a) 12 (c)
3 (c) 10 (c)
a c b d e f
0 2 3 10 12
- a c c c -
V, S, T
Y
P
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
2 (a) 12 (c)
3 (c) 8 (b)
a c b d e f
0 2 3 8 12
- a c b c -
V, S, T
Y
P
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
2 (a) 10 (d)
3 (c) 8 (b)
14 (d)
a c b d e f
0 2 3 8 10 14
- a c b d d
V, S, T
Y
P
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
2 (a) 10 (d)
3 (c) 8 (b)
13 (e)
a c b d e f
0 2 3 8 10 13
- a c b d e
V, S, T
Y
P
Algorithme de Dijkstra
a
db
f
c e
4 56
310
2
81 20
2 (a) 10 (d)
3 (c) 8 (b)
13 (e)
a c b d e f
0 2 3 8 10 13
- a c b d e
V, S, T
Y
P
s
c
b
a3
121 1
6
2
Algorithme de Dijkstra
Autre exemple
s
c
b
a3
121
1
6
2
3,s 9,a
11,b
0
s
c
b
a3
121
1
6
2
Pause, profitons pour nous amuser!
0
Appliquons le principe du relâchement à chaque arc de ce graphe, l’ordredes arcs est choisi aléatoirement. Un arc relâché sera mis avec une autrecouleur.
s
c
b
a3
121
1
6
2
1er tour…
0
s
c
b
a3
121
1
6
2
0
1er tour…
s
c
b
a3
121
1
6
2
3
0
1er tour…
s
c
b
a3
121
1
6
2
3
0
1er tour…
s
c
b
a3
121
1
6
2
3
0
1er tour…
s
c
b
a3
121
1
6
2
3
0
1er tour…
On a relâché tous les arcs. Recommençons une nouvelle fois. Un arc relâché sera mis cette fois en bleu
s
c
b
a3
121
1
6
2
3
0
2ième tour…
s
c
b
a3
121
1
6
2
3
0
2ième tour…
s
c
b
a3
121
1
6
2
3 9
0
2ième tour…
s
c
b
a3
121
1
6
2
3 9
0
2ième tour…
s
c
b
a3
121
1
6
2
3 9
0
2ième tour…
s
c
b
a3
121
1
6
2
3 9
11
0
2ième tour…
s
c
b
a3
121
1
6
2
Un autre tour?
3 9
11
0
On arrête ce jeu!
s
c
b
a3
121
1
6
2
Un autre tour?
3 9
11
0
s
c
b
a3
121
1
6
2
3 9
11
0
Résultat suite à l’exécution de l’algorithme de Dijkstra
Résultat à l’issu de l’exécution de l’algorithme de notre jeu …Il s’agit en fait de la simulationDe l’algorithme de Bellman-Ford! Limité à des poids
positifsPoids positifs et négatifs
Problématique des arcs de poids négatifs
Algorithme de Bellman-Ford
s
c
a3
56 -3
Si un graphe ne contient aucun circuit de poids négatif accessible à partir d’une origine s, alors, pour tout sommet i v, le poids du plus court chemin reste bien défini, même si sa valeur est négative.
S’il existe un circuit de poids négatif
accessible depuis s, le poids du plus court
chemin n’est pas correctement défini.
Aucun chemin entre s et un sommet du
circuit ne peut être plus court, on peut toujours trouver un encore plus court!
s
c
a3
53 -6
Algorithme de Bellman-Ford
Complexité ?
Soit le graphe G(V,E)
Initialiser yi = + pour tous les sommets i
Initialiser ys = 0.
Répéter |V| - 1 FOIS
Pour tout arc (u,v) de E
faire RELÂCHER(u, v, c(u,v))
Pour tout arc (u,v) de E faire
Si yv > yu + c(u,v) Alors
Retourner FAUX
Retourner VRAI
Faire les étapes nécessaires pour faire converger le
poids des chemins sachant qu’un plus court chemin de s
à tout autre sommet est un chemin d’ordre au plus n -1 arcs.
Vérifier s’ils ont tous convergé.
Retourner VRAI si c’est le cas.
Retourner FAUX sinon.
s
dc
b
a3
5
3
1 1 1 -3
5
5
5
0s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 6
4 9
0
Étape 1 relaxation de tous les arcs dans l’ordre :(s,a) (s,c) (a,b) (a,c) (b,d) (c,a) (c,b) (c,d) (d,b) (d,s)
Exemple 1
Algorithme de Bellman-Ford
s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 4
4 7
0s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 6
4 9
0
Étape 2 relaxation de tous les arcs dans l’ordre :(s,a) (s,c) (a,b) (a,c) (b,d) (c,a) (c,b) (c,d) (d,b) (d,s)
Algorithme de Bellman-Ford
s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 2
4 5
0s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 4
4 7
0
Étape 3 relaxation de tous les arcs dans l’ordre :(s,a) (s,c) (a,b) (a,c) (b,d) (c,a) (c,b) (c,d) (d,b) (d,s)
Algorithme de Bellman-Ford
s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 0
4 3
0s
dc
b
a3
5
3
1 1 1 -3
5
5
5
3 2
4 5
0
Étape 4 relaxation de tous les arcs dans l’ordre :(s,a) (s,c) (a,b) (a,c) (b,d) (c,a) (c,b) (c,d) (d,b) (d,s)
Cycle de coût négatif: réduction encore possible !
Algorithme de Bellman-Ford
Bellman-Ford revu….
Soit le graphe G(V,E)
Initialiser yi = + pour tous les sommets i
Initialiser ys = 0.
K 1
Répéter
Stable VRAI
Pour tout arc (u,v) de E
faire RELÂCHER(u, v, c(u,v))
Si …Alors Stable FAUX
K k + 1
Tant que Stable est FAUX ET k < n+1
Si non Stable alors présence d’un circuit de poids négatif
s
dc
b
a3
5
-3
1 -1 -1 3
5
5
5
0
Exemple 2
Étape 1 relaxation de tous les arcs dans l’ordre :(s,a) (s,c) (a,b) (a,c) (b,d) (c,a) (c,b) (c,d) (d,b) (d,s)
s
dc
b
a3
5
-3
1 -1 -1 3
5
5
5
3 8
4 7
0
Algorithme de Bellman-Ford
s
dc
b
a3
5
-3
1 -1 -1 3
5
5
5
3 8
4 7
0
Étape 2 relaxation de tous les arcs dans l’ordre :(s,a) (s,c) (a,b) (a,c) (b,d) (c,a) (c,b) (c,d) (d,b) (d,s)
Pas de réduction possible : coûts corrects!
s
dc
b
a3
5
-3
1 -1 -1 3
5
5
5
3 8
4 7
0
Algorithme de Bellman-Ford
Algorithme A*
Coût total (F) = Coût depuis la source(G) + Coût vers la destination(H)
Principe général : évaluation du coût total d’un sommet
Pourquoi évaluer un coût vers la destination ?
Afin de resserrer l’ensemble des sommets à explorer en privilégiant les sommets « qui semblent » nous rapprocher de la destination.
Conséquencel’algorithme A* est plus performant que n’importe quel autre algorithme puisqu’il diminue l’ensemble des sommets à explorer.
Comment évaluer un coût vers la destination ?
En utilisant des heuristiques (prédictions) afin d’évaluer un coût vers la destination INFERIEUR au coût réel (encore inconnu). À ce titre, A* est un algorithme dit optimiste.
Algorithme A*
Distance euclidienne
S
D
40
H 20
Théorème de Pythagore
H 2 = (Coté oppose) 2 +
(Coté adjacent) 2
H 2 = 40 2 + 20 2
= 2000
H = 20 x (5) 1/2
Algorithme A*
Distance de Manhattan
S
D
Nombre de cellules, en horizontal et en vertical entre la source et la destination.
Plus conforme à la nature des déplacements autorisés (haut, bas, gauche, droite)
Retour sur les piles
p1
pile
debutcpt
cpt
tab
suiv
nœud... en-tête(header)
114 4 3
Pile, modèle hybride
Graphe, lab#6 /**
* Algorithme de Warshall */
template <typename T>Graphe<T> fermetureGraphe(Graphe<T> g);
Graphe (int nbSommet);Graphe (const Graphe&); Graphe(const Graphe& g, std::vector<T>&);~Graphe (); Graphe& operator = (const Graphe&);void ajouterSommet(T s) ;void ajouterArc (T s1, T S2);void enleverArc (T s1, T s2);void enleverSommet ( T s);bool sommetExiste(T s);bool arcExiste ( T s1, T s2);int nbSommets();void affiche();std::vector<T> listerSommetsGraphe();int ordreEntreeSommet(T sommet);std::vector<T> listerSommetsAdjacents(T sommet);int ordreSortieSommet(T sommet) ;
Algorithme de Warshall{Soit A, un graphe orienté}
P APour k = 1, 2, ..., n Pour i = 1, 2, ..., n Pour j = 1, 2, ..., n
Pij Pij ou (Pik et Pkj)
Graphe, lab#6
Algorithme de Warshall{Soit A, un graphe orienté}P APour k = 1, 2, ..., n Pour i = 1, 2, ..., n Pour j = 1, 2, ..., n
Pij Pij ou (Pik et Pkj)
template <typename T>Graphe_Lab7::Graphe<T> fermetureGraphe (Graphe_Lab7::Graphe<T> g) { Graphe<T> fermG(g); vector<T> v = fermG.listerSommetsGraphe(); int nb = fermG.nbSommets(); for (int k = 0; k < nb; k++)
for (int i = 0; i < nb; i++)for (int j = 0; j < nb; j++)
if (fermG.arcExiste(v[i], v[j]) == false){
if ((fermG.arcExiste(v[i], v[k]) == true) && (fermG.arcExiste(v[k], v[j]) == true)){
fermG.ajouterArc(v[i], v[j]);}
}return fermG;
}