204 21 7MB
French Pages 229 Year 2017
À LA DÉCOUVERTE
DES GRAPHES
ET DES ALGORITHMES
DE GRAPHES Christian Laforest
Imprimé en France ISBN : 978-2-7598-1830-3 Tous droits de traduction, d’adaptation et de reproduction par tous procédés, réservés pour tous pays. La loi du 11 mars 1957 n’autorisant, aux termes des alinéas 2 et 3 de l’article 41, d’une part, que les « copies ou reproductions strictement réservées à l’usage privé du copiste et non destinées à une utilisation collective », et d’autre part, que les analyses et les courtes citations dans un but d’exemple et d’illustration, « toute représentation intégrale, ou partielle, faite sans le consentement de l’auteur ou de ses ayants droit ou ayants cause est illicite » (alinéa 1er de l’article 40). Cette représentation ou reproduction, par quelque procédé que ce soit, constituerait donc une contrefaçon sanctionnée par les articles 425 et suivants du code pénal.
© EDP Sciences 2017
Table des matières 1 Présentation
1
2 Un graphe. Qu'est ce que c'est ?
7
3 Parcourons un graphe en largeur
17
4 Parcourons un graphe en profondeur
35
5 Un arbre très léger
41
6 Construisons un arbre à partir d'une suite de degrés
49
7 Dessinons un graphe dans le plan sans croiser les arêtes
55
8 Passons une seule fois par chaque arête
65
9 Passons une seule fois par chaque sommet
73
10 Travaillons ensemble
83
11 Les ots : un problème de plomberie informatique
91
12 Fabriquons une notice de montage
107
13 À vous de jouer !
115
14 Des problèmes très diciles à résoudre
131
15 Colorions les graphes
137
16 Des couplages
147
17 Une petite couverture
151
18 Le problème du voyageur de commerce
167
19 Retour sur l'arbre léger
179
iv
Graphes et algorithmes
20 Un arbre couvrant minimisant la somme des distances
191
21 Découper un graphe en deux grâce à une pièce de monnaie
195
22 Un avenir incertain
201
23 Autres problèmes et autres approches
211
24 Quelques références et compléments
219
Index
223
Table des matières
v
Résumé visuel des principales notions utilisées dans ce livre. Un graphe = des sommets et des arêtes.
Figure 1: Un graphe avec cinq
sommets
et six arêtes
u, v est l'arête entre u et v .
Voisins d'un sommet, degré d'un sommet.
Deux sommets u et v sont voisins si le graphe contient l'arête u, v. Par exemple, dans le graphe de la gure 1, les sommets 1 et 5 sont voisins, comme les sommets 5 et 3, ainsi que 3 et 4, etc. En revanche, 2 et 4 ne sont pas voisins (il n'y a pas d'arête entre 2 et 4). Le degré d'un sommet dans un graphe est son nombre de voisins. Par exemple, le sommet 5 est de degré 4, le sommet 1 est de degré 1.
Chemin, cycle.
Graphes et algorithmes
vi
Graphe connexe. Un graphe est connexe s'il existe un chemin entre chaque
paire de sommets.
Arbre, arbre couvrant un graphe. Un arbre est un graphe connexe sans
cycle.
Couplage. Un couplage est un ensemble d'arêtes qui n'ont aucun sommet en
commun.
1
Présentation
De manière directe ou indirecte, chacun d'entre nous utilise de nos jours un logiciel, un produit ou un service numérique. Malgré cela, certaines personnes pensent que l'informatique n'est qu'un outil plus ou moins neutre, une sorte de gadget juste bon à faire la fortune de quelques étudiants qui ont débuté dans un garage en Californie. Si c'est votre cas, je vous propose une expérience de pensée. Imaginez que l'on arrête, d'un coup, tous les ordinateurs du pays. Tous les ordinateurs, cela veut dire bien sûr les ordinateurs de bureau mais aussi ceux qui font vivre les réseaux (Internet, téléphone), les serveurs de données (des banques, des assurances, des réseaux de distribution et de vente, de la santé, etc.), mais aussi les ordinateurs embarqués comme les smartphones, les outils de type GPS, etc. On imagine bien que nos activités et nos vies seraient gravement perturbées, pour le moins. Il faut bien se rendre compte que tous ces services informatiques, ces logiciels, ces réseaux, ces objets connectés ont été imaginés puis conçus, programmés, testés, déployés, commercialisés, maintenus, améliorés, sécurisés, etc., par des armées d'informaticiens qui ÷uvrent souvent dans l'ombre. L'image que le grand public a d'une profession se construit souvent à partir de personnages vus dans des lms, généralement des ctions hollywoodiennes ou des séries télévisées. Quel est l'informaticien type dans ces représentations ? Généralement un homme, plutôt jeune, plutôt socialement inadapté, se nourrissant de pizzas, buvant des litres de boissons gazeuses sucrées dans son antre dans lequel règne un chaos de machines, de ls et de cartes électroniques. De ces représentations ctives, beaucoup de personnes retiennent que son travail se réduit à la programmation et qu'il passe ses journées à taper frénétiquement sur un clavier. C'est loin d'être le cas pour nombre d'entre eux (même si c'est vrai pour certains). La programmation est bien sûr une activité informatique mais ce n'est pas la seule et ce n'est pas la première (en tout cas, ça ne devrait pas l'être). Avant d'écrire le code d'un logiciel, il faut savoir comment faire les opérations qui sont demandées. Il faut un plan. L'analogie (classique) avec la cuisine. En cuisine, si vous voulez faire un plat il vous faut une recette (le plan du plat). Vous suivez alors les instructions ligne à ligne pour préparer ce dont vous avez envie à partir des ingrédients. Ce livre va
Graphes et algorithmes
2
vous révéler les secrets de quelques recettes informatiques. C'est ce que l'on appelle un algorithme, c'est-à-dire une méthode (la recette) qui permet de construire une solution (cuisiner un plat) à partir de données (les ingrédients) en suivant les instructions pas à pas. Cet algorithme pourra ensuite être programmé puis intégré dans un ensemble plus vaste qui va constituer une solution informatique globale (le repas complet). La comparaison entre les algorithmes et la cuisine est un véritable cliché, mais elle est tellement parlante qu'il serait dommage de s'en passer. Les livres sur les algorithmes sont nombreux mais ils s'adressent aux experts (étudiants en informatique, ingénieurs, etc.). Dans ces ouvrages, ils sont décrits dans des langages proches de ceux de programmation, ce qui est normal puisqu'ils s'adressent à des gens qui vont devoir les programmer. Ils rebutent donc le grand public non initié, comme tout livre technique pointu dans quelque domaine que ce soit. Les algorithmes sont nombreux, bien trop nombreux et diversiés pour une courte présentation. Il y a des algorithmes pour traiter des textes (par exemple pour rechercher de manière ecace un mot dans un long chier), pour traiter des images (construire des images de synthèse ou analyser de manière automatique des photos), pour traiter des données : les compresser (en réduire le volume ), les sécuriser (cryptographie), les analyser (extraction de données pertinentes dans de grandes masses de données), etc.
Vulgarisation algorithmique.
L'ambition de ce livre est de vous présenter bon nombre de résultats et d'algorithmes en vous décrivant les idées principales de ces méthodes, sans utiliser de jargon technique. Il sura de vous laisser guider par les explications pour en comprendre la substantique moelle . De nombreux exemples et illustrations éclaireront le propos. Inutile d'avoir un ordinateur pour lire ce livre mais quelques feuilles de papier, un crayon et une gomme vous seront utiles si vous voulez reproduire les exemples ou en créer d'autres (ce que je vous conseille de faire si vous désirez approfondir le sujet). Je sais d'expérience qu'un tel livre ne se lit pas forcément comme un roman, de manière linéaire, de la première à la dernière page, dans l'ordre. Peut-être lirez-vous quelques passages, puis vous poserez l'ouvrage, pour le reprendre plus tard. Pour faciliter ce cheminement, les chapitres sont volontairement courts et en grande partie indépendants, de manière à ce que vous puissiez les lire à votre rythme. De plus, les passages qui demandent un peu plus de calculs ou nécessitent des raisonnements un peu plus complexes sont dans des parties bien identiées, des zones colorées, comme ci-dessous.
Zone orange Une zone orange contient des raisonnements moins immédiatement accessibles.
Zone rouge Une zone rouge contient des explications ou des calculs encore plus pointus.
1. Présentation
3
La diculté de lecture d'une zone orange ou rouge dépend forcément de vos connaissances préalables sur le sujet ou de votre aisance naturelle à appréhender de nouveaux concepts. Un conseil : essayez de les lire. Si vous éprouvez trop de dicultés, sautez-les et revenez-y plus tard.
Les graphes. Le choix fait ici est de se focaliser sur la manipulation d'un objet abstrait qui est le graphe. La raison est simple : les graphes sont partout. Imaginez une soirée organisée par un ami, appelons-le Paul. Parmi les invités, il y a des gens que vous connaissez bien, vos amis communs avec Paul et les autres. Certains d'entre eux se connaissent, même si vous, vous ne les connaissez pas. Faisons une expérience. Si deux personnes sont amies, elles vont se relier l'une à l'autre par un long l de laine. Par exemple, si Zoé a trois amis présents à cette soirée, elle sera à l'extrémité de trois ls la reliant à chacun de ses trois amis. La longueur des ls importe peu. Essayez de visualiser la situation. Pour vous aider, voici un schéma d'une telle (petite) rencontre. Un l de laine est représenté par un trait.
Le nombre de ls peut être très important. Si la soirée réunit un groupe de quinze amis qui se connaissent très bien, chaque personne sera ainsi à une des deux extrémités de quatorze ls. Même si l'appartement est grand, ils vont avoir du mal à se déplacer car le nombre total de l sera de . . . (je vous laisse y rééchir, la réponse est dans un des chapitres). Formellement, un graphe est la donnée d'un ensemble d'éléments, nommés les sommets dans notre exemple ce sont les personnes présentes à la soiréeet d'un ensemble de relations entre ces éléments, nommées les arêtes. Dans notre exemple, une arête est matérialisée par un l de laine et représente une relation d'amitié entre les deux personnes reliées.
4
Graphes et algorithmes
Ces dernières décennies, les graphes ont été utilisés dans l'industrie et la science pour représenter, modéliser, manipuler toutes sortes d'objets, de natures diérentes. Voici quelques exemples. Des pages web : chaque page est un sommet et une relation (orientée) est placée d'une page A vers une page B si la page A contient un lien qui permet d'aller directement sur la page B . Les moteurs de recherche utilisent ce type de modélisation pour présenter les pages les plus pertinentes, répondant au mieux aux critères de recherche des internautes. Des réseaux informatiques : chaque ordinateur est un sommet et chaque liaison directe entre deux machines est représentée par une arête. Cette carte (qui peut être changeante) sert de base aux opérations de routage (trouver une route entre deux points pour acheminer un message).
Des réseaux sociaux, réels ou virtuels, issus du domaine professionnel, associatif, politique, économique, etc. La représentation sous forme de graphe de ces réseaux permet par exemple de les analyser en déterminant des communautés ou les personnes qui sont les plus inuentes. Des réseaux de distribution de marchandises ou d'énergie. Ici la représentation des points de production, de transformation, de distribution, etc. (sommets), et les moyens d'acheminement des marchandises (arêtes) sert à organiser et à optimiser les ux de production et de distribution. Des plans. Lorsque vous prenez l'avion, la brochure publicitaire de la compagnie que vous trouvez dans la pochette du siège représente ses liaisons aériennes sous forme de graphe : un aéroport est un sommet et une arête est une liaison. Un plan de métro est aussi un graphe. Des relations entre des informations. Les programmes informatiques manipulent des données, parfois très volumineuses. Il faut les organiser de manière à ce que les ajouts, suppressions ou modications soient très rapides. Cela est le domaine des structures de données où les éléments sont mis dans des cellules, reliées par des relations pour les organiser. Le tout peut être vu comme un graphe.
1. Présentation
5
Les graphes sont virtuellement partout. Il en existe d'ailleurs de plusieurs types (orientés, pondérés, hypergraphes, multigraphes, etc.) pour tenir compte des spécicités des situations. Ils sont exploités pour organiser des structures (optimiser les réseaux de distribution), pour essayer de les comprendre (dynamique des réseaux sociaux), pour aider à les sécuriser (chercher des routes alternatives lors de pannes ou d'attaques), pour rechercher des informations (dans des structures de données), etc. Pour certaines personnes (des ingénieurs par exemple), les graphes sont un outil, pour d'autres c'est un objet d'étude et de recherche (plusieurs thèses sur les graphes sont soutenues chaque année en France). Cet outil abstrait est très simple à décrire et consiste comme nous l'avons dit en un ensemble d'éléments et de relations entre ces éléments. Les algorithmes qui sont au menu (pour rester dans l'analogie culinaire) de ce livre manipulent des graphes. C'est ce que nous découvrirons pas à pas.
Les apparences sont trompeuses. La recherche en marche. Nombre de problèmes de graphes, simples à décrire, sont, en pratique, très diciles à résoudre. Ce point est passionnant et, pour tout dire, nalement assez intriguant. Pour contourner cette diculté, des programmes de recherche universitaires ont été mis en ÷uvre. Ce livre présente dans quelques chapitres une voie explorée, nommément les algorithmes d'approximation. Certains sont susamment simples pour pouvoir être décrits ici. Ces méthodes s'appuient souvent sur celles, plus classiques, vues dans les premiers chapitres. Ordre de lecture des chapitres. Il est plutôt conseillé de lire les chapitres dans l'ordre des numéros mais ce n'est pas une obligation. Comme cela a été mentionné plus haut, ils ont été écrits pour être en grande partie indépendants. Cependant il est préférable de lire le chapitre 2 en premier car il présente la notion centrale de graphe, donne des illustrations pour vous familiariser avec le sujet et présente quelques résultats qui seront utiles dans d'autres chapitres. Les notions importantes pour comprendre un chapitre sont rappelées, même si elles ont été vues dans un précédent chapitre, de manière à vous éviter de faire des retours. Cela induit quelques redites mais vous permettra aussi de les revoir sous un autre angle, avec de nouveaux exemples. Pour lire les derniers chapitres, il est fortement conseillé d'avoir lu auparavant le chapitre 14. Bon voyage. Ce livre est pour vous, même (et surtout) si vous ne connaissez rien à ces domaines. Bon voyage dans le monde des graphes et des algorithmes de graphes.
2
Un graphe. Qu'est-ce que c'est ?
Dans ce chapitre, nous allons voir ensemble des éléments qui seront utiles tout au long de ce livre. Nous verrons qu'il est facile de dessiner un graphe et de le manipuler à la main . Mais commençons par la base. Un graphe est composé de deux choses : un ensemble d'éléments, qui sont appelés les sommets du graphe ; un ensemble de relations entre ces éléments. Plusieurs types de relations sont possibles mais nous allons nous focaliser principalement (pas exclusivement) dans ce livre sur les relations non orientées qui sont nommées les arêtes du graphe. Rien qu'avec cela, nous avons déjà largement de quoi faire une belle promenade. Voyons tout cela un peu plus en détail avant de donner quelques exemples.
Qu'est-ce qu'un sommet ? Encore une fois, un sommet est simplement un élément, faisant partie d'un ensemble : l'ensemble des sommets d'un graphe. Dans ce livre, nous allons nous restreindre aux graphes ayant un nombre ni de sommets (les graphes innis sont aussi étudiés mais nous n'en parlerons pas du tout). Chaque sommet va avoir un nom, un identiant, qui lui est propre et qui permet de le distinguer des autres. Ce sera souvent un numéro dans les divers exemples qui seront donnés. Mais ce n'est pas obligatoire. Si vous voulez représenter le graphe des stations de métro de la ville de Paris, les sommets peuvent avoir les noms des stations. Qu'est-ce qu'une arête ? Une arête entre les sommets u et v d'un graphe sera notée u, v dans ce livre. L'arête u, v traduit le fait que les deux éléments/sommets u et v sont en relation l'un avec l'autre, ils sont liés par cette arête. L'ordre de la relation n'est pas important ici. L'arête u, v est la même que v, u. Dans certains ouvrages, une arête u, v est notée uv ou {u, v} ou R(u, v). La nature ou l'origine d'une relation entre u et v importe peu ici. Le point important pour la dénition d'un graphe est de savoir s'il y a ou non une arête entre u et v.
Graphes et algorithmes
8
Entre chaque paire de sommets, il y a soit une soit zéro arête, ni plus, ni moins. Les graphes que nous allons manipuler ont donc un ensemble ni de
sommets, par exemple : {1, 2, 3, 4, 5, 6}. Entre chaque paire de sommets, il y a soit une arête soit zéro arête. Une arête sera toujours pour nous dans ce livre une relation entre exactement deux sommets (il n'y a pas d'arête entre u et lui-même). Un graphe sera noté G = (V, E) où V représente l'ensemble des sommets ( vertices en anglais) et E représente l'ensemble des arêtes ( edges en anglais).
Représentation graphique des graphes. Un énorme avantage de la théorie des graphes par rapport à d'autres domaines des mathématiques est qu'ici, la représentation graphique des objets est simple (s'ils ne sont pas trop gros) et sert très souvent de support à l'intuition. Nous allons pleinement en proter. Les sommets du graphe vont être représentés par des points sur la feuille de papier. Un sommet va volontairement être grossi de manière à pouvoir voir son nom, son numéro. Une arête entre deux sommets u et v sera représentée par un trait entre les deux sommets. Les traits ne sont pas nécessairement droits et peuvent se croiser (sauf dans le chapitre 7 de ce livre). Il faut impérativement représenter toutes les arêtes, sans ambiguïté. Le placement des sommets n'est pas imposé. L'essentiel est de bien distinguer tous les éléments du graphe. ILLUST RAT ION Considérons le graphe G = (V, E) représenté sur la gure 2.1.
Figure 2.1: Votre premier graphe Ses sommets sont 1, 2, 3, 4, 5 et 6 et il a sept arêtes. Il y a par exemple les arêtes 2, 4, 3, 6. En revanche, il n'y a pas d'arête entre 2 et 3, ni entre 1 et 4. La représentation graphique est arbitraire. Ce graphe peut aussi être dessiné comme sur la gure 2.2. Ce sont exactement les mêmes sommets et les mêmes arêtes mais représentées sous la forme d'un autre dessin. Notez au passage que sur la gure 2.2, les arêtes 2, 4 et 3, 5 se croisent, ce qui n'a pas d'importance.
2. Un graphe. Qu'est-ce que c'est ?
9
Figure 2.2: Une autre représentation graphique du graphe de la gure 2.1
Des voisins. Le degré d'un sommet.
Si un graphe G contient l'arête u, v, alors u et v sont dits voisins (l'un de l'autre) ; ils partagent la même arête, ils sont liés directement l'un à l'autre. Par exemple dans le graphe de la gure 2.2, le sommet 6 est voisin de 4 (mais aussi de 3 et de 2), le sommet 1 a comme unique voisin le sommet 5. Le nombre de voisins d'un sommet u dans un graphe G est son degré noté degG (u). Par exemple, le sommet 3 est de degré 2, le sommet 1 est de degré 1.
Les chemins d'un graphe.
On constate que tous les sommets ne sont pas forcément voisins les uns des autres. Par exemple, dans le graphe de la gure 2.2, les sommets 2 et 3 ne sont pas voisins. Mais on peut, malgré tout, aller de 2 vers 3 (ou de 3 vers 2) en suivant plusieurs arêtes, par exemple la suite d'arêtes : 2, 5, 5, 3 ou la suite, plus longue, 2, 4, 4, 6, 6, 3. Cela nous conduit vers la notion de chemin qui est très importante. On ne va pas en donner une dénition formelle mais une idée intuitive. Pour cela, imaginons un personnage que l'on nommera (on le retrouvera plusieurs fois dans cet ouvrage) qui a la capacité de se promener sur un graphe. Un graphe G = (V, E) contient un chemin entre u et v si est capable, en partant du sommet u et en passant de voisin en voisin (en suivant des arêtes), d'aller jusqu'au sommet v . Lors de ce parcours, ne doit pas passer plusieurs fois par la même arête ou le même sommet (c'est un chemin qui est dit élémentaire ). La longueur d'un chemin est son nombre d'arêtes, c'est-à-dire le nombre de pas que fait notre personnage pour aller d'une extrémité à l'autre.
Graphix Graphix
Graphix
Cas particulier : chemin de longueur .
0 Par convention, il y a toujours un chemin entre n'importe quel sommet u et lui-même, sans arête, de longueur 0. ILLUST RAT ION La gure 2.3 (a) représente un graphe. La gure 2.3 (b) représente un chemin entre 1 et 9, de longueur 6 (car il contient les 6 arêtes en pointillés). Ce chemin peut être identié par la suite de ses sommets, ici 1, 2, 6, 10, 7, 5, 9 ou, comme cela est représenté graphiquement sur la gure, par la suite de ses arêtes, ici 1, 2, 2, 6, 6, 10, 10, 7, 7, 5, 5, 9.
Graphes et algorithmes
10
Figure 2.3: (a) Un graphe. (b) Un chemin de longueur 6 entre les sommets 1 et 9 Sur la gure 2.4, un autre chemin entre 1 et 9 est représenté en pointillés ; il est de longueur 4. Il n'y en a pas de plus court entre 1 et 9. En revanche, le chemin 1, 3, 4, 8, 9 est lui aussi de longueur 4. Voici quelques autres chemins de ce graphe : 2, 4, 8, 7 (de longueur 3), 3, 1, 2, 4 (de longueur 3 aussi), 8, 7 (de longueur 1).
Figure 2.4: Un chemin de longueur 4 entre les sommets 1 et 9
Graphe connexe.
Pour aborder la notion de connexité, examinons le graphe de la gure 2.5. Il est composé de sept sommets et de six arêtes. C'est bel et bien un graphe. Il y a bien au plus une arête entre chaque paire de sommets. Mais ce graphe n'est pas en un seul morceau . Si part d'un sommet quelconque, il ne pourra pas rejoindre n'importe quel autre sommet en suivant un chemin. S'il part, par exemple, du sommet 4, il n'y a aucun chemin pour aller jusqu'en 1 (ou en 3 ou en 2). Dans ce cas, on dit que le graphe n'est pas connexe.
Graphix
2. Un graphe. Qu'est-ce que c'est ?
11
Figure 2.5: Un graphe non connexe à six sommets (avec deux composantes connexes) Un graphe G est connexe si pour toute paire de sommets u et v , il existe un chemin dans G entre u et v . Dit autrement, G est connexe si peut aller de n'importe quel sommet à n'importe quel autre sommet en suivant un chemin de G. Par exemple, le graphe de la gure 2.3 (a) est connexe. On imagine bien que si le graphe représente un réseau, la connexité est une notion centrale.
Graphix
Les cycles. Un cycle dans un graphe G est un chemin dont les deux extrémités sont les mêmes. Si parcourt un cycle en partant d'un sommet u, il sera de retour en u à la n de son périple circulaire. On suppose ici aussi, comme pour les chemins, que ne passe pas plusieurs fois par une arête ou par un sommet, sauf son point de départ où il revient à la n. La longueur d'un cycle est son nombre d'arêtes. La gure 2.6 présente en pointillés un cycle de longueur 8 de G que l'on note 2, 6, 10, 7, 5, 9, 8, 4, 2 (le 2 en début et en n de séquence indique que l'on a aaire à un cycle). Cette séquence aurait aussi bien pu s'écrire 10, 7, 5, 9, 8, 4, 2, 6, 10 car le point de départ n'a pas d'importance dans quelque chose de circulaire. Voici d'autres cycles de ce même graphe : 7, 5, 9, 8, 7 (de longueur 4), 2, 6, 10, 7, 8, 4, 2 (de longueur 6) ou même 1, 2, 6, 10, 7, 5, 9, 8, 4, 3, 1 qui contient les dix sommets du graphe et qui est donc de longueur 10.
Graphix Graphix
Figure 2.6: Un cycle de longueur 8 Attention, la séquence suivante n'est pas considérée comme un cycle : 1, 2, 1 car si
Graphix la suit, il repasse deux fois par la même arête 1, 2.
Les arbres. Les arbres constituent une famille importante de graphes que l'on va retrouver dans plusieurs chapitres. Un arbre est un graphe connexe et sans cycle.
Graphes et algorithmes
12
C'est-à-dire qu'il est composé d'un seul morceau et qu'il ne contient pas de cycle. Voici quelques propriétés des arbres. Tout arbre à n sommets a nécessairement exactement n − 1 arêtes. Un arbre T est un graphe connexe (par dénition) mais pour n'importe quelle paire de sommets u et v , il existe exactement un chemin entre u et v dans T . En supprimant n'importe quelle arête de T , le graphe obtenu n'est plus connexe (comme dans la nature, les arbres de la théorie des graphes sont fragiles ).
ILLUST RAT ION Examinons ces propriétés sur l'exemple de la gure 2.7. Ce qui est représenté est bien un arbre : c'est bien un graphe connexe et il ne possède pas de cycle. On compte n = 10 sommets et m = 9 arêtes. On a donc bien m = n − 1. De plus, n'importe quelle paire de sommets n'est reliée que par un seul chemin. Par exemple, si veut aller de 1 jusqu'à 4, il est obligé de suivre le chemin 1, 3, 2, 4. Il n'y en a pas d'autre. Vous pouvez aisément vérier cette propriété pour n'importe quelle paire d'extrémités.
Graphix
Figure 2.7: Un arbre La dernière propriété énoncée indique que si une arête de T est supprimée, alors le résultat n'est plus connexe. Par exemple, en supprimant l'arête 2, 4, le graphe obtenu est composé de deux morceaux (composantes) connexes : d'un côté les sommets 4, 8, 9 et de l'autre les sept autres sommets. Autre exemple : en supprimant 7, 10, le sommet 10 est isolé de tous les autres. Chaque arête possède cette propriété de couper le graphe en deux parties (pas forcément de même taille) si elle est supprimée. Je vous invite à vérier par vous-même ces propriétés sur les trois arbres de la gure 2.8.
Additionnons les degrés de tous les sommets d'un graphe.
Considérons un graphe G = (V, E) quelconque, connexe ou non. Faisons la somme des degrés (nombre de voisins) de tous ses sommets. Quel est le résultat ? Je vous invite à y rééchir avant de lire la suite.
2. Un graphe. Qu'est-ce que c'est ?
13
Figure 2.8: Trois arbres Cette somme vaut toujours 2m où m est le nombre d'arêtes de G. Pourquoi ? Car en faisant cette somme, chaque arête u, v est comptée exactement deux fois : en additionnant le degré de u, elle est comptée une fois et en additionnant le degré de v , elle est comptée une autre fois. Ensuite, elle ne sera plus prise en compte (car elle n'a que deux extrémités).
ILLUST RAT ION Illustrons cela sur le graphe de la gure 2.9. Il a m = 11 arêtes en tout. Comptez le nombre de voisins de chaque sommet u et vous obtiendrez son degré. Par exemple, le degré du sommet 1 est 3 (car il a trois voisins) ; le degré de 3 est 5, etc. Faisonsen la somme : 3 + 2 + 5 + 3 + 3 + 3 + 3 = 22, c'est-à-dire précisément deux fois le nombre d'arêtes. Par exemple, l'arête 3, 7 contribue pour une unité dans le degré du sommet 3, pour une unité dans le degré du sommet 7 et nulle part ailleurs. Il en va de même pour chacune des autres arêtes, ce qui explique le résultat nal.
Figure 2.9: Un graphe
Les graphes extrêmes. Quel est le nombre minimal d'arêtes que peut contenir un graphe ayant n sommets ? Rééchissez quelques secondes à la réponse. Beaucoup de gens ont tendance à répondre spontanément qu'un graphe a au minimum n − 1 arêtes (surtout après avoir lu le passage sur les arbres). Il n'en est rien. Un graphe peut
14
Graphes et algorithmes
contenir zéro arête. C'est un graphe dans lequel il n'y a aucune relation entre ses sommets. Si vous avez répondu n − 1, peut-être pensiez-vous à un graphe connexe qui a, eectivement, au minimum n − 1 arêtes (il peut en avoir beaucoup plus). Mais si la connexité n'est pas imposée, un graphe peut avoir zéro arête. Vous pensez peut-être qu'il n'a pas beaucoup d'intérêt. Ce n'est pas certain. Il est porteur d'information : il dit qu'il n'y a aucune relation entre ses sommets. Maintenant posons-nous la question opposée. Combien un graphe à n sommets peut-il contenir d'arêtes au maximum ? Ici aussi prenez quelques secondes pour y rééchir. Rappelez-vous qu'entre chaque paire de sommets, il y a soit une soit zéro arête. Pour remplir totalement le graphe, il faut mettre systématiquement une arête entre chaque paire de sommets. On obtient ainsi ce que l'on nomme un graphe complet . Il y a une astuce pour obtenir facilement son nombre m d'arêtes en fonction du nombre n de ses sommets. Pour cela, remarquons d'abord que dans un graphe complet à n sommets, chaque sommet a exactement n − 1 voisins car il est relié à tous les autres (qui sont au nombre de n − 1). Ce qui veut dire que le degré de chaque sommet est exactement n − 1. La somme des degrés des n sommets s'écrit donc (n − 1) + (n − 1) + · · · + (n − 1) : la quantité n − 1 est présente exactement n fois. Cette somme vaut donc n(n − 1). Par ailleurs, la somme des degrés des sommets de n'importe quel graphe est exactement égale à 2m (résultat obtenu . Un graphe complet un peu plus haut). Ainsi, n(n − 1) = 2m, d'où m = n(n−1) 2 a donc un nombre d'arêtes dont l'ordre de grandeur est le carré du nombre de sommets.
ILLUST RAT ION Voyez à la gure 2.10 (a) un graphe à sept sommets et zéro arête. La gure 2.10 (b) présente un graphe complet à sept sommets. Amusez-vous à compter ses arêtes, vous = 21 (attention de ne pas en oublier ou de ne pas en compter allez en trouver 7(7−1) 2 une plusieurs fois).
Figure 2.10: (a) Un graphe sans arête, (b) Un graphe complet
Les algorithmes de graphes. Vous voici maintenant munis des éléments de base pour découvrir des résultats plus avancés, exposés aux chapitres suivants.
2. Un graphe. Qu'est-ce que c'est ?
15
Dans certains d'entre eux vous allez découvrir des algorithmes de graphes. Ce sont des méthodes qui, exécutées pas à pas, permettent d'obtenir un résultat nal à partir des données de départ. Vous connaissez déjà des algorithmes, par exemple celui qui permet de faire une addition de deux valeurs : 2 456 + 159. Ici les données d'entrée sont deux entiers, le résultat de sortie est la somme de ces deux entiers et l'algorithme est la description des opérations d'addition à eectuer pas à pas sur les chires alignés, de la droite vers la gauche en propageant l'éventuelle retenue. C'est ce que vous avez appris à l'école. Pour nous, la donnée de départ (l'entrée de l'algorithme ) sera souvent un graphe (plus éventuellement d'autres éléments). L'algorithme va alors manipuler ce graphe pour en extraire un résultat. Par exemple, le chapitre 3 va décrire un algorithme qui prend en entrée un graphe connexe G ainsi qu'un sommet r de G et va construire pas à pas un arbre T composé de certaines arêtes de G et contenant tous ses sommets (dit arbre couvrant de G). Cet arbre résultat a une propriété intéressante : chaque chemin de T entre r et n'importe quel autre sommet u est aussi un plus court chemin entre r et u dans le graphe initial G. Cela permet ainsi d'obtenir de plus courts chemins et, en mesurant leur longueur, de calculer les distances entre r et tous les autres sommets de G. Ces chemins peuvent servir en pratique à organiser des déplacements de biens ou de personnes à partir d'une source r vers d'autres points cibles sur les routes les plus courtes dans un plan modélisé par G. Un point auquel il faut faire attention lors de la création d'un nouvel algorithme (pour manipuler un graphe ou autre chose) est le nombre d'opérations élémentaires qu'il devra eectuer pour calculer/construire le résultat nal à partir des données de départ. C'est la complexité (en temps) de l'algorithme. Encore faut-il savoir ce qu'est une opération élémentaire. Pour cela, il faudrait expliquer de manière plus précise la façon dont est représenté un graphe en mémoire, comment accéder à ses éléments, comment les manipuler, etc. Les descriptions que nous donnerons seront très schématiques et nous n'entrerons pas dans ces détails qui font perdre de vue les idées essentielles. Peut-être qu'après avoir lu ce livre d'introduction, vous voudrez en savoir plus, aller plus en profondeur et, dans ce cas, je vous invite à lire un livre d'algorithmique avancée du rayon informatique de votre librairie ou bibliothèque préférée (quelques références sont données en n d'ouvrage). De manière (très, très) schématique, on distingue deux types d'algorithmes : ceux dont la complexité est raisonnable et les autres. Le terme raisonnable désigne des algorithmes dont le nombre d'opérations élémentaires est au plus un polynôme en la taille de l'entrée. Si le graphe traité par l'algorithme a n sommets, alors une complexité polynomiale sera par exemple 3n3 −2n+4. Certains problèmes peuvent être résolus par un algorithme polynomial. Pour d'autres, on ne connaît pas d'algorithmes polynomiaux permettant de les résoudre. Mais on ne sait pas non plus s'il en existe ou non. C'est le ou le plus complet. Cela est en relation avec la conjecture P = N P dont vous avez peut-être entendu parler. Le chapitre 14 donne quelques éléments sur ces points. Un algorithme est conçu pour produire un résultat à partir des données d'entrée. Il faut donc prouver qu'il fait bien ce qu'il doit faire et pas autre chose. L'informatique produit aussi des théorèmes qui s'appuient fortement sur la chronologie des opérations
16
Graphes et algorithmes
réalisées, sur les étapes de l'algorithme analysé. Nous illustrerons ces idées à plusieurs endroits. À côté de cela, nous présenterons aussi quelques preuves dans le domaine des graphes, qui fait partie du vaste ensemble des mathématiques discrètes (discret doit être compris ici comme l'opposé de continu). Le plus ancien résultat présenté dans cet ouvrage date du XVIII e siècle et le plus récent a été publié il y a quelques années seulement. Les graphes ont connu un grand essor, principalement dans la seconde moitié du XXe siècle. Suite à la démocratisation des ordinateurs, un courant théorique et appliqué s'est développé, qui a conduit à de grands programmes de recherches en théorie structurelle, mais aussi en algorithmique des graphes. Nous croiserons tout au long de l'ouvrage quelques scientiques qui ont laissé leur nom à des théorèmes ou des algorithmes (parfois les deux). Il faudrait ajouter celui de Paul Erdös qui n'est pas explicitement cité dans ce livre, mais qui a été un géant dans le domaine des graphes, de la combinatoire et des mathématiques discrètes. Il est à l'origine des centaines de résultats avec des dizaines de coauteurs partout dans le monde. Infatigable travailleur, il est décédé à la toute n du XX e siècle et a marqué l'histoire des sciences. En France, Claude Berge (décédé quelques années plus tard) a été lui aussi un explorateur et un promoteur de premier plan de la théorie des graphes. On lui doit de nombreux résultats et des livres sur ces sujets. Bien d'autres mériteraient d'avoir leur nom ici. Il est dicile de tous les citer. Pour terminer, remarquons que les algorithmes ont vocation à être codés dans un langage de programmation, puis éventuellement intégrés dans des systèmes informatiques plus vastes. Comme expliqué plus haut, nous ne franchirons pas cette étape. Cependant, notre promenade ensemble va vous faire découvrir dans les premiers chapitres les belles idées qui se cachent dans certains classiques de l'informatique, puis va vous emmener faire la connaissance d'une branche très active de la recherche à travers la présentation de quelques algorithmes d'approximation dans les derniers chapitres (il est conseillé d'avoir lu le chapitre 14 préalablement).
3
Parcourons un graphe en largeur
Lorsque vous devez faire un trajet à pied, en voiture ou en métro, vous cherchez à arriver le plus vite possible. Dans une grande ville, avec de nombreuses lignes et de nombreuses stations, établir un trajet optimal en transports en commun d'un point A à un point B est parfois compliqué. Heureusement, des applications informatiques sont disponibles pour calculer ces trajets automatiquement (par exemple un GPS si vous êtes en voiture ou un site web spécialisé pour aller d'une station de départ à une station d'arrivée. Tout cela est maintenant disponible sur smartphone, souvent gratuitement). Ce chapitre est dédié à la description d'un algorithme classique pour calculer des plus courts chemins dans un graphe. Pour simplier la présentation, nous allons dans un premier temps nous restreindre à un cas particulier, en supposant que tous les segments de trajets sont de même longueur. Nous verrons aussi une application de cette technique pour déterminer si les sommets d'un graphe peuvent ou non être coloriés avec deux couleurs de manière à ce que deux voisins ne soient pas de la même couleur (ce problème semble, à première vue, n'avoir aucun rapport avec des plus courts chemins. Nous verrons quels sont les liens). Pour terminer, nous étudierons le cas plus général des graphes pondérés (où chaque arête a sa longueur propre). Mais pour l'instant, dénissons la notion de distance entre deux sommets.
La distance entre deux sommets dans un graphe. La distance entre deux sommets quelconques u et v d'un graphe G, notée distG (u, v), est la longueur du plus court chemin entre u et v dans G (rappel : la longueur d'un chemin est son nombre d'arêtes ). S'il n'y a aucun chemin entre u et v , alors distG (u, v) = ∞. Un chemin peut ne comporter qu'un seul sommet (et pas d'arête) et ainsi distG (u, u) = 0 (un sommet u est à distance 0 de lui-même). ILLUST RAT ION
Illustrons tout cela avec le graphe G de la gure 3.1.
Graphes et algorithmes
18
Figure 3.1: Un graphe G pour illustrer les distances. Calculons par exemple la distance distG (1, 10) entre 1 et 10. Il y a de nombreux chemins entre ces deux sommets. Par exemple 1, 2, 3, 4, 6, 5, 9, 10 est un chemin de longueur 7 car si devait le parcourir, il serait obligé de traverser sept arêtes. Mais il y en a de moins longs. Par exemple, le chemin 1, 2, 4, 6, 8, 10 est de longueur 5. Le moins long de tous est 1, 2, 5, 9, 10, de longueur 4. Il est assez facile de se convaincre ici qu'il n'y en a pas de plus court. Ainsi, distG (1, 10) = 4. Un plus court chemin entre u et v dans G de longueur distG (u, v) n'est pas forcément unique. Dans le graphe de la gure 3.1, le chemin 1, 2, 5, 8, 10 est aussi de longueur 4.
Graphix
Calculons toutes les distances à partir d'un sommet donné.
Maintenant que nous savons ce qu'est une distance, nous allons découvrir et utiliser ensemble un algorithme qui permet d'en calculer. Cet algorithme est le parcours en largeur ou BFS (pour Breath First Search en anglais). Pour le décrire, nous allons faire appel à notre personnage, , qui va se promener dans le graphe et faire, au fur et à mesure des étapes, diverses opérations. À la n, il pourra nous donner le résultat de ses pérégrinations. Mais nous n'en sommes pas encore là.
Graphix
De quoi l'algorithme a-t-il besoin ?
Les données qui vont être manipulées ici sont un graphe G = (V, E) et un sommet quelconque de G, noté r, à partir duquel les distances vont être calculées, c'est-à-dire distG (r, u) pour tous les sommets u de G.
Déroulement des opérations du parcours en largeur. Graphix
part du sommet r. Pour se souvenir qu'il a déjà vu r, il va le colorer. Pour xer les idées, disons qu'il le colore en vert. Il sait aussi, sans faire aucun calcul, que distG (r, r) = 0. Il examine ensuite un par un les voisins de r. Pour chacun d'eux, notons-le u, il fait quatre choses. 1. Colorer u en vert pour se rappeler qu'il l'a déjà partiellement traité. 2. Noter que distG (r, u) = 1. Il est clair que la distance entre r et chacun de ses voisins est 1. 3. Ajouter le nom du sommet u à droite d'une liste L, initialement vide. 4. Indiquer que le parent de u est r. Cela va servir à garder en mémoire un chemin entre u et r.
3. Parcourons un graphe en largeur
19
Graphix
L'ordre dans lequel examine les voisins du sommet courant (pour l'instant r) n'a pas d'importance. Lorsqu'il a terminé, il colore le sommet r en une autre couleur, disons rouge, pour indiquer que r et son voisinage ont été totalement exploités. Il sera inutile d'y revenir. voit maintenant un sommet rouge r et un certain nombre de sommets verts (pour l'instant les voisins de r ; les autres sommets n'ont pas encore de couleur). Il a aussi une liste L des voisins de r. Il se déplace alors sur le sommet u dont le nom est au début de la liste L (pour l'instant ce sommet est un voisin vert de r). En u, il va faire des traitements similaires à ceux qu'il a faits en r.
Graphix
Il examine un par un les voisins de u. Pour chacun d'eux, notés v , il fait les quatre opérations suivantes uniquement si v n'a pas encore de couleur (sinon il ne fait rien pour v ). 1. Colorer v en vert pour se rappeler qu'il l'a déjà partiellement traité. 2. Noter que distG (r, v) = distG (r, u) + 1. Un plus court chemin entre r et v est composé, en première partie, par un plus court chemin de r à u puis, pour nir, par l'arête u, v. 3. Ajouter le nom du sommet v à droite (à la n) de la liste L. 4. Indiquer que le parent de v est u.
Graphix
Lorsque a terminé ce traitement (pour tous les voisins de u), il colore u en rouge (n d'exploitation de u), et eace u de la liste L. Il se déplace ensuite vers le sommet dont le nom est maintenant en tête de la liste L et va faire le traitement précédent en ce sommet. Il va continuer ainsi tant que la liste L n'est pas vide. Lorsque L est vide, les sommets qui n'ont pas été atteints (par exemple si le graphe n'est pas connexe) sont donc à une distance innie de r : distG (r, u) = ∞. Ce sont les sommets qui n'ont pas reçu de couleur pendant tout le déroulement des opérations, sur lesquels n'est pas allé.
Graphix
ILLUST RAT ION La gure 3.2 présente les diérentes conventions graphiques qui vont être utilisées plus loin.
Figure 3.2: Résumé visuel des conventions. La sommet A est blanc (ou non coloré), ce qui indique que le parcours n'est pas encore parvenu jusqu'à lui. Le sommet B est vert, ce qui indique qu'il est en cours de traitement. Le sommet C est rouge, ce qui indique que son traitement est terminé. La èche de u vers v indique que u est le parent de v (ou v l'enfant de u).
Graphes et algorithmes
20
Figure 3.3: Début des opérations en partant de
r
= 5.
Le graphe qui sert de support à l'illustration est à la gure 3.3 (a). Choisissons par exemple de calculer les distances à partir du sommet r = 5. Au départ, la liste L est vide (représentée par un rectangle sous le graphe). fait les opérations décrites précédemment et examine les voisins de r, par exemple dans l'ordre 2, 6, 8, 9. Les sommets vont donc être insérés dans cet ordre dans L. Les arêtes entre r et ses enfants sont marquées au fur et à mesure sur la gure par une èche. Les voisins de r sont coloriés en vert. À la n de ce traitement, r prend la couleur rouge et la liste est donc L = [2, 6, 8, 9]. La situation est représentée à la gure 3.3 (b). On sait que distG (r, r) = 0 et que distG (r, 2) = distG (r, 6) = distG (r, 8) = distG (r, 9) = distG (r, r) + 1 = 1. Pour la prochaine étape, se déplace sur le sommet qui est en tête de la liste L, c'est-à-dire le sommet 2, examine tous ses voisins et, pour chacun d'eux, applique le traitement indiqué plus haut. Attention, il ne fait ce traitement à un voisin v que si v est blanc. Par exemple, il ne fait rien à r (qui est rouge), mais imaginons qu'il traite les autres voisins de 2 dans l'ordre 1, puis 3, puis 4 (qui sont donc insérés dans L dans cet ordre). Lorsqu'il a ni, il colore 2 en rouge et eace 2 de L. À la n, les éléments sont ceux de la gure 3.4 (c) et distG (r, 1) = distG (r, 3) = distG (r, 4) = distG (r, 2) + 1 = 2. poursuit son exploration en allant sur le sommet en début de liste L, c'est-à-dire le sommet 6 dont les voisins sont : 5, 4, 7, 8. Les sommets 5, 4, 8 ne sont pas blancs. Aucun traitement n'est fait sur eux. On comprend maintenant pourquoi. En eet, on sait déjà que distG (5, 5) = 0 et que distG (r, 4) = distG (r, 8) = 2 (calculées aux étapes précédentes). Inutile d'aller explorer à nouveau ces sommets à partir de 6.
Graphix
Graphix
Graphix
3. Parcourons un graphe en largeur
21
Donc ici seul 7 subit un traitement. À la n, la situation est celle de la gure 3.4 (d) et distG (r, 7) = distG (r, 6) + 1 = 2.
Figure 3.4: Suite des opérations.
Le sommet suivant traité est 8, en début de liste. Il a quatre voisins mais seul 10 est blanc. Il le traite. À la n, la situation est celle de la gure 3.5 (e) et distG (r, 10) = 2. Lors des étapes suivantes, aucune nouvelle distance n'est calculée, aucune èche n'apparaît. Chaque sommet traité n'a aucun voisin blanc. Il est donc simplement coloré en rouge et retiré de la liste L. Lorsque celle-là est vide, tous les sommets sont rouges. La gure 3.5 (f) représente en pointillés les arêtes par lesquelles ont été établies les relations entre enfants et parents. Ces arêtes forment un arbre.
Graphes et algorithmes
22
Figure 3.5: Fin du traitement. Cet arbre nal est représenté à la gure suivante, dessiné niveau par niveau, à partir de r = 5, pour mieux visualiser les distances par rapport à r.
Quels sont les résultats de cet algorithme ? À quoi sert-il ?
Le parcours en largeur est un algorithme, un outil. Vous pouvez utiliser cet outil comme bon vous semble, pour l'usage qui vous convient et retenir les résultats qui vous intéressent. Si vous cherchez à connaître les distances à partir d'un sommet r, l'algorithme les calcule et vous pouvez donc y avoir accès. Si vous cherchez des plus courts chemins à partir de r, seul l'arbre nal T vous intéressera. Cet arbre est constitué de l'ensemble des sommets rouges, atteints pendant le parcours, et des relations/arêtes qui relient chaque sommet à son parent. Cet arbre T possède la propriété suivante : pour tout sommet u atteint pendant le parcours, distG (r, u) = distT (r, u) (pour ces sommets, les distances dans l'arbre T sont égales aux distances dans le graphe G).
3. Parcourons un graphe en largeur
23
Un autre usage possible de cet algorithme est le test de connexité qui consiste à savoir si un graphe G = (V, E) donné est ou non connexe (c'est-à-dire si G contient (au moins) un chemin entre chaque paire de sommets). Ici la réponse de l'algorithme doit simplement être ou . Comment faire ? Appliquez le parcours en largeur à partir de n'importe quel sommet r de G. Si à la n du traitement chaque sommet est rouge, alors G est connexe, sinon il ne l'est pas. Ici c'est la couleur qui vous intéresse, pour savoir si tous les sommets ont été atteints. Les autres résultats ne sont pas utiles.
Oui il est connexe
pas connexe
Non il n'est
Impact de l'ordre d'examen des voisins du sommet courant.
Graphix
choisit l'ordre d'examen des voisins du sommet courant u sur lequel il se trouve (de manière aléatoire ou avec une stratégie déterministe). Quel est l'impact sur le résultat nal global de ces choix individuels locaux ? L'arbre nal (représenté par les èches sur les illustrations) sera éventuellement diérent, mais les distances calculées seront les mêmes.
ILLUST RAT ION Reprenons l'exemple de la gure 3.3, en partant toujours du sommet r = 5. Dans cette illustration, examinait les voisins de r dans l'ordre 2, puis 6, puis 8, puis 9, ce qui donnait la liste L = [2, 6, 8, 9]. Mais rien ne l'obligeait à suivre cet ordre. Il aurait pu examiner les sommets dans un autre ordre, par exemple 9, puis 6, puis 2, puis 8. La liste aurait alors été L = [9, 6, 2, 8]. Cela n'aurait pas changé les èches sortant de r, mais le prochain sommet sur lequel serait allé aurait été le sommet 9 et non le sommet 2. Dans ce cas, 10 aurait eu comme parent non pas le sommet 8, mais le sommet 9. Ensuite, serait allé sur 6 qui aurait eu comme enfants les sommets 4, 8 et 7, qui seraient eux-mêmes examinés dans un certain ordre, ce qui aurait eu des conséquences sur la suite, etc. À la n, les distances auraient été exactement les mêmes, les sommets atteints (et coloriés en rouge à la n) également. Seul l'arbre nal aurait été diérent. Mais quel que soit l'ordre choisi en chaque sommet, le résultat nal est toujours un arbre des plus courts chemins à partir de r. Ce sont simplement éventuellement d'autres plus courts chemins.
Graphix
Graphix
Graphix
Les graphes bipartis. Nous allons décrire une autre utilisation du parcours en largeur qui nécessite dans un premier temps la dénition suivante que nous illustrerons plus loin. Un graphe G = (V, E) est dit biparti si chaque sommet peut être colorié soit en bleu soit en orange de telle manière que chaque arête u, v de G ait une extrémité bleue et l'autre orange. Le choix des couleurs (ici bleu et orange) est bien sûr totalement arbitraire. Certains graphes sont bipartis, d'autres non. Lorsque G est biparti, on notera V1 l'ensemble des sommets d'une couleur (bleu par exemple) et V2 ceux de l'autre
24
Graphes et algorithmes
couleur. On a donc V = V1 ∪ V2 (chaque sommet de V a une couleur) et V1 ∩ V2 = ∅ (un sommet n'a qu'une couleur). Une manière de dessiner un tel graphe est de placer les sommets de V1 d'un côté du dessin et ceux de V2 de l'autre côté. Les arêtes sont alors entre les paquets V1 et V2 .
ILLUST RAT ION La gure 3.6 présente deux graphes. Celui de gauche est biparti. Vous pouvez constater que la coloration donnée est telle que chaque arête a bien des extrémités de couleurs diérentes. En revanche, celui de droite ne l'est pas. Il est impossible de le colorier en deux couleurs de façon à ce que chaque arête ait une extrémité de chaque couleur. Est-ce que vous comprenez pourquoi ?
Figure 3.6: Un graphe biparti à gauche et un autre qui ne l'est pas. Pour expliquer cela, le graphe de droite a été reproduit à la gure 3.7 et un cycle impair (contenant un nombre impair d'arêtes et de sommets) a été mis en lumière en dessinant en pointillés ses cinq arêtes pour bien les distinguer.
Figure 3.7: Un cycle impair (arêtes en pointillés) dans le graphe. Concentrons-nous sur ce cycle. Pour que le graphe puisse être colorié en deux couleurs, il faut que les cinq sommets de ce cycle lui-même puissent être coloriés de
3. Parcourons un graphe en largeur
25
manière à ce que chacune de ses arêtes ait des extrémités de couleurs diérentes. Si ce n'est pas possible pour ce cycle en particulier, ça ne sera pas possible pour le graphe entier. Pour l'étudier plus spéciquement, il a été sorti du graphe, à la gure suivante.
Essayons de colorier ses cinq sommets. Donnons à 1 une couleur, bleu pour xer les idées. Alors 6 et 5 prennent nécessairement l'autre couleur orange, ce qui entraîne nécessairement que 9 et 2 prennent tous les deux la couleur bleue. Les sommets 9 et 2 sont voisins et sont coloriés en bleu, ce qui n'est pas autorisé. Impossible de colorier le cycle donc impossible de colorier le graphe de la gure 3.7.
Si G contient un cycle impair, alors il n'est pas biparti. L'exemple précédent est facile à généraliser. Les cycles impairs ne sont pas bipartis. Ainsi, si un graphe G contient un cycle impair, il n'est pas biparti. Si G ne contient pas de cycle impair, alors il est biparti. Nous allons maintenant voir que si un graphe G ne contient pas de cycle impair alors il est biparti. Pour cela, nous allons proposer une méthode pour le colorier en bleu et orange. Pour simplier, nous supposerons que G est connexe , c'est-à-dire en un seul bout . Si ce n'est pas le cas, il faut appliquer ce qui va suivre sur chaque morceau (composante) connexe de manière indépendante. 1. Choisir un sommet r quelconque de G. 2. Calculer les distances entre r et chaque sommet u de G (avec le parcours en largeur). 3. Colorier en bleu les sommets qui sont à une distance paire de r (y compris r lui-même). 4. Colorier en orange les sommets qui sont à une distance impaire de r. À la n, chaque sommet aura bien une couleur. On peut démontrer que chaque arête u, v de G a ses deux extrémités de couleurs diérentes.
ILLUST RAT ION Appliquons cette procédure sur le graphe de la gure 3.8 (a). Prenons par exemple le sommet r = 1 et appliquons le parcours en largeur dans le graphe à partir de r.
26
Graphes et algorithmes
Cela nous donne les distances. Celles-la ne sont pas explicitement calculées ici, mais le graphe est redessiné à la gure 3.8 (b) en mettant le sommet 1 en haut et en plaçant les autres sommets en dessous, par niveaux de distance par rapport à r (par exemple 2, 3 et 4 sont au niveau 1, puis les sommets 5, 6, 7, 8 et 9 sont au niveau 2, etc.). Les sommets sont ensuite coloriés en fonction de la parité de la distance.
Figure 3.8: (a) Un graphe sans cycle impair. (b) Le résultat de la méthode.
Et si le graphe contient des cycles impairs ? Examinons maintenant ce que donne l'application de cette même procédure sur un graphe contenant un ou plusieurs cycles impairs. Testons-le sur le graphe non biparti de la gure suivante.
En choisissant le sommet r = 1 comme sommet de départ, calculons les distances par rapport à r et colorions en bleu et orange en fonction de la parité de la distance
3. Parcourons un graphe en largeur
27
à r. Cela donne alors le résultat coloré représenté sur la gure. Plusieurs arêtes, dont 2, 9, ont leurs deux extrémités de la même couleur. Cela se produit lorsque G a un cycle impair. Si en appliquant la procédure vous constatez qu'il y a une arête qui relie deux sommets d'un même niveau par rapport à r, alors cela implique que G contient un cycle impair (contenant cette arête). Par exemple, 2, 9 est contenue dans le cycle 1, 6, 9, 2, 5, 1 qui est impair (il contient cinq arêtes).
Un peu plus avec cette procédure.
Cette procédure appliquée sur un graphe quelconque permet d'obtenir les résultats suivants. Permet de colorier le graphe en deux couleurs s'il est biparti. Permet de trouver des cycles impairs si G n'est pas biparti (en ajoutant la détection des arêtes entre deux sommets à même distance de r).
Traitons le cas où les arêtes sont pondérées. Dans cette partie, nous allons supposer que les graphes manipulés sont pondérés, c'est-à-dire que chaque arête u, v a une longueur notée l(u, v) strictement positive : l(u, v) > 0 (dans d'autres ouvrages, on parle plutôt de poids d'une arête, c'est le vocabulaire traditionnel, mais ici le terme longueur, plus naturel, a été préféré). Jusqu'à présent, nous avons traité le cas particulier l(u, v) = 1. Nous n'allons manipuler ici que des graphes connexes. La gure 3.9 présente un exemple de tel graphe. La longueur de chaque arête est la valeur sur l'arête elle-même. Rien n'oblige à représenter des arêtes ayant une grande longueur par une longue arête sur le dessin. La longueur du dessin d'une arête u, v n'est pas nécessairement proportionnelle à l(u, v).
Figure 3.9: Un graphe pondéré. La valeur apparaissant sur chaque arête est sa longueur.
Les distances (pondérées). Avec ces conventions, la notion de distance évolue. Traverser une arête de longueur 10 n'est pas la même chose que traverser une arête de longueur 2. C'est cinq fois plus long. La longueur d'un chemin est maintenant naturellement la somme des longueurs de ses arêtes. Par exemple, sur le graphe de la gure 3.9, le chemin 1, 4, 3, 5 entre les sommets 1 et 5 est composé de trois arêtes,
Graphes et algorithmes
28
mais est de longueur totale 9 + 3 + 7 = 19. La distance entre deux sommets u et v dans G, notée encore distG (u, v), est la longueur du plus court chemin entre u et v dans G. Par exemple, dans le graphe de la gure 3.9, distG (1, 4) = 5 et non 9 qui est la longueur de l'arête directe entre 1 et 4. Cela veut dire que dans certains cas, le plus court chemin n'est pas forcément celui qui contient le moins d'arêtes, mais celui dont la somme des longueurs est la plus petite.
Zone orange Principe général de l'algorithme de Dijkstra.
Dans ce qui va suivre, nous allons décrire un algorithme (inventé par M. Dijkstra) pour calculer toutes les distances entre un sommet de départ r et tous les autres sommets d'un graphe G pondéré. Cette partie est en zone orange car l'algorithme est un peu plus dicile à comprendre que le parcours en largeur dont c'est une généralisation. Au départ, aucune distance n'est connue. Les distances provisoires, notées D[r, u], sont initialisées à l'inni ( D[r, u] = ∞), sauf la distance entre r et lui-même qui est bien sûr égale à 0 (D[r, r] = 0). Ces distances initiales sont des distances estimées qui vont évoluer au fur et à mesure des traitements. Comme, au début, aucun chemin entre r et u n'est connu, l'estimation de la distance distG (r, u) est l'inni. Quand un chemin sera découvert pour arriver à u, cette estimation sera mise à jour. Si plus tard un chemin encore plus court est trouvé, cette estimation sera à nouveau mise à jour. À la n, les distances estimées seront les vraies distances, dénitives : D[r, u] = distG (r, u). À chaque étape principale, le sommet u traité est celui qui est le plus proche de r parmi les sommets non encore traités. C'est-à-dire le sommet u non encore traité dont la distance estimée est, pour l'heure, la plus petite. Initialement, le sommet non traité le plus proche de r est r lui-même, c'est donc lui le premier sommet traité par l'algorithme, ce qui semble naturel. Le traitement du sommet u consiste à examiner tour à tour chaque voisin v de u et à faire les opérations suivantes : si la distance estimée entre r et v est strictement plus grande que la distance estimée entre r et u plus la longueur de l'arête u, v (c'est-à-dire si D[r, v] > D[r, u] + l(u, v)), cela veut dire qu'il existe un chemin plus court entre r et v que celui déjà connu. Dans ce cas (et uniquement dans ce cas), la distance estimée entre r et v est mise à jour (D[r, v] prend maintenant la valeur D[r, u] + l(u, v)). Examinons les premières étapes sur le graphe G de la gure suivante en prenant r = 2.
3. Parcourons un graphe en largeur
29
Chaque distance est initialisée à ∞, sauf D[2, 2] = 0. Le sommet 2 est traité et ses trois voisins sont examinés dans un ordre quelconque. Imaginons que le sommet 6 soit examiné en premier. Comme, pour l'instant, D[r, 6] = ∞, grâce à l'arête 2, 6, un chemin de longueur 10 vient d'être trouvé, ce qui est strictement plus court que ∞. La distance (provisoire) D[2, 6] est mise à jour et prend la valeur (provisoire encore) 10. Le traitement est similaire pour les sommets 4 et 5, les deux autres voisins de 2 qui ont, après cet examen, une distance de 2 et 5 respectivement. L'étape suivante va consister à traiter le sommet non encore traité (donc pas le sommet 2) dont la distance estimée est la plus petite : c'est le sommet 4 (sa distance estimée est 2 alors que toutes les autres sont strictement plus grandes). Le traitement de 4 consiste à examiner ses quatre voisins et à faire le traitement similaire à celui qui a été fait pour 2. Les voisins 1, 8 et 3, qui avaient une distance estimée innie, prennent maintenant une distance estimée de : 11, 6 et 5 respectivement. L'examen du sommet 2 (qui est un voisin de 4) ne change rien à sa distance estimée (qui reste à 0). L'algorithme va se poursuivre de proche en proche, en traitant en priorité le sommet non traité le plus proche de r. Ces deux premières étapes ainsi que la suite vont être illustrées graphiquement un peu plus loin, ce qui devrait vous aider à y voir plus clair. Avant cela, posons quelques conventions graphiques, similaires à celles qui ont été utilisées pour le parcours en largeur.
Une marque pour mémoriser les chemins.
Le fait de calculer les distances à partir de r est intéressant, mais il serait encore plus intéressant de connaître de plus courts chemins, comme dans le parcours en largeur. La grande diérence est qu'ici, les plus courts chemins peuvent changer au fur et à mesure des traitements. Au départ, aucun plus court chemin n'est connu, puis certains vont apparaître mais vont éventuellement être modiés plus tard. Comme dans le parcours en largeur, nous allons marquer des relations entre parent et enfant. Si une arête u, v est marquée avec u le parent de v (de manière équivalente v est un enfant de u), cela indique que le plus court
30
Graphes et algorithmes chemin trouvé pour l'instant entre r et v est composé d'un chemin entre r et u suivi de l'arête u, v. Comme plus haut, cette relation parent/enfant sera représentée par une èche de u vers v (du parent vers l'enfant). Si, plus tard, un chemin plus court entre r et v est découvert, dont la dernière arête est w, v, alors v va changer de parent, qui sera w et plus u.
ILLUST RAT ION Appliquons cette méthode et ces marques sur le graphe de la gure 3.9. Les deux premières étapes ont été décrites un peu plus haut. Le résultat est montré de manière graphique sur la gure 3.10 où les sommets colorés sont ceux qui sont déjà traités (et ne seront plus repris). Les èches indiquent les relations parent/enfant. La valeur colorée juste à côté d'un sommet u indique sa distance estimée, D[r, u].
Figure 3.10: Les premières étapes de l'algorithme. Le prochain sommet traité après 2 et 4 est celui qui a la plus petite distance estimée. Sur la gure 3.10, les sommets 5 et 3 ont tous les deux la même distance estimée minimale ( 5). L'algorithme ne dit rien de particulier sur le choix à faire. N'importe lequel peut être traité. Prenons alors le sommet 3 et examinons ses voisins. L'examen de 5 ne change rien car en passant par 3, il est possible d'atteindre le sommet 5 avec un chemin de longueur 5 + 7 = 12, ce qui est largement plus grand que la longueur de l'arête directe entre 2 et 5. Ainsi, le parent de 5 ne change pas et sa distance estimée non plus. En revanche, l'examen du voisin 7 de 5 modie sa distance estimée (qui est ∞) qui passe à 5 + l(3, 7) = 7. Le résultat nal de cette étape est présenté à la gure suivante.
3. Parcourons un graphe en largeur
31
Le sommet (non coloré) ayant la plus petite distance estimée est maintenant 5. Son traitement ne modie rien. Il ne permet pas de trouver de nouveaux chemins plus intéressants que ceux qui existent déjà pour ses deux voisins. Seule sa couleur change pour mémoriser le fait qu'il est traité. Le prochain sommet traité est 8 (avec une distance estimée de 6). L'examen de ses voisins ne modie rien en 7 (ni en 4), mais permet en revanche de mettre à jour un nouveau chemin entre 2 et 1, qui ne passe plus directement par 4 mais fait le détour (au total moins long) par 8. Un chemin de longueur 7 vient d'être trouvé alors que le précédent était de longueur 11. Maintenant le sommet 1 n'est plus l'enfant du sommet 4 mais devient celui du sommet 8, ce qui se traduit par une èche de 8 vers 1 (qui remplace celle de 4 vers 1 mise lors du traitement de 4). La situation courante est représentée sur la gure suivante.
Lorsque le sommet 8 a été traité, les plus petites distances estimées sont celles des sommets 1 et 7. Le traitement du sommet 7 ne change rien (seule sa couleur change ici pour mémoriser le fait qu'il a été traité). En revanche,
32
Graphes et algorithmes le traitement du sommet 1 modie la distance estimée du sommet 6 : l'examen par le sommet 1 de son voisin 6 permet de découvrir un chemin de longueur 7 + 1 = 8, ce qui est bien meilleur que celui composé de l'unique arête 2, 6. La distance estimée de 6 est modiée et passe de 10 à 8. Ce sommet change aussi de parent, passant de 2 à 1, ce qui se traduit par une èche de 1 vers 6 (remplaçant celle qui allait de 2 vers 6). Enn, le dernier sommet non traité 6 est examiné, ce qui ne change rien. La gure suivante donne le résultat nal. Les distances estimées sont maintenant les distances dénitives.
Cet exemple montre que les plus courts chemins évoluent au fur et à mesure de l'exploration. C'est le cas pour les sommets 1 et 6. À chaque étape, le sommet u (non traité) ayant la distance estimée la plus petite a en réalité sa distance dénitive. Elle ne changera plus par la suite.
Figure 3.11: L'arbre construit par l'algorithme, ainsi que les distances nales entre r = 2 et chaque autre sommet.
3. Parcourons un graphe en largeur
33
Cet algorithme permet non seulement de calculer les distances, mais aussi de plus courts chemins entre r et n'importe quel autre sommet. Ces chemins sont donnés par les relations parent/enfant (les èches sur les schémas). En oubliant le sens des èches et en ne gardant que les arêtes qui sont le support d'une relation parent/enfant, on obtient l'arbre couvrant de la gure 3.11.
Conclusion.
Calculer des distances dans un graphe (pondéré ou non) est un problème assez facile. Il faut simplement bien faire attention à réaliser les opérations dans l'ordre prescrit. Bien programmés, ces algorithmes sont ecaces et permettent, comme nous l'avons vu, de faire des opérations de plus haut niveau (test de connexité, plus courts chemins). Cela a aussi été l'occasion d'introduire la dénition de graphe biparti qui sera utile dans d'autres chapitres.
4
Parcourons un graphe en profondeur
Nous avons vu au chapitre 3 une façon de parcourir un graphe à partir d'un sommet donné. Ici nous allons en voir une autre (inutile d'avoir lu le chapitre 3, pour lire celui-ci), devenue un classique. Le graphe parcouru est noté G = (V, E) et r est un sommet de G qui sera le point de départ du parcours. Si G est connexe (s'il existe un chemin entre chaque paire de sommets de G), ce parcours en profondeur va construire un arbre T couvrant G (T contient tous les sommets de G). Si vous avez lu le chapitre 3, vous pouvez vous dire : mais n'est-ce pas déjà ce que fait le parcours en largeur ? La réponse est oui cependant la construction de l'arbre est faite ici grâce à d'autres règles et le résultat n'aura pas les mêmes propriétés. Ces dernières seront utiles dans plusieurs autres chapitres, mais il est encore un peu tôt pour en dire plus. Je vous encourage donc à vous approprier cet algorithme, comme vous apprendriez à vous servir d'un outil. Pour faciliter la compréhension, la description est découpée en deux parties : la phase de descente où l'exploration va plonger dans les profondeurs du graphe à partir du sommet r puis, lorsque ce n'est plus possible, une phase de remontée dans laquelle on reviendra sur nos pas pour essayer, à nouveau, d'aller explorer G en profondeur. La phase de descente. Notre personnage explorateur Graphix va partir de puis visiter les sommets en passant du sommet où il se trouve vers un voisin de ce sommet, uniquement s'il ne l'a pas déjà visité. Lorsqu'il visite pour la première fois un sommet v en venant du sommet u alors, tel un Petit Poucet, il laisse une marque sur l'arête u, v qu'il vient de traverser pour signier que v est le parent de u. r
ILLUST RAT ION
Illustrons déjà cette première phase de l'algorithme, qui est la partie descendante du parcours. Nous verrons la suite juste après. Sur le graphe de la gure suivante, faisons partir Graphix du sommet r = 7.
Graphes et algorithmes
36
Il a alors le choix d'aller soit en 2 soit en 1. Comme rien de particulier n'est spécié, imaginons qu'il décide de visiter le sommet 2. Il traverse donc l'arête 7, 2. Pour marquer cette arête (et le fait que 2 a maintenant pour parent 7), ajoutons sur le dessin une èche du sommet 7 vers le sommet 2. Maintenant, est sur le sommet 2 qui a pour voisins 1, 7, 6 et 4. D'après les règles précédentes, il ne va pas aller visiter 7 puisqu'il l'a déjà fait (c'est le sommet de départ). Il a donc le choix entre les trois autres. Imaginons qu'il aille vers le sommet 4 qui est alors visité pour la première fois. Il marque donc l'arête 2, 4. Il continue ainsi en allant, à chaque pas, vers un sommet non encore visité. Imaginons qu'il aille de 4 vers 3 puis de 3 vers 1. La gure 4.1 montre la trace du passage de .
Graphix
Graphix
Figure 4.1: Graphix est coincé en 1.
Que faire quand on est coincé ? Remonter vers le parent pour essayer de repartir vers l'avant. En appliquant les règles précédentes, Graphix peut se
retrouver coincé sur un sommet v : soit parce qu'il n'a pas d'autre voisin (on dit que v est une feuille ), soit parce qu'il est déjà passé par (il a déjà visité) tous les sommets qui l'entourent. C'est le cas sur la gure 4.1 ; il est entouré de sommets ( 7, 2 et 3) qu'il a déjà visités. Dans ce cas, va faire un pas en arrière et remonter vers le parent de v , qui est le sommet à partir duquel il a visité v pour la première fois (sur la gure 4.1, c'est donc le sommet 3). Appelons u, ce sommet (l'arête u, v est donc marquée par une èche). Revenu en u, va alors essayer, à nouveau, de progresser vers l'avant . est pugnace : tant qu'il peut aller vers l'avant
Graphix
Graphix
Graphix
4. Parcourons un graphe en profondeur
37
et visiter des sommets par lesquels il n'est pas déjà passé, il le fait. Il ne se résout à revenir vers le parent du sommet courant que s'il ne peut pas faire autrement, c'està-dire s'il n'a pas de voisin non encore visité. Ce point est important à comprendre. Le parcours en profondeur est donc une succession de découvertes de nouveaux sommets (phase vers l'avant ) et de retour en arrière vers le parent du sommet courant (phase dite de backtrack). Avant de savoir quand tout cela nit, ajoutons quelques conventions graphiques au parcours de notre personnage, ce qui nous permettra d'y voir plus clair et de dégager un peu mieux l'aspect chronologique des opérations. Lorsque est sur un sommet u, il doit savoir quels voisins de u il a déjà visités. En pratique, on ajoute à chaque sommet u une marque qui sera une couleur. Prenons les conventions suivantes pour xer les choses. Blanc (comme le sommet A de la gure 4.2) indique que le sommet n'a pas encore été visité (au tout début, tous les sommets sont blancs). Vert (comme le sommet B ) indique que le sommet a déjà été visité par mais que ce dernier est parti découvrir vers l'avant de nouveaue sommets ; nira par revenir en u (en suivant les èches à l'envers) ; le traitement de u n'est donc pas tout à fait terminé. Le sommet reçoit la marque rouge (comme le sommet C ) lorsque quitte le sommet u par l'arête qui le ramène vers le parent de u : toutes les possibilités d'exploration autour de u ont été épuisées et ne reviendra plus jamais en u.
Graphix
Graphix Graphix
Graphix
Graphix
Figure 4.2: Conventions de couleurs des sommets
Graphix
ILLUST RAT ION
En respectant ces conventions, lorsque arrive au sommet 1 à partir de 3, les sommets non encore visités sont toujours blancs et les autres sont, pour l'instant, verts. C'est ce qui est représenté sur la gure suivante.
Graphes et algorithmes
38
Graphix
Ne pouvant plus aller vers l'avant, fait un pas en arrière vers le parent 3 de 1. Avant de partir, il colore en rouge le sommet 1 qui n'a plus rien à lui orir. Revenu en 3, il va essayer d'aller à nouveau vers l'avant mais rien ne s'ore à lui, 1 et 4, les seuls voisins de 3, ayant déjà été visités. Il colore en rouge le sommet 3 puis fait un pas en arrière et revient en 4. Arrivé en 4, les choses sont un peu plus favorables. Il peut partir visiter 5 (c'est d'ailleurs son seul choix). Il marque donc l'arête 4, 5 (pour nous, une èche) et colore le sommet 5 en vert (qui vient d'être découvert). La gure suivante donne l'état des choses lorsqu'il arrive en 5.
Graphix
Ensuite va poursuivre son périple. Il a le choix entre 6 et 9. Imaginons qu'il aille en 6, puis en 8 et enn, en 9. La gure 4.3 (a) donne l'état des choses lorsqu'il est en 9. À partir de cette étape, il va être obligé de reculer (toujours vers son parent, c'est-à-dire en empruntant les èches à l'envers sur notre illustration). Cela va ainsi le faire revenir en 8, puis en 6, puis en 5, puis en 4, puis en 2, puis en 7. Chaque fois, il n'a pas pu aller vers l'avant pour découvrir des sommets non encore visités car il n'y en avait plus. colore en rouge le sommet en le quittant.
Graphix
Figure 4.3: (a) Graphix est en 9. (b) Situation nale
4. Parcourons un graphe en profondeur
39
Le parcours est terminé quand, revenu en r, il n'y a plus rien à explorer. Lorsque revient au sommet initial r et qu'il ne peut plus aller vers l'avant, alors le parcours est terminé. C'est le cas dans notre exemple lorsqu'il revient à r = 7. Le résultat de tout ce travail est l'ensemble des relations parent entre chaque sommet et son parent (les èches), ce qui est représenté sur la gure 4.3 (b). Notons qu'en oubliant le sens des èches, le graphe obtenu est un arbre, comme illustré sur la gure 4.4 où ses arêtes sont dessinées en pointillés.
Graphix
Figure 4.4: L'arbre construit par
Graphix
Mais le résultat n'est pas unique ? ! Oui, tout dépend des choix de
Graphix à chaque étape.
En eet, le résultat de cette méthode n'est (en général) pas unique. aurait aussi pu construire l'arbre de la gure 4.5. Essayez de refaire son parcours, toujours à partir de r = 7. Quoi qu'il en soit, si le graphe de départ est connexe, alors le résultat nal sera un arbre qui contiendra tous les sommets du graphe, quel que soit le sommet de départ.
Graphix
Figure 4.5: Un autre arbre qu'aurait pu construire
Graphix avec cette méthode.
Conclusion. Le parcours en profondeur est un algorithme classique , largement utilisé. Comme il a été dit un peu plus haut, il permet par exemple de construire
40
Graphes et algorithmes
un arbre couvrant un graphe connexe. En revanche, notez bien qu'il ne permet pas de calculer les distances par rapport au sommet de départ (pour cela, il faut utiliser le parcours en largeur vu au chapitre 3). Il peut aussi être utilisé pour tester la connexité d'un graphe. Nous aurons l'occasion de le revoir à l'÷uvre dans plusieurs chapitres de ce livre, notamment pour parcourir des arbres. Sa version adaptée aux graphes orientés sert de brique de base pour résoudre divers problèmes comme trouver les composantes fortement connexes, construire un ordre topologique, etc. Bref, c'est le couteau suisse de l'algorithmique.
5
Un arbre très léger
Ce chapitre va être l'occasion de manipuler des graphes pondérés. Dans un tel graphe, chaque arête u, v a un poids noté w(u, v), strictement positif : w(u, v) > 0. On se cantonnera ici aux graphes connexes (c'est-à-dire pour lesquels il existe au moins un chemin entre chaque paire de sommets). La gure 5.1 donne un exemple d'un tel graphe.
Figure 5.1: Un graphe connexe pondéré (l'entier sur l'arête est son poids)
Extraire un arbre couvrant de poids minimal. Notre objectif va consister dans ce chapitre à extraire d'un tel graphe connexe pondéré G = (V, E), un arbre T qui couvre tout G (qui contient tous les sommets de G et une partie de ses arêtes) et dont le poids total est minimal. Le poids de T étant tout simplement la somme des poids de ses arêtes. Une application classique est la suivante. Chaque sommet représente une agence d'une entreprise, chaque arête de G représente un lien réseau disponible entre deux agences et le poids de l'arête représente le coût (en euros) d'achat (ou de location, d'installation de mise en service, etc.) de ce lien. L'objectif
42
Graphes et algorithmes
est alors d'acheter (ou louer, installer, etc.) des liens de manière à assurer la connexité entre toutes les agences et que cela coûte le moins cher possible.
ILLUST RAT ION Considérons l'arbre de la gure 5.2 qui est bien un arbre couvrant le graphe G de la gure 5.1. Son poids est de 11 + 5 + 5 + 9 + 4 + 1 + 3 + 5 + 10 + 8 = 61.
Figure 5.2: Un arbre couvrant le graphe de la gure 5.1, de poids 61 Il est assez simple de se convaincre qu'il existe un arbre moins lourd. Essayez de rééchir quelques minutes à un algorithme qui pourrait en construire un. Avant d'aller plus loin, notons que la version pondérée du parcours en largeur, l'algorithme de Dijkstra décrit au chapitre 3, permet de calculer les distances par rapport à un sommet donné mais ne permet pas de résoudre notre problème. Ici l'arbre cherché doit être de poids total le plus petit possible ; ce n'est pas ce que fait l'algorithme de Dijkstra.
L'algorithme de Prim. Nous allons détailler maintenant un algorithme classique pour construire un arbre couvrant de poids minimal. Prenons n'importe quel sommet comme point de départ. Notons-le r (insistons sur le fait que r est vraiment n'importe quel sommet du graphe). Pour l'instant, r est seul dans l'arbre initial T . Ce dernier est donc encore loin de couvrir tout G. Faisons-le pousser pour qu'il couvre tout G. La première arête ajoutée est celle de poids minimal contenant r. S'il y en a plusieurs, en choisir une quelconque, celle que vous voulez. Notons-la r, u. Elle est ajoutée dans l'arbre, ainsi, bien sûr, que son extrémité u. Maintenant l'arbre a deux sommets (r et u) et une arête (r, u). Continuons la construction. Supposons que l'arbre T contienne déjà les sommets de l'ensemble V (T ) (dans lequel il y a r). L'étape suivante consiste à examiner toutes les arêtes qui ont une extrémité dans V (T ) et l'autre hors de V (T ). Parmi ces arêtes, prenons celle, notons-la u, v, de poids minimal, avec u ∈ V (T ) et v ∈ V (T ) (s'il y en a plusieurs de poids minimal, en prendre une, n'importe laquelle). Ajoutons cette nouvelle arête u, v et ce nouveau sommet v à l'arbre T . Cette règle d'ajout est appliquée tant que tous les sommets de G ne sont pas dans V (T ) (tant que l'arbre T en cours de construction ne couvre pas totalement (les sommets de) G).
5. Un arbre très léger
43
Cette stratégie est assez naturelle. Vous y aviez peut-être pensé. L'arbre courant est prolongé en lui ajoutant une nouvelle arête ( sortante ) de poids minimal. Notons que le choix de chaque étape est dénitif, la décision d'ajouter cette arête u, v sortante de poids minimal n'est pas remise en question dans la suite. En cela, cet algorithme est dit glouton.
ILLUST RAT ION Illustrons cette méthode sur le graphe de la gure 5.1, rappelé ci dessous.
Partons du sommet r = 6. Il y a trois arêtes contenant 6 : 6, 7 de poids 3, 6, 5 de poids 1 et 6, 8 de poids 4. Ici l'algorithme dit qu'il faut choisir l'arête 6, 5 car c'est la plus légère des trois. L'arbre T contient maintenant les sommets V (T ) = {5, 6} et l'arête 6, 5. Lors de la deuxième étape, les arêtes à examiner sont : 6, 7, 6, 8, 5, 7, 5, 3 et 5, 4 (mais pas l'arête 5, 6 car ses deux extrémités sont dans V (T )). Parmi celles-là, une seule est de poids minimal ; il s'agit de 5, 7, de poids 2. Maintenant V (T ) = {5, 6, 7} et les arêtes de T sont 5, 6 et 5, 7. La situation courante est donnée à la gure 5.3 où les arêtes en pointillés représentent les arêtes à examiner (celles qui sortent de T ) à la prochaine étape.
Figure 5.3: L'arbre T est coloré et les arêtes en pointillés sont celles à examiner pour continuer la construction Maintenant trois arêtes ont un poids minimal de 4 : 5, 3, 5, 4 et 6, 8. L'algorithme ne dit pas celle qu'il faut choisir précisément. En revanche, il exige d'en choisir
44
Graphes et algorithmes
obligatoirement une parmi ces dernières. Arbitrairement, choisissons 5, 4. La gure suivante représente la situation courante.
L'algorithme impose maintenant de choisir 4, 3 et on se retrouve dans la situation de la gure suivante.
À l'étape suivante, il n'y pas le choix, l'arête 6, 8 est la seule de poids minimal 4. Elle est intégrée. Comme tous les sommets ne sont pas encore dans l'arbre, l'algorithme se poursuit. Il y a le choix entre 4, 10, 3, 9 et 3, 2, toutes de poids 5. On choisit par exemple l'arête 3, 2, qui est ajoutée. La situation devient alors celle de la gure suivante.
5. Un arbre très léger
45
L'algorithme va ensuite ajouter 3, 9 (de poids 5) puis 4, 10 (de poids 5 aussi), puis 8, 11 (de poids 8), puis 2, 1, ce qui permet à tous les sommets d'être dans l'arbre. Le résultat nal est l'arbre de la gure suivante. Son poids total est 10 + 5 + 5 + 3 + 5 + 4 + 1 + 2 + 4 + 8 = 47.
Lorsque plusieurs arêtes peuvent être sélectionnées à une étape, est-ce qu'il y a un choix meilleur que les autres ? La réponse est non. L'arbre nal obtenu à la n de la construction dépend bien des choix faits à chaque étape, mais pas son poids.
ILLUST RAT ION Reprenons la construction à l'étape de la gure 5.3. À cette étape, la décision arbitraire de continuer avec l'arête 5, 4 a été prise mais 5, 3 aurait pu être choisie. En faisant ce choix de 5, 3, il faut ensuite prendre 3, 4 (de poids 3). La n de la démarche est similaire. Avec ces choix, l'arbre de la gure 5.4 aurait été construit au lieu de celui présenté un peu plus haut, mais le poids des deux est strictement le même.
Figure 5.4: Résultat nal si à l'étape 3 (voir gure 5.3), 5, 3 est choisie au lieu de 5, 4
Graphes et algorithmes
46
Zone orange Pourquoi est-ce que cela fonctionne ?
Pourquoi le fait d'ajouter l'arête de poids minimal à chaque étape donne à la n un arbre de poids minimal ? Vous pensez peut-être que c'est naturel : si je fais les meilleurs choix à chaque étape, alors à la n j'ai le meilleur résultat possible . Ce n'est pas toujours le cas (nous aurons l'occasion de le constater dans d'autres chapitres) mais ici, ça marche . Approfondissons un peu les choses en donnant les principaux arguments de la preuve (ce n'est pas une preuve complète et rigoureuse). À la première étape, l'algorithme sélectionne l'arête de poids minimal d'extrémité r (qui est le sommet par lequel la construction commence). Nécessairement, cette arête sera dans un arbre couvrant de poids minimal T ∗ (cela peut être montré par l'absurde par exemple). Notons V (T ) l'ensemble des sommets déjà inclus dans l'arbre partiel T et A l'ensemble des arêtes qui ont une seule extrémité dans V (T ) (les arêtes en pointillés sur les illustrations). Imaginons que jusqu'à présent l'algorithme ait fait les bons choix, c'est-à-dire qu'il ait sélectionné des arêtes faisant partie d'un arbre couvrant de poids minimal que l'on note T ∗ . Plaçons-nous à l'étape suivante. À cette étape, l'algorithme ajoute l'arête, notons-la u, v, de poids minimal, parmi toutes celles de A :
w(u, v) = min{w(a, b) : a ∈ V (T ), b ∈ V (T )}. Si cette arête fait partie de T ∗ , tout va bien. Si ce n'est pas le cas, montrons qu'alors u, v fait partie malgré tout d'un autre arbre optimal. Ajoutons pour cela l'arête u, v à T ∗ . Ajouter une nouvelle arête à un arbre créé un cycle (et un seul). Dans ce cycle, il y a nécessairement au moins une arête x, y de T ∗ dans A, c'est-à-dire avec x ∈ V (T ) et y ∈ V (T ) (sinon T ∗ ne serait pas connexe). Comme u, v est l'arête de poids minimal de A :
w(u, v) ≤ w(x, y). Supprimons alors x, y. Le nouveau graphe ainsi construit est un arbre couvrant T obtenu en ajoutant dans T ∗ l'arête u, v et en supprimant x, y. Le poids de T est donc égal à w(T ) = w(T ∗ ) − w(x, y) + w(u, v). Or, comme w(u, v) ≤ w(x, y), on a : w(T ) ≤ w(T ∗ ). Ainsi, comme T ∗ est un arbre couvrant de poids minimal, T en est un aussi. Conclusion : la nouvelle arête u, v ajoutée à cette étape ne fait pas partie de T ∗ mais fait partie d'un autre arbre couvrant de poids minimal T (qui contient aussi les arêtes précédentes sélectionnées). À chaque étape est donc bien ajouté un nouveau bout d'un arbre couvrant de poids minimal.
5. Un arbre très léger
Conclusion et suite.
47
L'algorithme de Prim (du nom de la personne qui l'a proposé) est simple à utiliser et permet d'obtenir un arbre couvrant de poids minimal d'un graphe pondéré connexe (à ne pas confondre avec l'algorithme de Dijkstra vu au chapitre 3 qui n'a pas les mêmes objectifs). C'est un bel exemple d'algorithme glouton qui, en prenant les meilleures décisions à chaque étape, construit une solution optimale à la n. Nous verrons au chapitre 19 une généralisation beaucoup plus dicile à résoudre de ce problème. Mais l'algorithme de Prim servira de base à une méthode approchée. Nous aurons aussi l'occasion de voir dans certains chapitres que prendre les meilleures décisions à chaque étape ne permet pas toujours d'obtenir une solution nale optimale, ce qui peut être contre-intuitif.
6
Construisons un arbre à partir d'une suite de degrés
Voici un petit exercice de construction d'arbre à partir d'informations restreintes. Imaginons que l'on dispose de n sommets, chacun associé à un entier positif non nul. La question ici est de savoir s'il est possible de construire un arbre T , contenant les n sommets, de telle manière que chaque sommet ait un degré (c'est-à-dire un nombre de voisins) égal à l'entier auquel il est associé. Rappelons qu'un arbre est un graphe connexe ne contenant aucun cycle. Pour l'illustrer, prenons n = 5 et les cinq entiers suivants : a1 = 3, a2 = 2, a3 = 1, a4 = 1, a5 = 1. La gure 6.1 donne un arbre qui vérie les contraintes. Les entiers associés sont écrits dans un cadre juste à côté du sommet. Vous pouvez vérier que cette valeur est bien égale au degré du sommet.
Figure 6.1: Un arbre dont la séquence des degrés est a1 = 3, a2 = 2, a3 = 1, a4 = 1, a5 =1
Avant de continuer, notons que le nom des sommets n'est pas important ici, seuls comptent le nombre de valeurs et les valeurs elles-mêmes. Prenons maintenant à nouveau n = 5 et cinq autres entiers : a1 = 4, a2 = 3, a3 = 2, a4 = 1, a5 = 1. Il faut donc construire un arbre T à cinq sommets tel que le degré d'un sommet soit égal à a1 = 4, le degré d'un autre sommet soit égal à a2 = 3, etc. Est-ce possible ? Vous pouvez prendre du papier, un crayon, une gomme et essayer. Vous n'y arriverez pas. Essayons de comprendre pourquoi. Rappelons d'abord que tout arbre à n sommets a exactement n − 1 arêtes (vu au chapitre 2). De plus, dans tout graphe (donc dans tout arbre aussi), la somme des
Graphes et algorithmes
50
degrés des sommets est égale à deux fois le nombre d'arêtes (vu aussi au chapitre 2). En combinant ces deux propriétés, on obtient que dans tout arbre à n sommets, la somme des degrés de ses sommets vaut exactement 2(n−1). Ainsi les n entiers donnés au départ doivent avoir pour somme exactement 2(n − 1), sinon il n'est pas possible de construire un tel arbre. Dans l'exemple numérique précédent, la somme des cinq valeurs vaut 4 + 3 + 2 + 1 + 1 = 11, or 2(n − 1) = 2(5 − 1) = 8. Cela ne concorde pas. Il est donc impossible de créer un arbre avec ces valeurs. Le raisonnement plus haut permet d'établir que s'il est possible de créer un arbre à n sommets ayant comme degrés n entiers strictement positifs a1 , . . . , an , alors leur somme vaut nécessairement 2(n − 1). La question naturelle maintenant est de savoir si la propriété réciproque est vraie. Exprimons-la : si n entiers strictement positifs a1 , . . . , an ont pour somme 2(n − 1), alors il existe nécessairement un arbre T à n sommets qui peuvent être numérotés 1, . . . , n de telle façon que degT (i) = ai . Il n'y a que deux possibilités. Soit la propriété est fausse. Il sut alors de trouver un contre-exemple pour le démontrer. Soit elle est vraie et il faut une preuve pour la démontrer. Je vous invite à y rééchir par vous-même avec du papier et un crayon avant de lire la suite.
Fabriquons un arbre.
On se donne n entiers strictement positifs a1 , . . . , an dont la somme vaut 2(n − 1). Construisons maintenant ensemble un arbre T à n sommets notés 1, . . . , n tel que degT (i) = ai . Suivez la recette. Créons n sommets 1, . . . , n. Au départ, il n'y a pas d'arête. Associons ensuite à chaque sommet i une variable xi dont la valeur initiale est l'entier ai . La valeur de la variable xi à chaque étape de construction va représenter le nombre d'arêtes auxquelles le sommet i devra encore être connecté. Voici l'opération d'ajout d'une arête. Ajouter une arête entre un sommet i dont la valeur xi est maximale et un sommet j dont la valeur xj est minimale mais non nulle, puis décrémenter d'une unité les valeurs des variables xi et xj (xi ← xi − 1 et xj ← xj − 1). Répéter cette opération d'ajout tant qu'au moins un sommet i n'a pas un degré égal à ai , c'est-à-dire tant qu'un sommet i a une variable xi ≥ 1. Avant d'illustrer cet algorithme, voyons combien d'étapes d'ajout sont nécessaires. Chaque étape ajoute une arête, or un arbre en contient n − 1. Il faudra donc n − 1 étapes en tout. Vu autrement, chaque étape permet de réduire d'une unité la valeur de deux variables, c'est-à-dire permet de diminuer de deux unités la somme des valeurs de toutes les variables qui vaut au départ, par hypothèse, exactement 2(n − 1).
ILLUST RAT ION Prenons n = 8 et a1 = 3, a2 = 2, a3 = 2, a4 = 2, a5 = 2, a6 = 1, a7 = 1, a8 = 1. La condition est bien vériée : 3+2+2+2+2+1+1+1 = 14 = 2(8−1). Construisons les huit sommets et les huit variables. La gure suivante représente ce graphe sans arête. Les valeurs des variables sont indiquées dans chaque cadre, à côté du sommet.
6. Construisons un arbre à partir d'une suite de degrés
51
Le sommet 1 est celui qui a la plus grande valeur. Plusieurs sommets ont la plus petite valeur. L'algorithme ne dit pas lequel choisir. Choisissons le sommet 8. Relions 1 et 8, et diminuons leur valeur d'une unité. Celle de 8 passe à 0. Le sommet 8 ne sera plus choisi aux prochaines étapes car seul un sommet de valeur non nulle peut être sélectionné. Pour bien les distinguer, les sommets qui ont une valeur nulle sont colorés. Nous veillerons aussi à représenter les sommets par ordre décroissant de leur valeur. La nouvelle situation est celle de la gure suivante.
La nouvelle étape d'ajout va permettre de relier un sommet de valeur maximale 2 à un sommet de valeur minimale 1. Comme ils sont plusieurs à avoir ces valeurs, décidons de relier le sommet le plus à gauche (ayant la valeur maximale) au sommet le plus à droite (ayant la valeur minimale) non coloré (valeur non nulle). Cela ajoute donc l'arête entre 1 et 7. La valeur de leur variable est diminuée d'une unité. Le sommet 7 devient coloré et le sommet 1 a maintenant une valeur de 1 seulement. Il est alors reclassé de manière à ce que les sommets soient triés par valeurs décroissantes. La nouvelle situation est par exemple celle de la gure suivante (le sommet 1 a été reclassé ici entre 5 et 6).
La prochaine connexion est entre les sommets 2 et 6. Après diminution des valeurs et reclassement de 2, on obtient la situation de la gure suivante.
Graphes et algorithmes
52
Les sommets 3 et 1 sont reliés, ce qui donne après reclassement :
Les sommets 4 et 3 sont reliés, ce qui donne après reclassement :
Les sommets 5 et 2 sont reliés, ce qui donne après reclassement :
Notez ici que l'ordre n'a pas besoin d'être changé. Les valeurs sont toujours décroissantes. Enn, 5 et 4 sont reliés.
Représentons le graphe obtenu en le redéployant pour constater de manière plus claire que c'est bien un arbre. Les valeurs ai initiales sont rappelées à côté de chaque sommet. Elles correspondent bien aux degrés.
Figure 6.2: L'arbre résultat
6. Construisons un arbre à partir d'une suite de degrés
53
À chaque étape, les sommets connectés ont été reclassés. Cette opération n'est pas obligatoire. Elle a été ajoutée ici pour garder les sommets par ordre décroissant des valeurs, ce qui permet d'ajouter systématiquement la nouvelle arête entre le sommet le plus à gauche et celui, non coloré, le plus à droite.
Zone orange Un peu plus général.
L'objectif de cette partie est de montrer que si une suite d'entiers positifs vérient la condition sur leur somme, alors il existe bien un arbre dont les degrés sont égaux aux éléments de la suite. Cette preuve est faite par récurrence. Si vous n'êtes pas à l'aise avec ce type de raisonnement, vous pouvez passer cette zone. Posons pour commencer quelques dénitions qui permettront d'énoncer les propriétés plus simplement. Une suite de n entiers positifs, non nuls, a1 , . . . , an dont la somme vaut 2(n − 1) sera appelée séquence arborescente et n est sa longueur. Avant d'aller plus loin, remarquons que la plus petite valeur d'une séquence arborescente est nécessairement égale à 1. Sinon la somme de toutes les valeurs vaudrait au moins 2n, ce qui n'est pas conforme à la dénition d'une séquence arborescente. De manière similaire, remarquons que lorsque n ≥ 3, la valeur la plus grande de toute séquence arborescente est strictement plus grande que 1. Sinon, si ce n'était pas le cas, toutes les valeurs de la séquence seraient égales à 1 et la somme vaudrait exactement n, ce qui n'est pas non plus conforme à la dénition d'une séquence arborescente. On dira qu'un arbre T à n sommets réalise la séquence arborescente a1 , . . . , an (ou que la séquence est réalisée par T ) si T a un sommet de degré a1 , un sommet de degré a2 ,. . . , un sommet de degré an . Par exemple, l'arbre de la gure 6.2 réalise la séquence a1 = 3, a2 = 2, a3 = 2, a4 = 2, a5 = 2, a6 = 1, a7 = 1, a8 = 1. Avec ce vocabulaire, l'objectif revient à prouver que pour toute séquence arborescente, il existe un arbre qui la réalise. Montrons cette propriété par récurrence sur la longueur des séquences. La seule séquence arborescente possible de longueur 2 est a1 = 1, a2 = 1 et elle est réalisée par un arbre avec une seule arête entre 1 et 2. Les seules séquences arborescentes possibles de longueur 3 sont : a1 = 2, a2 = 1, a3 = 1 ou a1 = 1, a2 = 2, a3 = 1 ou a1 = 1, a2 = 1, a3 = 2. Chacune est réalisée par l'arbre suivant.
La propriété est montrée aux rangs 2 et 3. Posons l'hypothèse de récurrence au rang n : pour toute séquence arborescente de longueur n, il existe (au moins) un arbre qui la réalise. Considérons maintenant n ≥ 3 et une séquence arborescente a1 , . . . , an+1 , notée A, de longueur n + 1. Notons ai une valeur minimale et aj une valeur maximale de A. Nous avons vu un peu plus haut que ai = 1 et aj ≥ 2.
Graphes et algorithmes
54
Construisons une nouvelle séquence B à partir de A en mettant toutes les valeurs de A, sauf ai , et en remplaçant aj par aj − 1. Cette nouvelle séquence B est de longueur n (l'élément ai a été supprimé), chaque entier a une valeur non nulle (comme aj ≥ 2, aj − 1 ≥ 1) et la somme de ses n valeurs vaut exactement 2(n − 1) (car la somme des ai initiaux vaut 2((n + 1) − 1) = 2n et la nouvelle séquence B a supprimé deux unités dans cette somme qui vaut donc 2n − 2 = 2(n − 1)). B est donc une séquence arborescente de longueur n. Par hypothèse de récurrence, il existe un arbre TB qui réalise B . Ajoutons maintenant à TB un nouveau sommet et connectons-le au sommet de degré aj − 1 de TB . Ce nouveau graphe est bien un arbre réalisant la séquence A. L'algorithme présenté plus haut permettant de construire un arbre réalisant une séquence arborescente est directement inspiré de cette preuve.
Conclusion.
Savoir s'il est possible de construire un arbre à partir d'une suite de degrés est facile. En construire un si c'est possible est facile aussi, il sut d'appliquer l'algorithme décrit dans ce chapitre. Maintenant imaginons que l'on veuille construire un graphe (pas forcément un arbre) à n sommets à partir d'une suite a1 , . . . , an d'entiers. Comme la somme des degrés des sommets d'un graphe vaut deux fois le nombre d'arêtes, il faut impérativement que la somme des entiers soit paire. Mais cela ne sut pas. Par exemple, la somme des éléments de la suite a1 = 3, a2 = 3, a3 = 1, a4 = 1 est paire et, pourtant, il n'existe aucun graphe (connexe ou non) dont les degrés sont ces valeurs. Il y a bien une formule permettant de savoir s'il existe ou non un graphe ayant comme degré une suite donnée de valeurs, mais elle est un peu plus compliquée à exprimer que dans le cas particulier des arbres vus ici.
7
Dessinons un graphe dans le plan sans croiser les arêtes
Ce chapitre est consacré à un problème lié au dessin d'un graphe dans le plan (ou sur une feuille de papier). On veut dessiner ici toutes les arêtes sous forme de traits (pas forcément droits) de manière à ce qu'ils ne se croisent pas. Si c'est possible, le graphe est dit planaire. Une application concerne les circuits électroniques où des ls électriques doivent être intégrés à une plaque plane : les ls ne doivent pas se croiser pour éviter tout risque de court-circuit. Le graphe G = (V, E) doit donc être dessiné sur le plan sans que ses arêtes se coupent, ni au milieu du trait ni sur les deux sommets qui dénissent cette arête. Les sommets peuvent être placés comme bon nous semble. ILLUST RAT ION
Illustrons cela à l'aide d'un exemple. Est-ce que le graphe de la gure 7.1 est planaire ?
Figure 7.1: Un graphe
À la vue de la représentation graphique de la gure 7.1, on risque de conclure un peu trop hâtivement que non car les arêtes 1, 4 et 2, 3 se croisent. Mais le graphe de la gure 7.1 peut aussi être dessiné comme sur la gure 7.2 et il s'agit maintenant d'une représentation des mêmes arêtes qui ne se croisent plus. Cela sut pour conclure qu'il est planaire.
56
Graphes et algorithmes
Figure 7.2: Représentation planaire du graphe de la gure 7.1 Une petite remarque à propos des sommets avant d'aller plus loin. Sur les dessins qui vont illustrer les explications, les sommets sont représentés par des (gros) cercles, souvent avec un numéro à l'intérieur. Comme c'est le cas par exemple sur le graphe de la gure 7.1. La grosseur des sommets est volontairement exagérée de manière à pouvoir les voir sur une gure. Mais il ne faut pas oublier que ces ronds représentent des éléments, les sommets du graphe. Ils n'ont pas d'épaisseur, pas de grosseur. Ce sont des points du plan, grossis articiellement pour les visualiser. Dans tous les autres chapitres de cet ouvrage, ce détail n'a aucune importance car la représentation graphique sert uniquement de support visuel pour représenter les liaisons, les relations entre les sommets. Les traits peuvent être placés n'importe comment. Ici les contraintes sont plus géométriques , il faut donc prendre en compte ce détail. Un sommet sera toujours représenté sous forme d'un rond dans ce chapitre, mais il faut bien se rappeler que ce sont en réalité des points, sans épaisseur.
L'étude des graphes connexes est susante. Si le graphe G n'est pas connexe, il sut de travailler sur chacune de ses composantes connexes de manière indépendante. Si elles sont toutes planaires, alors il sut de les dessiner chacune de manière planaire à bonne distance les unes des autres pour obtenir une représentation planaire du graphe dans son ensemble. La gure 7.3 donne un petit exemple.
Figure 7.3: Représentation planaire (à droite, en couleur) du graphe non connexe (de gauche)
Est-ce que tous les graphes sont planaires ? Si vous rééchissez deux minutes à la question, je pense que vous allez vous rendre compte que si un graphe a beaucoup d'arêtes alors il y a peu de chance qu'elles puissent être dessinées sans se
7. Dessinons un graphe dans le plan sans croiser les arêtes
57
croiser. Mais une intuition n'est évidemment pas une preuve. En revanche, la réponse à la question est : certains graphes ne sont pas planaires et leurs dessins nécessitent que des arêtes se croisent obligatoirement. Par exemple, le graphe de la gure 7.4, dont le nom est K3,3 , n'est pas planaire. Bien sûr, il ne sut pas de dessiner une représentation non planaire de K3,3 (comme sur la gure 7.4) pour prouver que K3,3 n'est pas planaire. Il faut une preuve. Celle-là étant un peu compliquée, nous la ferons en zone orange plus loin. Ce problème vous rappelle peut-être un jeu assez populaire qui consiste à relier trois maisons alignées (sommets 1, 2 et 3) à trois sources (eau, électricité, gaz) alignées elles aussi de l'autre côté de la rue (sommets 4, 5 et 6) de manière à ce que les tuyaux ne se croisent pas. Si vous avez essayé de le faire, vous n'y êtes jamais arrivé. Vous allez enn comprendre pourquoi ce n'est pas possible. Cela va aussi être l'occasion de découvrir la très belle formule d'Euler.
Figure 7.4: Le graphe
K3,3 , qui n'est pas planaire
Une face.
Pour exprimer la formule d'Euler, qui va nous servir plus tard pour montrer que K3,3 n'est pas planaire, nous avons besoin de dénir la notion de face. Prenons un graphe G = (V, E) connexe, planaire, à n sommets et m arêtes. Considérons une représentation graphique planaire de G. Pour xer les idées, prenons par exemple celle de la gure 7.5.
Figure 7.5: Une représentation planaire du graphe La dénition stricte d'une face est un peu compliquée mais intuitive. Une face d'une représentation graphique de G est la surface du plan fermée délimitée par des arêtes de G de manière à ce que si vous placez deux personnes à l'intérieur de cette zone, Alice et Bob, elles doivent pouvoir se rejoindre en cheminant dans cette zone sans jamais traverser aucune arête ou aucun sommet de G. À ces faces fermées
Graphes et algorithmes
58 est ajoutée une face un peu particulière, dite de plan innie en dehors du graphe .
face externe
, qui correspond à la surface
ILLUST RAT ION Dans l'exemple de la gure 7.5, on peut distinguer les faces suivantes : celle délimitée par les arêtes 1, 2, 2, 3 et 3, 1 ; celle délimitée par les arêtes 2, 5, 5, 4, 4, 3 et 3, 2 ; celle délimitée par les arêtes 2, 6, 6, 5 et 5, 2 ; la face externe est ici en réalité délimitée par les arêtes du pourtour de G. Dans cet exemple simple, les faces sont assez claires. Elles sont délimitées par des du graphe. Mais attention, tout cycle ne délimite pas forcément une face. Par exemple, le cycle suivant ne délimite pas une face : 2, 6, 5, 4, 3, 2. Plaçons Alice et Bob comme sur le schéma de la gure 7.6. Ils ne peuvent pas se rejoindre sans traverser au moins une arête de G. Ce cycle ne délimite donc pas une face. cycles
Figure 7.6: Alice ne peut pas rejoindre Bob sans traverser au moins une arête
La formule d'Euler pour les graphes planaires. Nous avons maintenant tout pour exprimer le très beau résultat suivant, valable pour toute représentation planaire d'un graphe G connexe à n sommets, m arêtes et f faces : n − m + f = 2. La formule (7.1) est la
formule d'Euler
(7.1)
pour les graphes planaires.
Vérions cette formule sur des exemples. Vérions cette formule sur le graphe de la gure 7.6. Il a n = 6 sommets, m = 8 arêtes et f = 4 faces (n'oubliez pas la face externe !). On a bien n − m + f = 6 − 8 + 4 = 2. Cela n'est, évidemment, pas une preuve de la formule d'Euler mais une simple vérication sur un exemple. Vérions-la sur d'autres représentations planaires de graphes connexes.
7. Dessinons un graphe dans le plan sans croiser les arêtes
59
Le graphe de la gure 7.7 a n = 11 sommets, m = 12 arêtes et f = 3 faces. Les trois faces sont délimitées ici par : le cycle 1, 2, 3, 4, 1 ; le cycle 3, 4, 5, 6, 7, 8, 3 ; plus la face externe, à l'extérieur du cycle qui fait le tour du graphe : 1, 4, 5, 6, 7, 8, 3, 2, 1.
Figure 7.7: Une représentation graphique planaire d'un graphe connexe avec n = 11,
m = 12 et f = 3
Remarquez que le cycle 3, 4, 5, 6, 7, 8, 3 délimite bien une face car où que soient Alice et Bob dans cette zone, ils peuvent se rejoindre sans croiser aucune arête de G (en contournant éventuellement l'arête 5, 11 et les arêtes 8, 9 et 9, 10). On a bien n − m + f = 11 − 12 + 3 = 2. Examinons le cas de la gure 7.8. Ce graphe est connexe, il a n = 9 sommets et m = 8 arêtes mais il n'a pas de cycle. Est-ce que f = 0 ? Non car il y a toujours la face externe, qui est la seule. Il s'agit bien d'une face car où que soient Alice et Bob, ils peuvent facilement contourner les arêtes pour se rencontrer. Il n'y a qu'une seule face (f = 1) et on a bien n − m + f = 9 − 8 + 1 = 2.
Figure 7.8: Un arbre est toujours planaire. Il n'a qu'une face (externe)
Graphes et algorithmes
60
Zone orange Utilisons maintenant la formule d'Euler pour montrer que K3,3 n'est pas planaire. La formule d'Euler est très élégante et très simple à écrire (quand on sait ce qu'est une face). Mais elle est aussi bien utile. Nous allons l'employer pour prouver que le graphe K3,3 , représenté à la gure 7.4, n'est pas planaire. Faisons une preuve par l'absurde (seuls les principaux arguments de la preuve sont donnés, pas les détails). Supposons que K3,3 est planaire. Sous cette hypothèse, xons une représentation planaire et notons f son nombre de faces. Comme K3,3 contient plusieurs cycles, il a plusieurs faces. Pour chacune de ses faces F , notons d(F ) le nombre d'arêtes du cycle qui délimite la face F . Dans K3,3 , chaque arête est à l'interface entre exactement deux faces (les arêtes du pourtour extérieur sont entre la face externe et une face interne). Ainsi, la somme des d(F ) pour toutes les faces est exactement égale à deux fois le nombre m d'arêtes de K3,3 . Écrit de manière synthétique, cela donne : d(F ) = 2m F ∈F
avec F représentant l'ensemble des faces. Par ailleurs, le plus petit cycle de K3,3 contient au moins quatre arêtes ( K3,3 est un graphe biparti ) et ainsi d(F ) ≥ 4 pour chaque face. En combinant ces résultats, on obtient : 2m = d(F ) ≥ 4f. F ∈F
Comme m = 9, on a 18 ≥ 4f , c'est-à-dire 92 ≥ f . Mais f étant un entier (il n'y a pas de bouts de faces), il est en réalité inférieur à 4 : f ≤ 4. Comme, par hypothèse, K3,3 est planaire et qu'il est clairement connexe, la formule d'Euler peut être utilisée. Or n = 6, m = 9 et f ≤ 4, on a ainsi 2 = n − m + f ≤ 6 − 9 + 4 = 1. Cela conduit à 2 ≤ 1, ce qui est absurde. L'hypothèse de départ ( K3,3 est planaire) est fausse. Dans ce qui précède, la formule d'Euler (7.1) a été utilisée mais n'a pas été démontrée. Pour cela, rendez-vous dans la zone rouge suivante.
Zone rouge Justication de la formule d'Euler.
Rappelons la formule. Soit une représentation graphique planaire d'un graphe G = (V, E) connexe à n sommets, m arêtes et f faces. Alors nécessairement : n−m+f = 2. Nous n'allons pas faire une démonstration de ce résultat au sens mathématique strict. Nous allons plutôt illustrer sur un exemple une de ses preuves (il en existe plusieurs) particulièrement élégante.
7. Dessinons un graphe dans le plan sans croiser les arêtes
61
Considérons donc une représentation planaire d'un graphe G. Par exemple celle du graphe de la gure 7.9 avec ses six faces : F 1, . . . , F 6.
Figure 7.9: Un graphe planaire et ses faces Comme G est connexe, construisons un arbre T qui le couvre. N'importe quel arbre T couvrant G fera l'aaire. En voici un à la gure 7.10, dont les arêtes sont en pointillés.
Figure 7.10: Graphe planaire, ses faces et un arbre couvrant quelconque (arêtes en pointillés) Construisons maintenant un nouveau graphe, le graphe des faces, que l'on note Gf , comme suit. À chaque face F i associons un nouveau sommet noté Xi. Une arête est placée entre deux sommets Xi et Xj si les deux faces correspondantes partagent une arête de G qui n'est pas dans T . Illustrons cette opération à partir des données de la gure 7.10. Le résultat est représenté à la gure 7.11. Un nouveau sommet (coloré) représente chaque face et les arêtes (en pointillés colorés) adéquates ont été placées entre les
Graphes et algorithmes
62
sommets Xi. Par exemple, l'arête X2, X6 entre X2 et X6 est présente car les deux faces F 2 et F 6 sont séparées par une arête qui n'est pas dans T (l'arête 6, 11).
Figure 7.11: Graphe planaire, ses faces, un arbre couvrant (quelconque) et le graphe des faces Gf
Faisons le décompte de toutes ces arêtes.
jeu :
Trois graphes sont en
le graphe G = (V, E) de départ pour lequel il faut montrer que la formule d'Euler est vériée. G a n sommets, m arêtes et f faces ; l'arbre T = (V, E ) qui couvre G. Notons mT son nombre d'arêtes. Comme T couvre les n sommets de G, il a lui aussi n sommets. Comme tout arbre ayant n sommets a nécessairement n − 1 arêtes (propriété de tous les arbres, voir chapitre 2), mT = n − 1 ; le graphe des faces Gf qui a mf arêtes. Ce graphe a autant de sommets que G a de faces, c'est-à-dire f . Or Gf est lui aussi un arbre. On ne démontrera pas ce point mais on peut le vérier sur la gure 7.11 : ce graphe est bel et bien un arbre. Ainsi (comme pour T ) son nombre d'arêtes est égal à son nombre de sommets moins 1. Comme il a f sommets, il a donc mf = f − 1 arêtes. Examinons plus précisément ses f − 1 arêtes. Par construction, une arête de Gf coupe une arête de G qui n'est pas dans T . De plus, chaque arête de G qui n'est pas dans T sépare exactement deux faces et est donc coupée par exactement une arête de Gf . Ainsi il y a le même nombre d'arêtes dans Gf que d'arêtes de G pleines (constatez-le sur l'illustration). De tout cela, on tire que le nombre d'arêtes mT de T plus le nombre d'arêtes mf est exactement égal au nombre d'arêtes m de G : mT + mf = m.
7. Dessinons un graphe dans le plan sans croiser les arêtes
63
Or, mT = n − 1 et mf = f − 1. Cela entraîne : n − 1 + f − 1 = m. En réarrangeant, on obtient : n − m + f = 2, c'est-à-dire précisément la formule d'Euler.
Conclusion.
Dessiner un graphe sans que les arêtes se croisent est un problème qui a des applications pratiques en électronique des circuits par exemple. C'est un sujet d'étude qui mélange la combinatoire et la géométrie du plan ou d'autres espaces plus exotiques (dessins de graphes sur des tores par exemple). L'étude des graphes planaires a dégagé des concepts profonds qui ont, à leur tour, eu des impacts théoriques ailleurs. La présentation faite dans ce chapitre n'est qu'un aperçu de cet immense chantier toujours en cours.
8
Passons une seule fois par chaque arête
Reprenons notre personnage Graphix qui doit, dans ce chapitre, cheminer dans un graphe G, de voisin en voisin, et emprunter chaque arête exactement une fois. De plus, il devra partir d'un sommet r initial et se retrouver, à la n de son périple, à nouveau sur r. On suppose dans tout ce chapitre que G est connexe. Un tel parcours, s'il existe, est dit parcours cyclique eulérien de G (connu aussi sous le nom de parcours eulérien ou même cycle eulérien). Cela est particulièrement utile si Graphix conduit une benne à ordures ménagères et doit faire une tournée pour récolter le contenu de toutes les poubelles individuelles déposées sur les trottoirs d'une ville dont le plan est représenté par un graphe (une rue ou une portion de rue est représentée par une arête). À la n de sa tournée, il doit revenir à son point de départ, la déchetterie, pour vider sa benne. Pour être sûr de ne pas oublier de poubelles, il doit passer au moins une fois par chaque arête et pour minimiser la longueur de sa tournée, il doit passer au plus une fois par chaque arête (si c'est possible). ILLUST RAT ION
Illustrons cela avec le graphe de la gure 8.1 (a) qui a dix arêtes. Plaçons Graphix initialement sur le sommet 4 par exemple. Il peut alors faire le parcours indiqué à la gure 8.1 (b) où le numéro de passage de chaque arête est indiqué dans une étiquette grise sur l'arête elle-même. Il va donc suivre les arêtes dans l'ordre suivant : 4, 6, puis 6, 5, puis 5, 4, puis 4, 3, puis 3, 1, puis 1, 2, puis 2, 3, puis 3, 5, puis 5, 2, puis 2, 4. Il est donc parti du sommet r = 4 et, après avoir traversé exactement une fois chaque arête en passant d'un sommet vers un de ses voisins, il est revenu, à la n, à son sommet de départ r = 4. Ce graphe contient donc bien un parcours cyclique eulérien. Remarquez que Graphix est passé une seule fois par chaque arête mais plusieurs fois par les sommets 2, 3, 4, 5.
Graphes et algorithmes
66
Figure 8.1: (a) Un graphe connexe. (b) Un parcours cyclique eulérien de ce graphe
Le sommet de départ n'est pas important. Si le graphe G a les étiquettes d'un parcours cyclique eulérien, alors peut partir de n'importe quel sommet initial, pas forcément celui qui est dans une arête d'étiquette 1. S'il débute d'un autre sommet u, il va partir en premier sur l'arête ayant la plus grande étiquette, puis poursuivre tranquillement son voyage en passant à chaque étape par l'arête qui a l'étiquette de numéro suivant (circulairement). Par exemple, si dans le parcours de la gure 8.1 (b), il part du sommet 3, il va emprunter en premier l'arête 3, 5 de numéro 8, puis l'arête d'étiquette 9 qui est 5, 2, puis l'arête 2, 4 d'étiquette 10. Arrivé en 4, il n'y a pas d'arête d'étiquette 11. Qu'à cela ne tienne, à ce point de rupture des étiquettes, il repart sur la plus petite, l'arête 4, 6 d'étiquette 1, et continue en empruntant à nouveau l'arête d'étiquette suivante, etc. Il terminera son périple en traversant 2, 3 qui porte le numéro de départ ( 8) moins 1, la précédente de celle de départ. C'est pour cela que ce parcours est qualié de cyclique.
Graphix
Certains graphes n'ont pas de parcours cyclique eulérien. Il faut impérativement que le graphe soit connexe. Mais est-ce que tout graphe connexe a un parcours cyclique eulérien ? Un instant de réexion va vous persuader que non. Par exemple, aucun des trois graphes (connexes) de la gure 8.2 n'en possède. Essayons de comprendre pourquoi. Le cas du graphe le plus à gauche est relativement clair. Si arrive sur le sommet qui n'a qu'une arête, il ne pourra plus en sortir. S'il part initialement de ce sommet, il ne pourra plus y revenir à la n.
Graphix
8. Passons une seule fois par chaque arête
67
Figure 8.2: Trois graphes connexes dont aucun n'a de parcours cyclique eulérien Les deux autres graphes ont chacun un sommet u de degré 3. Ce sommet u a trois voisins, que l'on note v1 , v2 , v3 . Comme cela a été remarqué plus haut, on peut débuter le parcours cyclique de n'importe quel sommet. peut donc se placer initialement sur le sommet u. Il part alors sur l'arête u, v1 par exemple. Plus tard, il va revenir en u, par exemple par l'arête u, v2 . Il devra donc repartir par l'arête u, v3 . Une fois cette arête franchie, il n'a plus la possibilité de revenir en u pour nir son parcours car toutes les arêtes contenant u ont déjà été empruntées une fois.
Graphix
Entrer et sortir de u nécessairement un même nombre de fois. Le petit raisonnement précédent peut facilement être généralisé si G contient un sommet u qui a un nombre impair de voisins (qui a donc un degré impair). Ainsi, si G a un sommet de degré impair, alors il n'a pas de parcours cyclique eulérien. Ce qui revient à dire : si G a un parcours eulérien cyclique, alors chaque sommet de G a un degré pair (rappelons que G est connexe). Est-ce susant ? Est-ce que la réciproque est vraie ? Considérons un graphe G = (V, E) connexe dans lequel chaque sommet a un degré pair. Est-ce qu'il contient forcément un parcours cyclique eulérien ? La réponse est oui. Mieux que ça, il y a un algorithme qui permet de le construire, à découvrir dans la zone orange suivante. Zone orange Nous allons décrire ici les idées principales d'une méthode pour construire de manière eective un parcours cyclique eulérien dans un graphe connexe où chaque sommet a un nombre pair de voisins. Cela se fait en deux temps.
Laissons d'abord Graphix déambuler librement dans G sans passer deux fois par la même arête. Nous allons donner une idée
de l'algorithme en reprenant l'exemple du graphe de la gure 8.1 (a). Il est facile de voir qu'il est bien connexe et que chaque sommet a bien un nombre pair de voisins. Plaçons , par exemple, sur le sommet 1 et
Graphix
Graphes et algorithmes
68 laissons-le déambuler librement dans
G
de voisin en voisin avec la règle sui-
vante : interdiction de passer par une arête par laquelle il est déjà passé. Il peut, par exemple, suivre les arêtes suivantes, dans cet ordre :
2, 3,
puis retour en
de sortir de
1
1
par
3, 1.
Le voilà revenu en
1
1, 2, puis
mais sans possibilité
puisque les deux arêtes ont déjà été empruntées. Pour xer les
choses, la gure 8.3 représente en pointillés ces trois arêtes empruntées, sur lequelle est noté aussi l'ordre dans lequel il les a empruntées.
Figure 8.3: Trace du sous-parcours numéro 1 de Graphix Dans cette situation, on impose une seconde règle à
Graphix
arêtes qui sont autour de toi ont déjà été empruntées, alors
saute
: si toutes les directement
sur un sommet qui a encore des arêtes non empruntées et déambule à nouveau à partir de ce sommet (en réalité, la règle exacte est plus précise mais serait un peu longue et compliquée à décrire). Il doit appliquer ces règles tant qu'il y a des arêtes non encore empruntées. Continuons l'illustration sur notre exemple. Il saute directement, par exemple, sur le sommet
2
(qui a encore des arêtes non empruntées), puis
continue sa déambulation en empruntant par exemple les arêtes
4, 5,
puis
5, 2.
Le voilà revenu en
2,
sans pouvoir sortir de
2.
2, 4,
puis
La gure 8.4
représente en pointillés les arêtes empruntées pendant cette deuxième partie.
Figure 8.4: Trace du sous-parcours numéro 2 de Il
applique
sommet
3
à
nouveau
la
seconde
règle
et
saute
Graphix directement
sur
le
qui a des arêtes non encore empruntées. La gure 8.5 représente
en pointillés les arêtes empruntées.
8. Passons une seule fois par chaque arête
69
Figure 8.5: Trace du sous-parcours numéro 3 de
Graphix
Graphix
Le voyage de est terminé car toutes les arêtes ont été traversées une fois. Cependant, le résultat n'est, pour l'instant, pas un parcours cyclique eulérien. Ce sont des bouts de parcours, appelés des sous-parcours, qu'il faut recoller pour donner une cohérence globale à tout cela.
Recollons les morceaux. Graphix
Les divers sous-parcours sont ensuite mis en cohérence. Dans l'exemple des trois sous-parcours représentés à la gure 8.5, l'idée est la suivante. va repartir du premier sommet par lequel tout à commencé, ici le sommet 1. Il va ensuite parcourir les arêtes une à une en les numérotant au fur et à mesure qu'il les traverse, de manière incrémentale, à partir de 1. Il va parcourir le premier sous-parcours puis, lorsqu'il rencontre un sous-parcours sur un sommet tel que 2, il va suspendre le premier sousparcours pour faire le deuxième et, à la n, reprendre le premier, etc. (ici aussi les règles exactes sont un peu plus compliquées et ne sont pas données). La gure 8.6 donne un résultat possible. part de 1, va vers 2. En 2, il suspend son voyage dans le sous-parcours 1 pour faire le sousparcours 2 entièrement. Revenu au sommet 2, il reprend le sous-parcours 1 et arrive en 3. Il suspend alors à nouveau le sous-parcours 1 et fait le sousparcours 3. Revenu au sommet 3 il peut, enn, terminer le sous-parcours 1.
Graphix
Figure 8.6: Le parcours nal (numéros en gris)
Graphes et algorithmes
70
Une variante : parcourir une fois toutes les arêtes sans forcément revenir au point initial à la n. Imaginons que Graphix ait simplement besoin de
traverser une seule fois chaque arête (comme précédemment) mais sans la contrainte d'avoir à revenir sur le sommet initial à la n de son voyage. On suppose aussi qu'il a la possibilité de se placer initialement sur le sommet de son choix. Examinons le graphe de la gure 8.7. Le sommet 5 a trois voisins, ce qui indique que G n'a pas de parcours cyclique eulérien (d'après les résultats évoqués plus haut). Pourtant, en partant de 5, peut parcourir une fois chaque arête. L'ordre de parcours des arêtes est donné à la gure 8.7. Ici il débute en 5 et termine en 4.
Graphix
Figure 8.7: Un parcours eulérien non cyclique La particularité de ces deux sommets est que ce sont les seuls à avoir un nombre impair de voisins. Si G est connexe et a exactement deux sommets ayant un nombre impair de voisins, alors G a un parcours eulérien non cyclique. Les preuves et les techniques sont très similaires à ce qui a été fait juste avant. La gure 8.7 vous rappelle peut-être le jeu qui consiste à dessiner un motif sans lever le crayon. Ici, il s'agit de la traduction en termes de graphes de ce jeu si le motif à réaliser est celui de la gure 8.8.
Figure 8.8: La petite maison à dessiner sans lever le crayon
8. Passons une seule fois par chaque arête
Conclusion.
71
Savoir si un graphe connexe a ou non un parcours cyclique eulérien est particulièrement simple, il sut de vérier que chaque sommet a un degré pair. L'algorithme qui permet de construire de manière eective un tel parcours est un peu délicat à manipuler, mais cela reste un problème facile à résoudre. Il semble que le premier qui se soit posé ce type de question (et qui soit arrivé à lui donner une réponse) est Leonhard Euler (17071783), génial mathématicien suisse qui est l'auteur d'importantes contributions dans de nombreuses branches des mathématiques. Ce travail originel est connu sous le nom du problème des ponts de Königsberg qui consiste à passer exactement une seule fois sur chacun des sept ponts reliant les berges d'une rivière et deux îles. La représentation des ponts de cette conguration particulière sous forme d'arêtes donne un multigraphe, c'est-à-dire un graphe dans lequel il est possible d'avoir plusieurs arêtes entre chaque paire de sommets. Les résultats de ce chapitre peuvent être étendus à ces cas. Il peuvent aussi être étendus à des graphes orientés où le sens de parcours d'une arête est imposé (pensez à une rue à sens unique). Ces notions et ces algorithmes servent de manière concrète à planier des trajets de véhicules, par exemple la tournée d'un camion qui doit passer dans chaque rue pour collecter les ordures ménagères. Pour ce type d'application, une question naturelle est la suivante. Si le graphe n'a pas de parcours cyclique eulérien, comment passer au moins une fois par chaque arête, en revenant à la n à son sommet de départ, tout en repassant un nombre minimal de fois par chaque arête (pour minimiser le coût de la tournée) ? Cela est connu sous le nom du problème du postier chinois. Dans le (vaste) domaine industriel des tournées de véhicules, d'autres contraintes peuvent intervenir : capacité du camion (que faire s'il est plein avant la n de la tournée ?), gestion de ottes de plusieurs camions (comment les répartir au mieux ?), temps de repos des chaueurs, entretien des véhicules, prise en compte de dates de passage à certains points, etc. La gestion de ces contraintes nous entraîneraient bien trop loin et ne sont pas toutes du strict domaine des graphes, même si ceux-là servent à représenter le réseau sur lequel les véhicules doivent circuler. Comme mentionné plus haut, ce problème était à l'origine un simple dé ludique pour les habitants de Königsberg du XVIII e siècle, qui n'arrivaient pas à le résoudre. Euler a apporté une réponse en constatant que ce n'était pas un problème de nature géométrique mais combinatoire. Il ne connaissait pas encore les graphes (en tout cas tels que présentés ici), cependant ce résultat est considéré comme l'un des plus vieux dans ce domaine. Il a quitté le domaine des mathématiques amusantes depuis longtemps pour entrer dans celui de l'algorithmique où il est utilisé pour organiser des déplacements de biens et de personnes. Le prochain chapitre présente un problème d'apparence plus simple (passer une seule fois par chaque sommet) mais qui est en réalité profondément plus complexe à résoudre.
9
Passons une seule fois par chaque sommet
Un cycle hamiltonien d'un graphe G = (V, E) connexe est un cycle qui contient tous les sommets de G. Si G a un cycle hamiltonien C , alors le personnage Graphix peut, en partant de n'importe quel sommet r de G, passer une et une seule fois en chaque sommet de G, en traversant à chaque pas une arête de C , tout en revenant, à la n, sur le sommet de départ r. Le nombre d'arêtes d'un cycle hamiltonien est donc égal au nombre n de sommets dans G. Cela peut être utile par exemple si Graphix est un convoyeur de fonds dont la mission est de récolter en n de journée le contenu des caisses de divers magasins, chacun étant représenté par un sommet et les rues par des arêtes. Notons que certains graphes n'ont pas de cycle hamiltonien alors que d'autres en ont un ou plusieurs. Hélas, savoir si G a ou non au moins un cycle hamiltonien est un problème que personne ne sait résoudre avec un algorithme ecace (il en sera dit plus à ce propos au chapitre 14). Devant cette diculté, les chercheurs tentent depuis plusieurs dizaines d'années de résoudre ce problème dans des cas particuliers. Ce chapitre présente quelques-uns de ces résultats, devenus des classiques, qui sont des conditions susantes grâce auxquelles un graphe contient nécessairement un cycle hamiltonien. ILLUST RAT ION
Avant de décrire ces conditions, étudions quelques exemples. Examinons le graphe de la gure 9.1 (a). Il contient bien (au moins) un cycle hamiltonien qui est représenté à la gure 9.1 (b) en pointillés. C'est bien un cycle qui contient les six sommets du graphe.
Graphes et algorithmes
74
Figure 9.1: (a) Un graphe. (b) Un cycle hamiltonien de ce graphe
Graphix
Examinons le graphe de la gure 9.2. Si part d'un sommet de la partie de gauche du graphe (par exemple de 2), il doit passer une fois par chaque sommet, donc passer par la partie de droite (il faut qu'il passe à un moment ou un autre par le sommet 4 par exemple). Pour cela, il doit nécessairement passer par le sommet 1. Ensuite, pour revenir à son sommet de départ, il faut qu'il passe à nouveau par le sommet 1, ce qui n'est pas autorisé dans la dénition : un cycle hamiltonien doit contenir les n sommets du graphe et n arêtes, ni plus ni moins. Ici, c'est impossible.
Figure 9.2: Un graphe qui ne contient pas de cycle hamiltonien
Certains graphes contiennent beaucoup de cycles hamiltoniens.
Si certains graphes ne contiennent aucun cycle hamiltonien, d'autres, au contraire, en contiennent beaucoup. C'est le cas du graphe complet (dans lequel il y a une arête entre chaque paire de sommets). La gure 9.3 représente un tel graphe complet à dix sommets dans lequel ont été mis en relief deux cycles hamiltoniens en pointillés. Ce ne sont pas les seuls.
9. Passons une seule fois par chaque sommet
75
Figure 9.3: Un graphe complet. Il contient de très nombreux cycles hamiltoniens (ici seuls deux sont montrés en pointillés)
Quels graphes ont un cycle hamiltonien ?
Comme dit en introduction du chapitre, il est dicile de répondre à cette question. Elle est posée depuis de nombreuses années, il y a eu de nombreux travaux de recherche dans ce domaine. L'objet principal de ce chapitre est de décrire un de ses résultats. Il a été publié à la toute n des années cinquante par Oystrein Ore qui était à l'époque à l'université Yale. Le résultat (son énoncé et la preuve) tient sur une seule page, ce qui est très court pour un article de recherche (mais il s'adresse à des spécialistes des graphes). Nous allons explorer ce joli résultat qui est devenu un classique .
Les graphes de Ore.
Le résultat que nous allons énoncer plus loin dit que si un graphe a une certaine propriété, alors il contient nécessairement un cycle hamiltonien. Pour exprimer cette propriété, nous avons besoin de la notion de degré d'un sommet u dans G, noté degG (u), qui, rappelons-le, est tout simplement le nombre de voisins de u dans G. Décrivons cette propriété. En hommage à celui qui a prouvé ce résultat, O. Ore, nous dirons qu'un graphe G = (V, E) à n sommets est un graphe de Ore si pour toute paire de sommets u et v entre lesquels il n'y a pas d'arête (u, v ∈ E ) :
degG (u) + degG (v) ≥ n. Un graphe de Ore à n sommets a donc la propriété suivante pour toute paire u et v de sommets : soit il y a une arête entre u et v dans G, soit ce n'est pas le cas et alors degG (u) + degG (v) ≥ n.
Graphes et algorithmes
76
ILLUST RAT ION Un graphe complet à n sommets est un graphe de Ore car la propriété est vraie (il y a une arête entre chaque paire de sommets). Considérons un graphe complet à n ≥ 4 sommets dans lequel on supprime une seule arête entre les sommets a et b. Pour montrer que ce graphe G très particulier est un graphe de Ore, il sut de démontrer que degG (a) + degG (b) ≥ n. Or, dans un graphe complet à n sommets, chaque sommet a un degré de n − 1 (car il a exactement n − 1 voisins). Comme l'arête entre a et b a été supprimée, ces deux derniers ont un degré diminué d'une unité, c'est-à-dire degG (a) = degG (b) = n − 2. Ainsi, degG (a) + degG (b) = 2n − 4 ≥ n (car n ≥ 4). Voici un autre exemple de graphe de Ore à la gure 9.4.
Figure 9.4: Un graphe de Ore Ce graphe est composé d'une partie complète entre les sommets 1, 2, 3, 4, 5, 6, 7 et de deux autres sommets a et b de degré 3 chacun. C'est un graphe à n = 9 sommets. Il sut de vérier que pour toute paire de sommets non reliés par une arête, la somme de leurs degrés est d'au moins 9. C'est un peu fastidieux mais faisable. Par exemple, degG (a) + degG (2) = 3 + 6 = 9, degG (a) + degG (1) = 3 + 7 = 10 ≥ 9, etc.
Tout graphe de Ore contient un cycle hamiltonien. Le résultat attendu est que tout graphe de Ore contient un cycle hamiltonien. Une idée de la preuve de cette démonstration est donnée dans une zone colorée, un peu plus loin. Par exemple, le graphe de la gure 9.4 contient le cycle 1, 2, 3, 4, 5, a, 6, 7, b, 1 (rappel : le dernier 1 répété indique qu'en partant de 1, à la n on y retourne ici via l'arête b, 1).
9. Passons une seule fois par chaque sommet
77
Les graphes de Ore sont des graphes de Dirac. Un graphe G à n sommets est un graphe de Dirac si chaque sommet u de G a un degré d'au moins n2 : degG (u) ≥
n . 2
Remarquons que tout graphe de Dirac est un graphe de Ore car comme degG (u) ≥ n2 et degG (v) ≥ n2 , on a nécessairement degG (u) + degG (v) ≥ n (qu'il y ait ou non une arête entre u et v , c'est donc vrai aussi quand il n'y en a pas). En revanche, la réciproque est fausse : le graphe de la gure 9.4 est un graphe de Ore (vu plus haut) mais n'est pas un graphe de Dirac car degG (a) = 3 < 92 . Quelques années avant O. Ore, M. Dirac avait démontré que tout graphe de Dirac a un cycle hamiltonien. Le résultat de Ore est donc plus fort que le résultat de Dirac dans la mesure où il arrive à la même conclusion sur une famille plus étendue de graphes. Dit autrement, le résultat de Dirac n'est qu'une simple conséquence (on dit que c'est un corollaire) du résultat de Ore.
Zone orange Montrons le résultat de Ore.
Considérons un graphe de Ore G à n sommets. Un arrangement circulaire L des n sommets de G est une liste de ces n sommets, considérée de manière circulaire. Notons-le L = u0 , . . . , un−1 . L induit un cycle hamiltonien si pour tout i, 0 ≤ i ≤ n−1, ui , ui+1 mod n ∈ E , c'est-à-dire que deux sommets consécutifs (circulairement) dans L sont voisins dans G. Si L induit un cycle hamiltonien, alors G a bien un cycle hamiltonien. Sinon, il y a au moins un indice i tel que ui , ui+1 mod n ∈ E . Si c'est le cas, on dira que c'est un trou (pas d'arête) dans L.
ILLUST RAT ION Pour illustrer, la gure 9.5 présente un arrangement circulaire du graphe de Ore de la gure 9.4. Ici les sommets sont placés sur un cercle qui représente cet arrangement. On voit qu'il y a trois trous : un entre 3 et a, l'autre entre a et 7, et le dernier entre b et 6.
Graphes et algorithmes
78
Figure 9.5: Un arrangement circulaire, contenant trois trous, du graphe de Ore de la gure 9.4
S'il y a un trou, alors il y a deux arêtes croisées . Considérons un trou, noté ui ui+1 (on n'indique plus ici le modulo pour ne pas alourdir les notations) de L. La propriété suivante est importante. Il existe uk et uk+1 deux sommets consécutifs de l'arrangement L tels que ui est voisin de uk et ui+1 est voisin de uk+1 . Zone rouge Montrons la propriété. Supposons qu'elle est fausse. Dans ce cas, dans l'arrangement circulaire L, aucun des degG (ui ) voisins de ui dans G ne peut être suivi d'un voisin de ui+1 . Ainsi, les degG (ui ) successeurs dans L des degG (ui ) voisins de ui sont des non-voisins de ui+1 . Il y a donc au moins degG (ui ) non-voisins de ui+1 . Où sont les degG (ui+1 ) voisins de ui+1 ? Parmi les n sommets, ce n'est ni ui+1 lui-même ni les (au moins) degG (ui ) successeurs des voisins de ui . Ainsi, degG (ui+1 ) ≤ n − 1 − degG (ui ) dont on tire degG (ui+1 ) + degG (ui ) ≤ n − 1, ce qui est en contradiction avec l'hypothèse selon laquelle G est un graphe de Ore.
Zone orange ILLUST RAT ION Illustrons cela sur les trois trous de la gure 9.5. L'ordre circulaire est lu dans le sens des aiguilles d'une montre.
9. Passons une seule fois par chaque sommet
79
Pour le trou entre ui = 3 et ui+1 = a, on peut prendre uk = 7 (voisin de 3 dans G) et uk+1 = b (voisin de a dans G et qui suit immédiatement 7 dans l'ordre circulaire) ; Pour le trou entre ui = a et ui+1 = 7, on peut prendre uk = 5 (voisin de a dans G) et uk+1 = 4 (voisin de 7 dans G et qui suit immédiatement 5 dans l'ordre circulaire) ; Pour le trou entre ui = b et ui+1 = 6, on peut prendre uk = 1 (voisin de b dans G) et uk+1 = 2 (voisin de 6 dans G et qui suit immédiatement 1 dans l'ordre circulaire).
S'il y a un trou, il peut être supprimé. Soit L un arrangement circulaire contenant un nombre minimal de trous. S'il en contient 0, alors c'est un cycle hamiltonien. Sinon, considérons un trou ui ui+1 . Grâce à la propriété vue plus haut, on sait qu'il existe uk et uk+1 deux sommets consécutifs de l'arrangement L tels que ui est voisin de uk et ui+1 est voisin de uk+1 . On peut alors considérer un autre arrangement circulaire : ui+1 , . . . , uk , ui , . . . , uk+1 , ui+1 . Cet arrangement circulaire contient un trou de moins que L (un exemple de cette manipulation est donné juste après). Ce qui est absurde par hypothèse sur L. Donc L ne contient aucun trou et est donc bien un cycle hamiltonien. Ce raisonnement indique que s'il n'y a pas de trou, alors il y a un cycle hamiltonien, sinon il sut de procéder comme indiqué juste avant pour diminuer d'une unité le nombre de trous. Illustrons cela. La gure 9.6 représente un arrangement circulaire, un trou entre b et 6, et les deux sommets 1 et 2 (voir les arêtes en pointillés).
Figure 9.6: Un trou entre 6 et b, et les arêtes en pointillés qui vont servir de pivot
80
Graphes et algorithmes
Figure 9.7: Résultat de l'exploitation des arêtes en pointillés pour supprimer le trou entre 6 et b par rotation de la partie basse de l'arrangement de la gure 9.6
Suite au chapitre 18. Ce chapitre a présenté des conditions susantes grâce auxquelles un graphe possède un cycle hamiltonien. Il en existe d'autres, un peu plus compliquées à décrire, comme la condition de Bondy-Chvatal (en 1972). On peut remarquer que la condition de Ore (ou celle de Dirac qui en est un simple corollaire) est vériée pour des graphes denses, c'est-à-dire qui ont beaucoup d'arêtes, ce qui n'est pas surprenant. Mais un graphe avec peu d'arêtes peut aussi être hamiltonien, par exemple s'il est réduit à un simple cycle contenant tous les sommets. Dans ce cas, il a n sommets et seulement n arêtes. D'un point de vue informatique, il serait intéressant d'avoir un algorithme qui prend en entrée un graphe G quelconque et détermine si G a ou n'a pas un cycle hamiltonien. Attention, on pose ici le problème dans sa généralité, pour un graphe quelconque, pas uniquement, comme vu dans ce chapitre, un graphe vériant les conditions de Ore (ou celles de Dirac). Ce problème général est très dicile, actuellement personne ne sait le résoudre avec un algorithme ecace. Vous en saurez un peu plus en lisant le chapitre 14. Ce problème et ses dérivés sont devenus des classiques de la théorie des graphes. Le chapitre 18 présentera une version dans laquelle le graphe (complet) a des arêtes qui ont chacune un coût. L'objectif est alors de trouver un cycle contenant tous les sommets, ayant un coût total le plus petit possible. Une application pratique est de planier les trajets d'un voyageur de commerce qui doit se rendre dans plusieurs villes en avion et doit minimiser le coût de ses déplacements. Ce problème est dicile mais nous montrerons comment trouver une tournée dont le coût total n'est pas trop éloigné de celui de la tournée optimale. Vous verrez que les méthodes proposées utilisent des algorithmes vus dans les premiers chapitres de ce livre. Pour nir, notons que le nom de ce problème vient du nom d'un mathématicien astronome, W. R. Hamilton (XIX e siècle), qui avait proposé un jeu mathématique
9. Passons une seule fois par chaque sommet
81
dont le but était de parcourir tous les sommets d'une classe particulière de graphes. Il est amusant de constater que deux grandes idées de théorie des graphes, parcours eulériens (vus au chapitre 8) et hamiltoniens, viennent de considérations ludiques. Les applications actuelles, quant à elles, n'ont qu'un rapport lointain avec ces motivations initiales. Les idées scientiques empruntent souvent des chemins détournés.
10
Travaillons ensemble
Imaginez une grande entreprise qui a
n
A1 , . . . , An , disagences a besoin d'un nouveau
agences, que l'on notera
séminées dans plusieurs régions. Chacune de ces
n
comptable. Le service central des ressources humaine (DRH) a recruté que nous nommerons les
candidats,
notés
C1 , . . . , Cn ,
n
personnes
ayant les compétences et les
qualications requises. Chaque candidat peut occuper le poste de comptable dans n'importe laquelle des il faut
aecter
n
agences. Maintenant que les recrutements ont été eectués,
un candidat à chaque agence.
Les 2n listes de préférences.
Voici ce que font chaque agence et chaque can-
didat, de manière totalement indépendante les uns des autres.
Ai établit la liste des n candidats, triés dans l'ordre de ses propres préférences. Nous noterons LAi la liste de Ai . Par exemple, l'agence A3 préfère en priorité avoir le candidat C7 (car il a une
Chaque agence
plus grande expérience dans tel sous-domaine de la comptabilité qui intéresse
A3 ).
C7 , elle aimerait avoir le C4 (car il a une qualication spéciale qui peut intéresser certains clients
Ensuite, si elle n'arrive pas à avoir le candidat
candidat
de l'agence), etc ;
Ci lui aussi établit une liste LCi des n agences dans l'ordre de ses préférences. Par exemple, le candidat C2 préfèrerait être aecté en priorité à l'agence A5 (pour des raisons géographiques) puis, si ce n'est pas le cas, voudrait être aecté à l'agence A1 (pour des raisons de potentiel
De manière indépendante, chaque candidat
d'évolution de carrière par rapport à ses qualications), etc. Dans chaque liste LAi de chaque agence Ai , les n candidats, doivent gurer chacun exactement une fois et dans chaque liste LCi de chaque candidat Ci , les n agences doivent gurer chacune exactement une fois.
Graphes et algorithmes
84
ILLUST RAT ION Avec n agences et n candidats, cela fait en tout 2n listes, chacune de longueur exactement n. Voici un exemple de listes, en supposant que n = 4.
LA1 = [3, 2, 1, 4], LA2 = [2, 4, 1, 3], LA3 = [2, 1, 4, 3], LA4 = [4, 3, 2, 1]. LC1 = [3, 1, 4, 2], LC2 = [4, 1, 2, 3], LC3 = [2, 4, 1, 3], LC4 = [2, 3, 4, 1]. La liste LA2 = [2, 4, 1, 3], par exemple, indique que l'agence A2 préfère en priorité le candidat C2 puis, si elle ne l'a pas, préfèrerait alors avoir le candidat C4 , etc. De même, la liste LC4 = [2, 3, 4, 1] indique que le candidat C4 préfère en priorité être aecté à l'agence A2 et, s'il n'a pas cette agence, préfèrerait être aecté à l'agence A3 , etc. Insistons sur le fait que chaque liste doit être de longueur exactement n et ne contenir que des éléments deux à deux diérents. Par exemple, si n = 5, les listes suivantes ne sont pas valides : LC4 = [1, 1, 3, 4, 2] est non valide car contient deux fois A1 , LC2 = [2, 3, 1] n'est pas valide car elle ne contient pas les n = 5 agences.
Une aectation.
La DRH centrale de l'entreprise a une copie de ces 2n listes et doit décider d'une aectation, c'est-à-dire doit aecter chaque candidat à une agence de manière à ce que chaque agence reçoive exactement un candidat. On se doute qu'il y a de nombreuses aectations possibles. Par exemple, la DRH pourrait décider d'aecter le candidat Ci à l'agence Ai . C'est une aectation possible, parmi de très nombreuses autres (si vous êtes à l'aise avec les dénombrements, essayez de les compter).
Un couple frustré dans une aectation.
La DRH a un rôle important : elle doit fournir un comptable à chaque agence et une agence à chaque comptable. Comme on l'a vu, il existe de nombreuses possibilités pour le faire. L'idéal bien sûr serait d'aecter chaque candidat à son premier choix et que chaque agence reçoive le candidat qui est en tête de sa liste. Il est facile de constater que ce n'est pas toujours possible. C'est évident sur l'exemple donné plus haut car A2 et A3 ont classé en tête de liste le même candidat C2 qui ne peut aller que dans une seule agence (qui ne sera d'ailleurs pas forcément A2 ou A3 ). Il faut donc un autre critère. Celui-là va être d'éviter la frustration, notion que nous allons expliquer maintenant. Considérons une aectation proposée par la DRH et imaginons que dans cette aectation, le candidat Ca soit aecté à l'agence Ab et que le candidat Cc soit aecté à l'agence Ad . Imaginons aussi que la liste de Ca soit LCa = [. . . , Ad , . . . , Ab , . . . ] et que la liste de Ad soit LAd = [. . . , Ca , . . . , Cc , . . . ]. Qu'est-ce que cela veut dire ? Ca est aecté à Ab , pourtant d'après sa liste de préférence, il préfère Ad (en tout cas, il préfère Ad à Ab ) , dans le même temps, l'agence Ad a le candidat Cc alors que d'après sa liste de préférence elle préfère Ca (à Cc ). On dira alors que le couple (Ad , Ca ) est frustré (dans la terminologie le candidat d'origine, on dit qu'il est instable ) car chacun des deux (l'agence Ad Ca ) auraient pu avoir mieux que ce qu'ils ont eu dans cette aectation.
et
et
10. Travaillons ensemble
85
ILLUST RAT ION Il est probable qu'un exemple vous aidera à y voir plus clair. Reprenons les listes de l'illustration précédente, que l'on rappelle :
LA1 = [3, 2, 1, 4], LA2 = [2, 4, 1, 3], LA3 = [2, 1, 4, 3], LA4 = [4, 3, 2, 1] ; LC1 = [3, 1, 4, 2], LC2 = [4, 1, 2, 3], LC3 = [2, 4, 1, 3], LC4 = [2, 3, 4, 1]. La gure 10.1 représente ces données : chaque sommet représente une agence ou un candidat. La liste à côté de chaque sommet est sa liste de préférence. Les quatre arêtes représentent ici une aectation particulière (dans laquelle, par exemple, C3 est aecté à A2 ) que nous allons étudier pour illustrer la notion de frustration dénie plus haut.
Figure 10.1: Un exemple pour n = 4 et une aectation Dans cette aectation, il y a (au moins) un couple frustré. Il s'agit du couple (A1 , C2 ) car : 1. ils n'ont pas été aectés l'un à l'autre, 2. et A1 aurait préféré C2 au candidat (C1 ) qui lui a été aecté, 3. et C2 aurait préféré A1 à l'agence (A3 ) à laquelle il a été aecté. Pour mieux voir les choses, la gure 10.2 représente la portion de l'aectation de la gure 10.1 concernée (les autres données ont été supprimées car elles n'entrent pas en compte dans cette frustration). Les deux éléments frustrés ont été coloriés, reliés en pointillés ns. Ce couple se sent frustré car pour ces deux éléments spéciquement, une autre aectation (aectant C2 à A1 ) aurait été strictement meilleure que ce qu'ils ont eu dans l'aectation de la gure 10.1.
86
Graphes et algorithmes
Figure 10.2: Focus sur le couple frustré (A1 , C2 ) dans l'aectation de la gure 10.1 Notons que (A2 , C2 ) est aussi un autre couple frustré dans cette aectation. Cela montre qu'une aectation peut éventuellement générer plusieurs couples frustrés. D'ailleurs, il y en a encore au moins un autre ; je vous invite à le chercher. Terminons par un exemple de couple non frustré, toujours pour l'aectation de la gure 10.1 : (A1 , C3 ). On a bien : A1 et C3 ne sont pas aectés l'un à l'autre, A1 préfèrerait C3 (classé premier) au candidat ( C1 ) auquel il est aecté (C1 est troisième dans sa liste). En revanche, le candidat C3 est aecté à son premier choix A2 et n'a pas envie de le quitter pour aller à A1 , qui est seulement troisième dans sa liste de choix. L'agence A1 préfèrerait donc C3 mais C3 ne préfère pas A1 .
Peut-on toujours éviter qu'il y ait des couples frustrés ? Étant donné 2n listes de préférences, est-ce qu'il existe toujours au moins une aectation n'ayant aucun couple frustré ? Ou, au contraire, est-ce que dans certains cas, éventuellement très spéciaux, toute aectation engendre nécessairement (au moins) un couple frustré ? Essayez d'y rééchir quelques instants car on arrive au c÷ur du débat ici. Beaucoup de gens pensent spontanément qu'il y a des cas où ça coince, quoi que l'on fasse . Pour certains, cette réponse est due à une mauvaise compréhension de ce que l'on cherche. On ne cherche pas à aecter au mieux (ce qu'il faudrait dénir précisément) les candidats, on cherche plus simplement une aectation dans laquelle il n'y a pas de couple frustré. Après un peu plus de réexion, certaines personnes pensent que si toutes les listes sont les mêmes (ce qui n'est absolument pas interdit), ça va coincer , il y aura forcément des couples frustrés, quelle que soit l'aectation. Examinons une telle situation. Prenons n = 4 et prenons par exemple les données suivantes : LA1 = [1, 2, 3, 4], LA2 = [1, 2, 3, 4], LA3 = [1, 2, 3, 4], LA4 = [1, 2, 3, 4] (les listes sont identiques). Toutes les agences veulent donc en priorité le candidat C1 . Une seule l'aura. Mais attention, ce n'est pas pour cela que les autres agences seront frustrées, avec la dénition donnée plus haut de la frustration. Prenons LC1 = [3, 1, 4, 2], LC2 = [4, 1, 2, 3], LC3 = [2, 4, 1, 3], LC4 = [2, 3, 4, 1]. Est-ce qu'il existe une aectation ne produisant aucun couple frustré ? La gure 10.3 représente ces données ainsi qu'une aectation (les arêtes). Est-ce que cette aectation particulière génère un couple frustré ? Remarquons que C1 , C2 et C3 ont tous leur premier choix. Ils ne peuvent donc pas faire partie d'un couple frustré. Reste C4 qui est aecté à l'agence A1 classée en dernière position dans sa liste. S'il fait partie d'un couple frustré, c'est donc avec A2 , A3
10. Travaillons ensemble
87
ou A4 (ceux qui sont devant C1 dans sa liste). Or, dans la liste de ces trois agences, C4 est classé en dernier. Les agences A2 , A3 et A4 préfèrent le candidat auquel elles sont aectées que C4 , ce qui n'engendre aucune frustration. Conclusion : même pour ces données particulières, il existe une aectation sans frustration. Mais cela ne prouve pas que c'est toujours possible.
Figure 10.3: Une aectation sans couple frustré
Un algorithme pour construire une aectation sans couple frustré.
Cela va peut-être vous surprendre, mais il existe toujours une aectation sans couple frustré. De plus, on peut en construire une avec un algorithme que nous allons présenter maintenant. C'est la DRH centrale de l'entreprise qui va dérouler ces opérations pour trouver une aectation. Lorsqu'elle aura ni, elle pourra rédiger les lettres d'aectation et les candidats seront eectivement envoyés dans les agences. La DRH a la liste des préférences de chaque agence et de chaque candidat. Elle en fait des copies sur lesquelles elle va travailler. Elle démarre avec une aectation courante ne contenant initialement aucune liaison (ou aucun lien ). À chaque étape, elle va soit ajouter une nouvelle liaison (entre une agence et un candidat), soit en modier une. Le c÷ur de l'algorithme est le suivant. Tant qu'au moins une agence n'a aucune liaison, faire les opérations suivantes. Choisir une agence, notons-la Ai , qui n'a aucune liaison (s'il y en a plusieurs, en choisir une quelconque). Soit Cj le candidat en tête de la liste de l'agence Ai . Eacer Cj de la liste de Ai . Si Cj n'a aucune liaison alors le lier à Ai .
Sinon :
notons Ak l'agence avec laquelle Cj a une liaison ; si le candidat Cj préfère Ai à l'agence Ak avec laquelle il a une liaison dans l'aectation courante (donc si LCj = [. . . , Ai , . . . , Ak . . . ]) alors changer cette liaison pour le lier à Ai (après cette opération, l'agence Ak se retrouve sans liaison) ;
88
Graphes et algorithmes N.B. : si Cj préfère Ak à l'agence Ai alors ne rien faire, ne pas changer la liaison entre Ak et Cj . Dans ce cas, Ai n'a toujours pas de liaison.
Lorsque tout cela est terminé, chaque liaison devient une aectation dénitive. Une illustration complète va reprendre toutes ces règles.
ILLUST RAT ION Illustrons cela avec les données représentées sur les gures suivantes. Rappelez-vous que l'algorithme n'impose pas l'ordre dans lequel les agences non encore liées sont examinées.
10. Travaillons ensemble
89
Le résultat nal (l'aectation nale) est représentée, avec les listes de départ pour rappel à la gure 10.4. Vous pouvez vérier que cette aectation est sans couple frustré.
Graphes et algorithmes
90
Figure 10.4: Aectation nale produite par l'algorithme Si vous reprenez ces données et que vous appliquez la méthode en faisant d'autres choix (parmi ceux permis par l'algorithme bien sûr), vous aboutirez peut-être à une autre solution. Toutes les solutions produites sont sans couple frustré. Cela peut se démontrer.
Changeons les rôles des candidats et des agences dans la méthode.
Dans la version de l'algorithme décrite plus haut, les agences mènent la danse . C'està-dire que ce sont les agences qui sont prioritaires. Par exemple, si à une étape Ai a en tête de sa liste le candidat Cj qui n'a pas de liaison, alors l'algorithme impose la nouvelle liaison entre Cj et Ai . Les préférences du candidat Cj dans ce cas ne sont pas prises en compte du tout. Cela a tendance à avantager globalement les agences par rapport aux candidats. Si vous voulez plutôt donner un avantage aux candidats, vous pouvez appliquer la même méthode en changeant les rôles entre candidats et agences.
Conclusion.
Ce chapitre a été l'occasion de découvrir un problème d'aectation avec prise en compte des préférences. Il n'était pas forcément évident de deviner que toute instance avait au moins une aectation sans couple frustré et encore moins de deviner un algorithme pour en trouver une. Ce travail a été publié en 1962 par David Gale et Lloyd Shapley. Depuis, des variantes plus ou moins faciles à résoudre ont été étudiées. Lloyd Shapley a obtenu le prix Nobel d'économie en 2012 pour ses travaux dans le domaine des jeux collaboratifs dont ce résultat est une contribution. David Gale a lui aussi reçu un prix prestigieux, mais pas le Nobel car il est décédé en 2008.
11
Les ots : un problème de plomberie informatique
Ce chapitre décrit un algorithme, très célèbre dans le domaine de l'optimisation, qui a été mis au point dans les années cinquante et a servi de base à de nombreuses variantes, généralisations et améliorations. Nous aurons besoin pour cela de dénir ce qu'est un réseau à ots, qui peut être vu comme une sorte de plan de canalisations. Puis nous xerons des règles pour faire passer un liquide abstrait, appelé ot, dans ces tuyaux . L'objectif principal sera d'en faire transiter la quantité maximale depuis un sommet source vers un sommet puits. Cette technique (celle présentée ici et d'autres, très nombreuses, non décrites dans ce livre) peut donc concrètement servir à planier/optimiser des mouvements de vrais uides ou de marchandises dans un réseau de distribution. Le domaine de la plomberie évoqué dans le titre fait bien sûr référence à toute cette tuyauterie , mais aussi au fait que l'algorithme présenté est une sorte de clef à molette, c'est-à-dire un outil adaptable pour résoudre des problèmes de diverses natures. Cette adaptabilité est assez surprenante et pas si simple à voir a priori. Nous en donnons un exemple à la n de ce chapitre. Pour l'instant, nous allons présenter cet outil pas à pas.
Un réseau : un graphe orienté, des capacités sur les arcs, une source s et un puits t. Nous allons manipuler des réseaux (à ots) ici. Un tel
réseau est en réalité un graphe G = (V, E) orienté, c'est-à-dire un graphe ayant un ensemble V de sommets et un ensemble d'arcs. Un arc est une relation orientée. On parle donc d'un arc qui va du sommet u vers le sommet v et on le note (u, v) (alors qu'une arête u, v est une relation entre u et v, sans ordre relatif entre ces deux sommets). Graphiquement, un arc (u, v) est représenté par un èche qui part de u et pointe sur v.
Figure 11.1: Un arc, orienté du sommet u vers le sommet v
Graphes et algorithmes
92
Ici, on peut imaginer qu'un arc (u, v) est un tuyau qui peut faire passer du liquide, un ot, de u vers v (et pas dans le sens inverse). Chaque arc (u, v) du réseau a une capacité notée c(u, v) > 0 qui peut être vue comme le diamètre du tuyau. Plus cette capacité est grande, plus une quantité importante de ot va pouvoir passer dans le tuyau. Dans ce réseau, deux sommets jouent un rôle particulier : la source, notée s, et le puits, noté t. Pour simplier la présentation, on va supposer ici que s n'a pas d'arc entrant (du type (u, s)) et t n'a pas d'arc sortant (du type (t, v)). La gure 11.2 donne un exemple de réseau. La capacité de chaque arc est inscrite sur l'arc dans un rectangle.
Figure 11.2: Un réseau : une
source s,
un puits t, des arcs avec des
capacités
Flot, quantité de ot sur un arc, contraintes de capacité et de conservation. Maintenant que le plan de la tuyauterie est connu, du liquide va pouvoir
couler à l'intérieur. L'objectif est d'en faire passer la plus grande quantité depuis la source s jusqu'au puits t, en passant par les sommets et arcs intermédiaires. On notera x(u, v) la quantité de ot que l'on décide de faire circuler sur l'arc (u, v) de u vers v . Mais attention, on ne peut pas faire n'importe quoi. La quantité x(u, v) de ot qui passe de u vers v sur (u, v) ne doit jamais être supérieure à la capacité c(u, v) de cet arc. Il est possible de ne rien faire passer sur cet arc, c'est-à-dire x(u, v) = 0. Ainsi, en chaque arc (u, v) il faut que :
0 ≤ x(u, v) ≤ c(u, v). C'est ce qui est appelé la contrainte de capacité : on ne peut pas faire passer plus de ot que ce que permet la capacité de l'arc (sinon le tuyau explose). L'autre contrainte à prendre en compte s'appelle la contrainte de conservation et s'exprime comme suit. En tout sommet u, qui n'est ni s ni t, la quantité totale de ot qui entre en u sur tous ses arcs doit être égale à la quantité totale de ot qui sort de u. Un sommet u se contente juste de faire passer , de router , le ot qui lui arrive. Il n'en produit pas, il n'en retient pas.
ILLUST RAT ION Une illustration de ces contraintes est donnée à la gure 11.3 qui représente un sommet u ayant quatre arcs entrants (a, u), (b, u), (c, u), (d, u) et trois arcs sortants (u, e), (u, f ), (u, g). La capacité de chacun est indiquée sur le dessin. Attention, cela ne représente pas un réseau entier, c'est un simple focus sur un sommet u pour illustrer cette notion de conservation. Sur chaque arc, est indiquée dans un rectangle coloré la
11. Les ots : un problème de plomberie informatique
93
quantité de ot qui le traverse. On constate (et c'est obligatoire) que cette valeur est toujours inférieure ou égale à la capacité de chaque arc. Ici rien ne circule sur (c, u), alors que (b, u), (u, e) et (u, f ) sont au contraire saturés. La quantité totale de ot qui entre en u est de 9 + 3 + 0 + 1 = 13. Par la contrainte de conservation, cette quantité doit sortir de u. C'est précisément ce qui se passe ici. Une quantité totale de 3 + 6 + 4 = 13 sort bien de u.
Figure 11.3: Conservation : en u rien ne se crée, rien ne se perd, tout se transporte
La valeur V d'un ot x. Nous avons donné les contraintes sur les quantités de ot qui peuvent circuler sur les arcs et à travers les sommets à l'intérieur du réseau. Examinons maintenant s et t. Imaginons que l'on fasse sortir une certaine quantité de ot sur les arcs sortants de s (la source produit du ot). À cause de la contrainte de conservation, cette quantité de ot doit progresser de sommet en sommet sans aucune fuite . Le seul sommet autorisé à retenir ce qu'on lui envoie est le puits t (le puits consomme du ot). Ainsi, si V est la quantité de ot qui sort de s, cela sera aussi la quantité de ot qui va arriver en t ; V est nommée la valeur du ot. Flot nul. Si la quantité de ot qui circule sur chaque arc est de 0, alors c'est un ot nul. Il est simple de voir qu'il vérie toutes les contraintes de capacité et de conservation. La valeur d'un tel ot est évidemment 0. L'objectif qui va nous préoccuper sera de construire un ot de valeur maximale dans un réseau. En pratique, nous partirons d'un ot nul puis nous l'améliorerons étape par étape. Mais avant cela, illustrons ces notions de ot, de valeur d'un ot, etc. ILLUST RAT ION La gure suivante représente un réseau, avec sa source s, son puits t et des arcs, chacun avec sa capacité (valeur dans le cadre sur l'arc). Une quantité de ot x(u, v) circulant sur chaque arc (u, v) est représentée par la valeur colorée sur l'arc.
Graphes et algorithmes
94 Est-ce que x est bien un ot ? Que faut-il vérier ?
Que la contrainte de capacité est bien satisfaite, c'est-à-dire que sur chaque arc, la qualité de ot qui y circule ne dépasse pas la capacité de l'arc (la valeur dans le cadre). C'est bien le cas ici. Que la contrainte de conservation est aussi bien vériée, c'est-à-dire que la quantité totale de ot qui entre dans un sommet u (diérent de s et de t) doit être égale à la quantité de ot totale qui en sort. Là aussi, on constate que c'est le cas. Par exemple, au sommet 2 il rentre une quantité de 5 (via (s, 2)) et il sort 3 + 2 = 5 (sur deux arcs).
x est bien un ot. Quelle est sa valeur V ? C'est, par dénition, la quantité totale de ot qui sort de s ou qui entre en t. On peut constater sur cet exemple que c'est bien la même quantité, c'est-à-dire ici 1 + 5 + 1 = 4 + 3 = 7. Examinons maintenant la gure suivante qui représente un autre ot dans le même réseau (vériez que c'est bien un ot). La valeur de ce ot est ici 6 + 2 + 2 = 10, c'està-dire une valeur supérieure à celle du ot précédent qui était 7. Tous les ots n'ont donc bien sûr pas nécessairement la même valeur.
Figure 11.4: Un ot de valeur 10
Les goulots d'étranglement du réseau Est-ce que vous pensez que le ot de la gure 11.4 a une valeur maximale ? Pourrait-on construire un ot avec une valeur encore plus grande ? Les arcs qui sortent de s ne sont pas totalement saturés et ceux qui entrent en t non plus, il y a peut-être encore du potentiel d'amélioration. Mais les contraintes de conservation et de capacité interdisent de faire n'importe quoi. Dans un réseau, il y a des goulots d'étranglement. Ce sont des ensembles d'arcs qui font que la valeur d'un ot sera limitée (même si la capacité totale des arcs de s et de t est très grande). Un goulot d'étranglement est appelé une s, t-coupe. ILLUST RAT ION Avant de donner une dénition plus précise, présentons une illustration volontairement exagérée dans laquelle un goulot très étroit va clairement apparaître. Examinons le réseau de la gure 11.5. La source s a des arcs sortants dont la capacité totale est de 4 000 et le puits t a des arcs dont la capacité entrante totale est de 3 000. Tous les arcs ont une capacité de 1 000 (ce sont de gros tuyaux), sauf (5, 6) qui n'a qu'une capacité
11. Les ots : un problème de plomberie informatique
95
de 1. Or cet arc est incontournable pour acheminer du ot de s vers t. Tout devra passer obligatoirement par cet arc, qui est ici clairement un goulot d'étranglement, un entonnoir . N'importe quel ot dans ce réseau a donc une valeur d'au plus 1.
Figure 11.5: Un goulot d'étranglement (arc (5, 6)) : un ot ne peut pas avoir une valeur strictement supérieure à 1
Capacité d'une
-coupe.
s, t Dénissons cette notion de manière plus précise. Pour cela, partageons l'ensemble des sommets en deux parties disjointes, S et T . Dans l'ensemble S est obligatoirement placé s et dans T se trouve obligatoirement t. Chaque autre sommet u est dans S ou dans T (mais dans un seul de ces deux ensembles). Ces deux ensembles (S, T ) dénissent ce qui s'appelle une s, t-coupe. Les arcs de la s, t-coupe (S, T ) sont tous les arcs (u, v) avec u dans S et v dans T . Ces arcs séparent s de t. Tout le ot allant de s vers t doit passer par ces arcs. C'est donc un goulot d'étranglement qui va limiter la quantité totale de ot pouvant passer de s vers t, c'est-à-dire la valeur d'un ot. L'étroitesse de ce goulot (S, T ) est quantiée par la somme des capacités de ses arcs. C'est la capacité, notée c(S, T ), de la s, t-coupe (S, T ). ILLUST RAT ION Illustrons tout cela. Reprenons le réseau de la gure 11.5. Voici quelques exemples de s, t-coupes dans ce réseau, avec leur capacité. S = {s, 1, 2, 3, 4} et T = {5, 6, 7, 8, 9, t}. Les arcs de cette s, t-coupe sont (1, 5), (2, 5), (3, 5), (4, 5). La somme des capacités de ces arcs est c(S, T ) = 4 000. S = {s, 1, 2, 3}, T = {4, 5, 6, 7, 8, 9, t}. Les arcs de cette s, t-coupe sont (s, 4), (1, 5), (2, 5), (3, 5). La somme des capacités de ces arcs est ici aussi c(S, T ) = 4 000. S = {s, 1, 2, 3, 4, 5, 6, 7, 8, 9} et T = {t}. Les arcs de cette s, t-coupe sont (7, t), (8, t), (9, t). Ici c(S, T ) = 3 000. S = {s, 1, 2, 3, 4, 5} et T = {6, 7, 8, 9, t}. Le seul arc de cette coupe est (5, 6) qui a une capacité de 1 et ainsi c(S, T ) = 1. Dans ce réseau, c'est la s, t-coupe qui a une capacité minimale. C'est le goulot le plus étroit.
Graphes et algorithmes
96
Quelle est la relation entre la valeur d'un ot et la capacité d'une -coupe ?
s, t Considérons un réseau et une s, t-coupe (S, T ) quelconque de ce réseau. (S, T ) étant un goulot d'étroitesse c(S, T ), la valeur V de n'importe quel ot x de ce réseau ne pourra pas être supérieure à c(S, T ). On a donc : V ≤ c(S, T ). Ainsi, pour montrer qu'un ot a une valeur maximale, il sut de trouver une s, t-coupe dont la capacité est égale à cette valeur. Reprenons le ot x de la gure 11.4 qui, on le rappelle, a une valeur V = 10. Comme on n'arrive pas à trouver un ot ayant une plus grande valeur, cherchons une s, t-coupe de capacité 10. Étudions la suivante : S = {s, 1, 2, 3, 5} et T = {4, t} représentée à la gure 11.6. Les arcs de (S, T ) sont (3, t), (2, 4) et (5, 4). La somme des capacités de ces arcs (valeurs encadrées) est : 5 + 3 + 2 = 10. C'est bien une coupe de capacité 10 qui limite tout ot à une valeur d'au plus 10. Comme le ot atteint exactement cette valeur 10, il est maximal. Impossible d'avoir mieux.
Figure 11.6: La s,t -coupe (S,T ) de capacité c (S,T )=10
Construisons un ot de valeur maximale : les chaînes améliorantes.
Cette partie est consacrée à la description d'un algorithme permettant de construire un ot de valeur maximale. L'idée générale est la suivante. Partir d'un ot initial nul (quantité de ot de 0 sur tous les arcs), de valeur 0. Tant que c'est possible, améliorer à chaque étape le ot courant grâce à une chaîne améliorante qui est une suite d'arcs qui relient s à t (chaîne), sur laquelle il est possible de faire passer plus de ot , permettant ainsi d'augmenter la quantité de ot allant de s vers t (c'est pour cela qu'elle est dite améliorante ). L'algorithme ne dit pas quelle chaîne choisir. On peut en prendre une quelconque à partir du moment où elle permet d'améliorer le ot. Au lieu de donner une dénition formelle d'une chaîne améliorante, décrivons un exemple qui va vous en montrer.
ILLUST RAT ION Pour cela, nous étudierons le réseau de la gure 11.7 dans lequel un ot nul noté x0 a été placé.
11. Les ots : un problème de plomberie informatique
97
Figure 11.7: Un ot initial nul (valeur 0)
Utilisons la chaîne s → 1 → 3 → t. La capacité de ces trois arcs est 2, 1 et 3 respectivement et aucun ot n'y circule. Ce long tuyau reliant s à t peut être utilisé pour faire passer du ot. Comment peut-on l'utiliser au mieux ? Il est tentant d'envoyer dessus une quantité de ot égale au maximum des possibilités/capacités, c'est-à-dire 3. Mais en faisant cela, la contrainte de capacité de l'arc (s, 1) est violée, le tuyau explose. Il faut donc prendre le pour préserver cette contrainte. Faisons-le, ajoutons une quantité de ot de 1 sur chacun de ces arcs. Le résultat, le nouveau ot noté x1 , est représenté à la gure 11.8. Sa valeur est 1. Notez (et vériez-le) que x1 est bien un ot.
minimum
Figure 11.8: Un ot de valeur 1
Continuons à améliorer le ot grâce, par exemple, à la chaîne s → 1 → 2 → 4 → t. Quel est le potentiel d'amélioration grâce à cette chaîne ? Sur (s, 1), l'augmentation possible n'est que d'une unité (passer de 1 à 2 qui est égal à la capacité), sinon sa capacité est dépassée. Sur (1, 2) et (2, 4), la quantité peut passer de 0 à 1 (mais pas plus). L'augmentation sur (4, t) est d'au plus deux unités. Sur toute la chaîne, le maximum d'augmentation n'est que d'une unité. Appliquons cette augmentation le long de cette chaîne. Cela donne le nouveau ot noté x2 , de valeur 2, représenté sur la gure 11.9. Notez qu'il s'agit toujours bien d'un ot valide. En faisant ces modications, on garde bien un ot.
Graphes et algorithmes
98
Figure 11.9: Un ot de valeur 2 Arrivé à ce point, tout semble terminé. Impossible d'ajouter du ot sur (s, 1) qui est saturé. Si l'on utilise (s, 2) pour faire circuler une unité de ot par exemple, celle-là va arriver en 2 qui ne pourra plus la faire sortir sur son seul arc de sortie (2, 4) qui est saturé. Bref, il semble impossible de progresser. Voici la chaîne que nous allons prendre maintenant (notez bien le sens des èches) : s → 2←1 → 4 → t. Ce qui veut dire que nous allons utiliser (entre autres) l'arc (1, 2) mais en venant de 2, c'est-à-dire à l'envers. Comment est-ce possible ? Voici ce que nous allons faire. Observons les arcs de cette chaîne. Sur le premier arc (s, 2), il est possible d'augmenter d'au plus deux unités. L'arc (1, 2) est considéré ici à l'envers. Attention, nous n'allons pas envoyer du ot de 2 vers 1. C'est impossible, l'arc (2, 1) n'existe pas. En revanche, du ot va être retiré de (1, 2), diminuant la quantité de ot d'une unité au maximum. Nous expliquerons un peu plus loin la cohérence de tout cela. Mais en attendant, poursuivons l'examen de notre chaîne : sur les arcs (1, 4) et (4, t), une augmentation maximale de 1 est possible. Au total, en suivant cette chaîne, la capacité d'amélioration est de 1. Faisons-le. La règle est : si l'arc (u, v) est dans le sens de u vers v dans la chaîne, la quantité de ot est augmentée ; sinon, elle est diminuée. Augmentons d'une unité sur les arcs (s, 2), (1, 4), (4, t) et diminuons alors d'une unité sur l'arc (1, 2). La gure 11.10 donne les valeurs obtenues.
Figure 11.10: Un ot de valeur 3 Observez que ces valeurs forment toujours un ot. Vous constatez que la contrainte de capacité est toujours vériée (valeurs colorées au plus égales à celles encadrées). Maintenant regardez attentivement ce que nous avons fait autour des sommets 2 et 1. La quantité de ot qui passait sur cet arc a été diminuée d'une unité. Il rentre donc une unité de ot de moins en 2 et il en sort une unité de moins en 1 à cause de ce changement. Mais, cette unité de ot en moins a été compensée en 2 par la
11. Les ots : un problème de plomberie informatique
99
nouvelle unité de ot venant de s et en 1 par la nouvelle unité de ot qui sort vers 4. La contrainte de conservation est bien vériée à chaque sommet. C'est toujours un ot, dont la valeur a augmenté d'une unité.
Augmenter si et diminuer si .
→ ← Il est donc possible d'améliorer la valeur globale d'un ot en utilisant une chaîne améliorante avec la règle : si l'arc est pris dans le sens de la èche alors augmenter, sinon diminuer la quantité de ot qui y passe. La limite de l'augmentation ou de la diminution est dictée par la contrainte de capacité. Il faut toujours que : 0 ≤ x(u, v) ≤ c(u, v) en chaque arc (u, v).
Que faire lorsque l'on ne trouve plus de chaîne améliorante ? Trouver une s, t-coupe pour être sûr que l'on ne peut plus augmenter. Dans la
situation de la gure 11.10, il n'y a plus de chaîne améliorante. Le ot a une valeur V = 3. Pour montrer que ce ot est maximal, il sut de trouver une s, t-coupe de capacité 3. En voici une : S = {s, 1, 2}, T = {3, 4, t}. Les arcs de cette s, t-coupe sont : (1, 3), (1, 4), (2, 4). La somme des capacités de ces arcs (valeurs encadrées) est : C(S, T ) = 1 + 1 + 1 = 3 = V . La valeur du ot est maximale.
Flot max. = coupe min.
Nous avons évoqué plus haut le résultat : V ≤ c(S, T ), valable pour n'importe quelle s, t-coupe et n'importe quel ot de valeur V . C'est ce qui vient de nous aider à montrer que le ot de la gure 11.10 est maximal. À partir de ce résultat, il est possible de montrer que si x est un ot de valeur maximale Vmax , alors il existe une s, t-coupe (S, T ) de capacité minimale c(S, T ) telle que :
Vmax = c(S, T ). Si un ot a une valeur maximale, alors il y a une s, t-coupe qui permet de le montrer. Il existe un moyen automatique pour trouver une telle coupe mais cela nous entraînerait un peu loin.
Est-ce que le choix des chaînes améliorantes a un impact sur le résultat ?
Sur la valeur du ot nal non. En revanche, le choix des chaînes peut avoir un impact sur le nombre d'itérations à faire pour obtenir le résultat nal. Examinons le réseau à quatre sommets et cinq arcs de la gure 11.11. Chaque arc a une capacité de 1 000 sauf (2, 1) qui a une capacité de 1. L'algorithme précédent indique qu'il faut partir d'un ot initial nul puis l'améliorer. On peut donc choisir en premier la chaîne s → 1 → t qui permet d'améliorer la valeur du ot de 1 000 unités en une seule étape. Ensuite, ce ot peut encore être amélioré de 1 000 unités avec la chaîne s → 2 → t. On obtient donc un ot de valeur 2 000, ce qui est optimal car la s, t-coupe S = {s}, T = {1, 2, t} est de capacité 2 000. Avec ce déroulement (ce choix des chaînes), un ot maximal est obtenu en deux étapes seulement. Cependant l'algorithme, tel qu'il a été décrit, ne dit absolument pas comment choisir les chaînes. Un ÷il humain voit bien qu'il faut choisir ces deux-là. Mais un ordinateur qui déroule l'algorithme est aveugle à ce type d'intuition. Voici un autre déroulement possible des opérations avec le même algorithme.
Graphes et algorithmes
100
Figure 11.11: Un tout petit réseau dans lequel le nombre d'itérations peut être très important si les chaînes améliorantes sont mal choisies La chaîne 1 choisie est s → 2 → 1 → t. Amélioration d'une unité de la valeur du ot (x(2, 1) = 1). La chaîne 2 choisie est s → 1 ← 2 → t (x(2, 1) = 0 maintenant car l'arc (2, 1) est utilisé à l'envers dans la chaîne). La valeur du nouveau ot est 2. Ensuite, ces deux chaînes sont utilisées alternativement. Les étapes impaires sont s → 2 → 1 → t et les étapes paires s → 1 ← 2 → t. Avec ce choix de chaînes, le ot n'augmente que d'une seule unité à chaque étape. Il faut donc 2 000 étapes pour arriver au bout. Pour un tout petit réseau, cela fait vraiment beaucoup d'itérations dans le pire cas. Imaginez ce que cela donnerait avec des capacités de 10 000 000... Une façon de contourner ce problème est de choisir les chaînes les plus courtes à chaque étape.
Zone orange Une utilisation des ots.
Dans cette partie, nous allons évoquer une utilisation plutôt inattendue de cette technique de ots. Avant d'entrer dans les détails, nous avons besoin de savoir ce qu'est un couplage dans un graphe non orienté. La notion de couplage est utilisée dans d'autres chapitres mais si vous ne l'avez pas encore vue, en voici une présentation. Un couplage M d'un graphe non orienté G = (V, E) est un sous-ensemble d'arêtes de E deux à deux disjointes, c'est-à-dire n'ayant aucune extrémité, aucun sommet en commun. La taille, notée |M |, de ce couplage est le nombre d'arêtes qu'il contient.
ILLUST RAT ION Considérons le graphe G de la gure 11.12. Ce graphe contient plusieurs couplages. Par exemple celui, de taille 2, qui contient les arêtes 1, 3 et 2, 4. Un autre, composé de 1, 4 et 2, 3, est aussi de taille 2. En revanche, 1, 3, 3, 4 et 2, 4 n'est pas un couplage car certaines arêtes partagent des extrémités (le sommet 3 est l'extrémité de deux de ces arêtes), ce qui n'est pas autorisé par la dénition. Remarquons pour nir avec cet exemple que l'arête 3, 4 toute seule forme bien un couplage aussi, de taille 1. Ce qui
11. Les ots : un problème de plomberie informatique
101
montre au passage que certains graphes contiennent des couplages de tailles diérentes. On admettra aussi que l'ensemble ne contenant aucune arête est aussi un couplage (de n'importe quel graphe).
Figure 11.12: Un graphe pour illustrer la notion de couplage
Les graphes bipartis. Avant d'aller plus loin, nous avons encore besoin d'une notion, celle de graphe biparti, vue au chapitre 3. Voici quelques rappels. Un graphe G = (V, E) est dit biparti si : l'ensemble V des sommets de G peut être divisé en deux ensembles V1 et V2 disjoints (V1 ∩ V2 = ∅) et contenant tous les sommets de V ( V1 ∪ V2 = V ) ; chaque arête u, v de E a une extrémité dans V1 et l'autre dans V2 . Attention, V1 et V2 n'ont pas forcément la même taille.
ILLUST RAT ION Les graphes bipartis sont souvent représentés graphiquement en mettant tous les sommets de V1 à gauche et ceux de V2 à droite. Ainsi toutes les arêtes sont entre les deux. Voici trois exemples de graphes bipartis à la gure 11.13.
Figure 11.13: Quelques exemples de graphes bipartis
Graphes et algorithmes
102
Objectif : construire un couplage de taille maximale dans un graphe biparti. Nous allons décrire maintenant une technique pour
construire un couplage de taille maximale dans un graphe biparti G = (V, E) dont le découpage de l'ensemble des sommets est V1 et V2 . Cette technique utilise les ots. À ce point, vous vous posez sans doute la question : mais quel est le rapport entre un problème de graphe pur et dur (le couplage de taille maximale) d'un côté et des tuyaux qui transportent du ot d'un autre ? Comment résoudre le premier avec le second car its n'ont aucun lien à première vue ? À première vue seulement. C'est ce que nous allons voir. Pour utiliser un ot, il faut un réseau, des arcs, des capacités, une source, un puits, etc. Construisons-en un, nommons-le R = (VR , AR ), à partir de G = (V1 ∪ V2 , E) biparti. Les sommets du réseau R sont tous les sommets de G plus deux nouveaux sommets, la source s et le puits t. Les arcs de R sont les suivants. Créer un arc de la source s vers chaque sommet de V1 et un arc de chaque sommet de V2 vers le puits t. Chaque arête u, v de G avec u ∈ V1 et v ∈ V2 est transformée en un arc (u, v) du réseau. Les arcs pointent donc de V1 vers V2 dans R. La capacité de chaque arc du réseau R est 1.
ILLUST RAT ION La gure 11.14 illustre cette transformation d'un graphe G biparti en un réseau R associé.
Figure 11.14: Transformation d'un graphe biparti en un réseau
11. Les ots : un problème de plomberie informatique
103
Construire un ot de valeur maximale dans R et en tirer un couplage M pour G. Dans ce réseau R, il faut construire un ot de valeur
maximale, que l'on note x∗ . Notons V sa valeur. Dans ce ot, observons chaque unité de ot partant de s et allant vers t. Elle emprunte un chemin orienté direct de s vers t, du type s, u, v, t avec u ∈ V1 et v ∈ V2 . Aucun autre chemin n'est possible à cause de la forme du réseau R (tous les arcs sont orientés globalement de s en direction de t). Dans un tel chemin s, u, v, t, extraire l'arête u, v de G associée à l'arc (u, v) de ce chemin. Notons M l'ensemble de toutes ces arêtes ainsi récoltées. C'est un couplage de G.
Pourquoi est-ce un couplage ?
Essayons d'aller un peu plus loin dans la compréhension des choses. Imaginons que ce n'est pas un couplage. Cela veut dire que deux arêtes partagent une même extrémité. Par exemple a, b et a, c. Imaginons que a soit dans V1 (b et c sont donc dans V2 ). Si ces deux arêtes ont été extraites, c'est qu'il passait une unité de ot sur l'arc (a, b) sur l'arc (a, c) dans le réseau. Or, a n'est alimenté en ot que par la source s, via un arc de capacité 1. Pour que le sommet a puisse faire sortir deux unités de ot, il faudrait qu'il en reçoive deux (à cause de la contrainte de conservation). Or, à cause de la contrainte de capacité et du fait que le seul arc entrant en a est (s, a), c'est impossible. Le même type de raisonnement peut être fait avec deux arêtes u, v et w, v où v ∈ V2 (u ∈ V1 et w ∈ V1 ). Il est impossible en v de faire sortir deux unités de ot sur le seul arc sortant (v, t) du réseau, qui est de capacité 1. Conclusion : ce qui est construit est bien un couplage de G.
et
Pourquoi est-ce un couplage de taille maximale ?
Notons que le couplage construit par la méthode des ots est de taille exactement V , la valeur du ot. Il faut maintenant montrer qu'il n'y a pas de couplage plus grand (éventuellement, il peut y en avoir un autre de même taille, mais pas un strictement plus grand). Supposons le contraire, c'est-à-dire qu'il existe dans G un couplage M dont la taille est strictement supérieure à la valeur d'un ot maximal du réseau : |M | > V . Construisons un ot dans le réseau à partir de ce couplage M . Pour chaque arête u, v de M , faisons passer une unité de ot sur chaque arc du chemin s, u, v, t du réseau. Toutes ces unités de ot composent bien un ot qui respectent les contraintes de capacité (pas plus d'une unité de ot par arc) et les contraintes de conservation (ici les chemins de ot ne partagent aucun arc car M est un couplage). La valeur de ce ot est exactement le nombre de chemins de s vers t qui portent une unité de ot, c'est-à-dire précisément |M |. On a donc créé un ot dont la valeur est strictement supérieure à la valeur d'un ot maximal, ce qui est absurde. Le couplage construit par l'algorithme est bien de taille maximale.
104
Graphes et algorithmes ILLUST RAT ION Considérons le graphe biparti de la gure 11.15 sur laquelle est aussi représenté le réseau associé, ainsi qu'un ot de valeur maximale V = 3. Il est facile de vérier que c'est bien un ot. Montrons que sa valeur est maximale. Pour cela, il sut de trouver une s, t-coupe de capacité 3. Prenons S = {s, 4, 6, 1} et T = {2, 3, 5, 7, 8, t}. Les arcs de cette s, t-coupe sont donc : (s, 2), (s, 3) et (6, t) (ce sont les trois seuls, il n'y a pas d'autre arc (u, v) avec u ∈ S et v ∈ T dans le réseau). Comme chaque arc a une capacité de 1, cette s, t-coupe a une capacité de 3, ce qui montre que le ot a bien une valeur maximale. D'après la méthode, il faut extraire les arcs entre V1 et V2 qui transportent une unité de ot. Ce sont les arcs (1, 6), (2, 7) et (3, 5) (il faut regarder les valeurs colorées ici). Cela nous donne donc les arêtes suivantes dans G : 1, 6, 2, 7 et 3, 5 qui est bien un couplage (représenté à droite de la gure 11.15) du graphe initial.
Figure 11.15: Un graphe biparti, son réseau avec un ot de valeur maximale et un couplage extrait
11. Les ots : un problème de plomberie informatique
Conclusion.
105
Ce chapitre a été l'occasion d'aborder les ots qui forment une boîte à outils très utilisée en pratique en recherche opérationnelle (ensemble des méthodes et des techniques mathématiques ou informatiques permettant la recherche de la meilleure façon d'opérer des choix ou d'organiser des ressources en vue d'aboutir à un résultat visé ou au meilleur résultat possible). L'industrie se sert des résultats du domaine de la recherche opérationnelle pour organiser, planier, optimiser, voire sécuriser des réseaux (transport, énergie), des chaînes de production, etc. L'utilisation eective d'un ot pour résoudre un problème d'optimisation n'est pas toujours évidente. Un exemple a été donné avec la recherche d'un couplage de taille maximale dans un graphe biparti. L'algorithme des chaînes améliorantes, mis au point par les inventeurs des ots dans les années 1950, Ford et Fulkerson, a été présenté dans sa version la plus simple. Depuis, de nombreuses autres variantes de ots et d'autres algorithmes plus ecaces ont été proposés, améliorés, etc. Un des livres de référence, Network Flows (Ahuja, Magnanti et Orlin, chez Prentice Hall), fait déjà presque 850 pages chargées dans sa version de 1993. Nous sommes loin d'avoir fait le tour de ce vaste sujet.
12
Fabriquons une notice de montage
Pour construire une maison, il faut creuser les fondations, installer les réseaux (eau, électricité, évacuations, etc.), monter les murs, poser la toiture, faire les crépis, installer les portes, les fenêtres, faire la décoration intérieure, etc. Les tâches sont nombreuses et variées. Plusieurs corps de métiers peuvent intervenir. De même, faire un bon gâteau nécessite aussi d'accomplir plusieurs tâches : casser les ÷ufs, les mélanger à la farine, ajouter un zeste de ceci ou de cela, beurrer le récipient, étaler, décorer, ajouter la crème qui a été préparée avant, etc.
Un projet = des tâches à exécuter. Le point commun entre ces deux activités (et bien d'autres) est la réalisation d'un projet (construire une maison, faire un gâteau, etc.) qui nécessite d'accomplir diverses tâches (monter les murs, casser des ÷ufs, etc.). Nous parlerons dans ce chapitre de tâche, sans référence à ce qu'elle représente concrètement. L'accomplissement d'un projet revient ainsi à l'exécution de ses tâches. Deux paramètres importants sont à prendre en compte. Le premier est le nombre de ressources d'exécution , c'est-à-dire le nombre de machines ou de personnes aectées à l'exécution des tâches. Nous n'allons pas insister plus avant sur ce point car dans ce chapitre, nous traiterons du cas particulier où il n'y a qu'une seule personne ou qu'une seule machine pour les exécuter. Suite à ce point, la seconde question est de savoir dans quel ordre il faut les exécuter. Il est clair qu'il vaut mieux casser les ÷ufs avant de les mélanger à la farine. En revanche, la crème qui servira de nappage nal peut être préparée avant ou après l'action de beurrer le moule de cuisson. Une tâche T doit être exécutée avant T si T a besoin du résultat de T pour être elle-même exécutée (pour mettre les tuiles, il faut que les murs aient déjà été construits ; il faut aussi que la charpente ait été posée). Nous supposerons ici que toutes ces contraintes sont connues. En fonction de ces dernières, notre rôle va consister à déterminer l'ordre d'exécution des tâches. Le graphe orienté des contraintes de précédence. Nous allons représenter ces contraintes dites de précédence sous forme d'un graphe. Chaque sommet u va représenter une tâche du projet. Les relations entre les tâches/sommets sont
108
Graphes et algorithmes
nécessairement orientées ici pour capturer le fait que l'une doit avoir lieu avant l'autre. Pour cela, nous utiliserons des arcs qui vont créer un graphe orienté. Cette notion d'arc est utilisée au chapitre 11 mais voici un rappel. Un arc du sommet u vers le sommet v est noté (u, v) et représente une relation orientée de u vers v . L'arc (u, v) n'est pas la même chose que l'arc (v, u) (alors que l'arête u, v est le même objet que v, u). Un arc (u, v) est représenté graphiquement par une èche dirigée de u vers v .
Nous placerons donc un arc (u, v) si u doit être exécutée avant v .
ILLUST RAT ION Imaginons un projet composé de n = 7 tâches que l'on numérote 1, 2, 3, 4, 5, 6, 7. Voici des contraintes de précédence entre ces tâches, directement représentées par le graphe suivant.
Figure 12.1: Un graphe orienté de contraintes de précédence Interprétons quelques éléments de ces contraintes. La tâche 4 ne peut pas commencer à être exécutée avant que les tâches 1 et 2 soient totalement nies. De même, la tâche 2 ne peut débuter que si 5 et 3 sont nies. En revanche, rien n'oblige à exécuter 4 immédiatement après la n de 1 et de 2. La seule contrainte est que 4 ne doit pas débuter avant que 1 et 2 soient terminées.
La notice de montage, l'ordre d'exécution des tâches du projet, l'ordre topologique du graphe. Les données sont donc : un graphe G = (V, E) orienté
avec n sommets, chacun représentant une tâche. Rappelons que dans notre cas d'étude, il n'y a qu'une seule personne/machine pour les exécuter. Il faut donc qu'elle les exécute l'une après l'autre, dans un certain ordre. Cet ordre d'exécution sera représenté par une liste des n sommets du graphe G des contraintes. Reprenons l'exemple de la gure 12.1 et étudions la liste L = [1, 2, 3, 4, 5, 6, 7]. Elle n'est pas correcte car sa première tâche est 1 mais elle ne peut pas être exécutée tant que 3 et 6 ne sont pas exécutées.
12. Fabriquons une notice de montage
109
La liste L à construire doit impérativement vérier la propriété suivante : pour tout arc (u, v) de G, le sommet u doit se trouver avant le (à gauche du) sommet v dans L. Si elle existe, cette liste est appelée un ordre topologique du graphe orienté G. Essayons sur le graphe de la gure 12.1. Étudions la liste L = [3, 6, 5, 2, 1, 4]. Il faudrait vérier la propriété pour chaque arc, ce qui serait un peu long. Vérions-la pour l'arc (3, 6), 3 est bien avant 6 ; pour l'arc (2, 4), 2 est bien avant 4, etc. En exécutant les tâches dans cet ordre : 3, puis 6, puis 5, puis 2, puis 1, puis 4, toutes les contraintes de précédence sont bien satisfaites. Il s'agit bien d'un ordre topologique du graphe de la gure 12.1. Pour s'en convaincre de manière plus visuelle, il sut de dessiner ce graphe en plaçant les sommets dans l'ordre de L, ce qui donne le résultat suivant.
On constate ici que les arcs sont tous orientés de la gauche vers la droite. Lorsque vient le tour d'un sommet u, tous les sommets v pour lesquels il y a un arc (u, v) ont déjà été exécutés, ce qui est conforme à ce qui est attendu. La solution n'est pas unique, voici une autre liste qui convient aussi.
Un graphe orienté avec circuit n'a pas d'ordre topologique.
le graphe orienté suivant.
Figure 12.2: Trouvez un ordre topologique
Considérons
110
Graphes et algorithmes
Ce graphe orienté n'a pas d'ordre topologique. Pour s'en persuader, voici le même graphe avec certains arcs dessinés en pointillés.
Ces arcs forment ce qui s'appelle un circuit (c'est le pendant d'un cycle dans un graphe non orienté). Examinons-le. Pour satisfaire uniquement ces contraintes, il faudrait que 9 soit avant 3, lui-même avant 7, qui devrait être avant 5, qui devrait être avant 9. Il faudrait donc que 9 soit placé dans une liste avant 9. Ce qui est impossible. Il est clair que ce raisonnement peut facilement être généralisé et permet d'obtenir le résultat suivant. Si G a un ordre topologique, alors il ne contient pas de circuit. Il serait intéressant de savoir si la réciproque est vraie. C'est le cas.
Un graphe orienté sans circuit contient un ordre topologique.
Le point intéressant ici est que l'on va proposer un algorithme pour construire un ordre topologique d'un graphe sans circuit. Soit G = (V, E) un tel graphe. On peut montrer que G contient nécessairement un sommet u qui n'a pas d'arc entrant, c'est-à-dire pas d'arc de la forme (v, u) (dont la èche pointe sur u). Cela peut se montrer par contradiction. Nous n'allons pas le faire, juste constater que c'est le cas du graphe sans circuit de la gure 12.1 vu plus haut : les sommets 3 et 5 ont cette propriété. L'autre propriété évidente est qu'en supprimant n'importe quel sommet d'un graphe sans circuit, on garde un graphe sans circuit (le fait de supprimer un sommet ne peut pas créer un circuit). Grâce à ces propriétés, voici une méthode pour construire un ordre topologique d'un graphe G = (V, E) orienté sans circuit. Faire une copie H du graphe G original. Initialiser une liste L, vide au départ. Tant que H contient au moins un sommet, faire les opérations suivantes. Soit u un sommet de H qui n'a pas d'arc entrant. Ajouter u à la n de la liste L puis supprimer u de H (ainsi que tous les arcs dans lesquels u est une extrémité). À la n, chaque sommet de G sera dans la liste L (et sa copie H n'aura plus aucun sommet). À chaque étape, l'algorithme ajoute un sommet u à L et le supprime de H , grâce aux propriétés évoquées plus haut.
12. Fabriquons une notice de montage
111
ILLUST RAT ION Considérons le graphe orienté G de la gure suivante. Il n'a pas de circuit. Attention, les sommets 5, 4 et 3, par exemple, ne sont pas dans un circuit car les èches ne sont pas toutes orientées dans le même sens de circulation circulaire .
Figure 12.3: Un graphe orienté sans circuit Le premier sommet que l'on peut mettre dans L (initialement vide) est 5. C'est ici le seul premier choix possible car tous les autres sommets ont au moins un arc entrant. Maintenant L = [5] et le graphe H dans lequel on supprime 5 est :
Ce nouveau graphe est toujours sans circuit bien sûr et il a deux sommets, 3 et 1, sans arc entrant. L'algorithme demande d'en choisir un des deux. Prenons 3 qui est alors ajouté à la liste, qui devient L = [5, 3] ; le graphe H est le suivant après suppression de 3.
Graphes et algorithmes
112
Deux choix sont maintenant possibles : le sommet 1 ou le sommet 4 mais pas les deux autres. Prenons 1. La liste devient alors L = [5, 3, 1] et le nouveau graphe est :
Maintenant c'est 4 ou 6 qui doit être traité. Traitons 4. Cela donne la liste L = [5, 3, 1, 4] et le graphe :
Les deux dernières étapes consistent à prendre 6 puis 2. La liste nale sera donc : L = [5, 3, 1, 4, 6, 2]. En plaçant les sommets dans l'ordre de cette liste, le graphe se dessine de la manière suivante.
Les arcs sont bien orientés de la gauche vers la droite.
Conclusion.
L'algorithme proposé consiste à exécuter une tâche qui n'est pas contrainte, puis à l'enlever et à recommencer tant que toutes les tâches ne sont pas exécutées. Cela revient à construire un ordre topologique d'un graphe orienté. Un tel ordre existe si et seulement si le graphe ne contient pas de circuit. La méthode décrite dans ce chapitre est facile à mettre en ÷uvre dans un petit graphe. Dès que celuilà devient plus gros, c'est plus compliqué. Un autre algorithme basé sur le parcours en profondeur (vu au chapitre 4) fait aussi le même travail. Bien programmé, il est ecace. La situation décrite dans ce chapitre est un cas particulier d'ordonnancement. De très nombreuses variantes existent et ne sont pas toutes aussi simples à résoudre. En voici quelques-unes. Dans ce chapitre, une seule machine est disponible pour exécuter les tâches, mais le système pourrait en comporter plusieurs. Dans ce cas, certaines tâches peuvent être exécutées en même temps que d'autres. Il s'agit alors d'une exécution parallèle. Les tâches peuvent aussi avoir une durée d'exécution. Elles peuvent aussi éventuellement avoir besoin de plusieurs machines de manière simultanée pour étre exécutées. Les tâches considérées dans ce chapitre doivent être exécutées d'un bloc, entièrement, avant de passer à la suivante. Ce n'est pas forcément le cas dans
12. Fabriquons une notice de montage
113
tous les systèmes. Dans certaines situations, des tâches peuvent être exécutées par petits bouts. Il est alors possible de mélanger sur une même machine l'exécution de plusieurs d'entre elles. Dans d'autres cas, les tâches peuvent avoir des dates : l'exécution de telle tâche de durée p doit débuter après la date d et être terminée avant la date f . Les tâches à exécuter ne sont pas forcément toutes connues dès le début mais dévoilées une par une ou par lots. Le critère associé à la qualité d'un ordonnancement peut aussi être diérent. Dans ce chapitre, nous demandions simplement à ce que toutes les tâches soient exécutées, en respectant les contraintes de précédence. Dans des modèles plus compliqués, la date de n de l'ordonnancement peut être un objectif à minimiser. Il existe une multitude de modèles d'exécution, de contraintes et d'objectifs. Les problèmes associés sont souvent diciles. Le monde de l'ordonnancement a ses propres résultats et méthodes. Un ouvrage entièrement consacré à ce sujet pourrait être écrit, sans en épuiser toutes les subtilités.
13
À vous de jouer !
Ce chapitre propose des activités qui reprennent, pour certaines, une partie de ce qui a été vu jusqu'à présent. Il est conseillé de rééchir aux solutions avant de lire les réponses. C'est aussi (et surtout) l'occasion de présenter d'autres résultats.
Le nombre de sommets de degré impair d'un graphe
Considérons un graphe G = (V, E) quelconque. Rappelons que le degré d'un sommet u dans G, noté degG (u), est le nombre de ses voisins. Que pouvez-vous dire du nombre de sommets de G ayant un degré impair ? Étes-vous capable de construire un graphe ayant un nombre donné quelconque de sommets de degré impair ? (par exemple, essayez de construire un graphe ayant sept sommets de degré impair.) Essayez de répondre à ces questions avant de lire la réponse qui suit. Notons I l'ensemble des sommets de G qui ont un degré impair. Montrons que I contient nécessairement un nombre pair (ou nul) de sommets (ce qui entraîne que G ne peut pas avoir un nombre impair de sommets de degré impair. La construction demandée avec 7 est donc impossible). La somme des degrés de tous les sommets de G est paire (cela a été montré au chapitre 2). La somme des degrés des sommets qui ont un degré pair (ou nul) est forcément paire (ou nulle) (car c'est une somme d'entiers pairs (ou nuls)). Les autres sommets de G sont ceux de I . La somme de leurs degrés doit donc forcément être paire aussi. Or une somme d'entiers impairs ne peut être paire que s'il y a un nombre pair (ou nul) de valeurs à sommer. Ce qui montre que I contient un nombre pair (ou nul) de sommets. Ce résultat est valable pour n'importe quel graphe G. Examinons rapidement le cas où l'on veut construire un graphe ayant un nombre p pair de sommets de degré impair. C'est facile, il sut par exemple de prendre le graphe complet (chaque sommet est voisin de tous les autres) à p sommets. Chacun des p sommets a un degré égal à p − 1, qui est bien impair.
Paramètres de quelques familles de graphes Voici la description de quelques familles particulières de graphes. Pour chacune d'elles, dessinez quelques exemples puis calculez le nombre n de sommets, le nombre
Graphes et algorithmes
116
m d'arêtes ainsi que les degrés des diérents sommets, en fonction du (ou des) paramètre(s) de la famille.
Graphes complets.
Soit p un entier positif. Kp est un graphe à p sommets dans lequel chaque sommet est voisin de tous les autres.
Cycles.
Soit p un entier, p ≥ 3. Cp est un graphe à p sommets numérotés 0, 1, . . . , n − 1. Chaque sommet i est connecté au sommet i + 1 sauf le sommet n − 1 qui est connecté au sommet 0.
Zone orange Grilles. Ce graphe est en zone orange car sa dénition est un peu plus
abstraite. Mais vous comprendrez facilement sa structure lorsque vous verrez un exemple dessiné. Soit p ≥ 2 et q ≥ 2 deux entiers. La grille p×q est notée GRp×q . À chaque couple (x, y), avec x un entier compris entre 1 et p, et y un entier compris entre 1 et q , correspond un sommet (le couple (x, y) peut être vu comme les coordonnées du sommet). De manière informelle, deux sommets sont voisins si une seule des deux coordonnées dière d'exactement une unité. Exprimons-le de manière plus rigoureuse : les deux sommets (x, y) et (a, b) de la grille sont voisins si : |x − a| + |y − b| = 1 (|x − a| est la valeur absolue de x − a).
Zone rouge Les deux familles suivantes sont plus diciles à comprendre. Les représentations graphiques de quelques exemples donnés plus loin vous aideront sûrement.
Tores.
Soit p ≥ 3 et q ≥ 3 deux entiers. Le tore p × q est noté Tp×q . À chaque couple (x, y), avec x un entier compris entre 0 et p − 1, et y un entier compris entre 0 et q − 1, correspond un sommet (le couple (x, y) peut être vu comme les coordonnées du sommet). Les arêtes sont les suivantes. Les deux sommets (x, y) et (a, b) du tore sont voisins uniquement si : soit x = a et y = b ± 1 mod p, soit x = a ± 1 mod q et y = b.
Hypercubes.
Soit d un entier, d ≥ 0. L'hypercube de dimension d est noté Hd . À chaque vecteur binaire à d composantes possible, correspond un sommet de Hd . Un sommet est donc représenté ici par un vecteur (b1 , . . . , bd ) où chaque bi vaut soit 1 soit 0. Il y a une arête entre deux sommets dans
13. À vous de jouer !
117
Hd si les deux vecteurs associés à ces sommets dièrent sur exactement une composante. Avant de lire la suite, essayez par vous-même de dessiner des cas particuliers (xez une valeur des paramètres), puis tentez d'évaluer n, m et les degrés des diérents sommets.
Graphes complets.
Le graphe complet Kp contient n = p sommets et arêtes. Chaque sommet a le même degré que tous les autres, p − 1.
p(p−1) 2
Les cycles.
Les cycles dans les graphes ont été évoqués au chapitre 2. Ici Cp n'est qu'un cycle, sans rien d'autre autour. Voici dessiné C5 pour illustration.
Les paramètres de Cp sont les suivants. Il a n = p sommets, m = p arêtes et le degré de chaque sommet est égal à 2.
Les grilles.
Voici le dessin d'une grille GR3×4 (p = 3 et q = 4) avec les sommets qui ont pour identiant un couple de coordonnées.
Dans un tel graphe, n = pq (car il y a pq couples possibles de coordonnées), m = (p−1)q +(q −1)p. Voyons comment obtenir ce résultat. Prenons comme référence une grille GRp×q dessinée comme à la gure précédente. Chaque ligne est composée de q − 1 arêtes horizontales et il y a p lignes. Chaque colonne est composée de p − 1 arêtes verticales et il y a q colonnes. Cela donne le résultat. Il est facile de constater que le degré de chaque sommet est 2 (les coins ) ou 3 (les sommets sur le pourtour ) ou 4 (les sommets internes ).
T3,4 p = 3
q=4
4 n = pq
m = 2pq p
q
q
p
pq + qp = 2pq
Hd (b1 , . . . , bd )
bi
0
1 b1 . . . bd
H3
H2
13. À vous de jouer !
119
Notez que la construction est faite dimension par dimension. H3 est construit en prenant deux copies de H2 , en ajoutant 0 à gauche de chaque vecteur d'une copie et 1 à gauche de chaque vecteur de l'autre. Ensuite, les nouvelles arêtes sont ajoutées entre sommets qui avaient le même vecteur avant l'ajout du nouveau 0 ou 1. Voici les paramètres de Hd . Le nombre de sommets est égal au nombre de vecteurs binaires possibles à d composantes b1 . . . bd . Pour chaque bi , il y a exactement deux choix, 0 ou 1, indépendamment des choix sur les autres éléments du vecteur. Cela donne n = 2d sommets. Notons que chaque sommet a exactement d voisins qui sont les d sommets ayant exactement un élément du vecteur binaire qui dière du sien. De ce fait, pour calculer le nombre m d'arêtes, il est possible d'utiliser le résultat selon lequel la somme des degrés des sommets d'un graphe vaut 2m. Dans Hd , il y a n = 2d sommets qui ont chacun un degré d. La somme des degrés vaut donc 2d d, par conséquent 2m = 2d d et ainsi m = 2d−1 d. La gure suivante représente H4 .
L'art de découper une planche en dominos Considérons une plaque (de bois par exemple) carrée composée de n2 carrés unitaires dans laquelle deux carrés dans deux coins opposés sont supprimés. Une telle plaque sera dite tronquée n × n. Par exemple si n = 8, la plaque tronquée 8 × 8 est la suivante.
120
Graphes et algorithmes
L'objectif est de découper une telle plaque tronquée n × n en dominos, en ne faisant aucun déchet (chaque carré unitaire de la plaque doit faire partie d'un domino). Un domino est composé de deux carrés partageant un côté dans la plaque initiale.
Quand est-ce possible ? À vous d'essayer, avant de lire la suite.
La solution du problème. Une plaque tronquée n × n contient n2 − 2 carrés unitaires. Pour qu'il n'y ait pas de déchet, il faut que chaque carré unitaire fasse partie d'un (seul) domino. Pour cela, il est nécessaire que n2 − 2 soit pair, c'est-à-dire que n soit pair. Mais, attention, cela n'est qu'une condition nécessaire. Cela veut donc dire que si n est impair, alors (comme il y a un nombre impair de carrés unitaires) un découpage sans déchet est impossible. Mais ça ne montre pas que lorsque n est pair, c'est possible. D'ailleurs, même si n est pair, c'est impossible aussi. Voyons pourquoi. Pour mieux visualiser les choses, nous allons peindre les cases de la plaque en noir et blanc en alternance, par ligne et par colonne, comme les cases d'un échiquier. Voici ce que cela donne pour n = 8.
13. À vous de jouer !
121
Avec cette coloration des cases, un domino doit nécessairement être composé d'une case blanche et d'une case noire voisines l'une de l'autre. Or, dans une plaque tronquée n × n avec n pair, il y a strictement plus de cases d'une couleur que de cases de l'autre couleur. Dans l'exemple, il y a plus de cases noires que de blanches. Cela s'explique par le fait que si la plaque contenait les n2 carrés unitaires (si elle n'était pas tronquée), alors il y aurait autant de blanc que de noir et les cases de coins diamétralement opposées seraient de la même couleur. En les supprimant, on crée donc un déséquilibre de couleurs . Tout découpage en dominos laisse donc nécessairement au moins un carré unitaire en déchet.
Une autre façon de voir ce problème à travers les graphes.
La plaque noire et blanche tronquée peut être associée à un graphe. Chaque carré unitaire est un sommet et une arête lie deux sommets/carrés s'ils partagent un même côté (voir gure suivante pour une illustration). Ce graphe est biparti (voir chapitre 3) car chaque sommet a une couleur parmi deux (ici noir ou blanc) et chaque arête a ses deux extrémités de couleurs diérentes. Or, un domino doit être composé d'une case noire et d'une blanche côte à côte, c'est donc une arête de ce graphe. Comme deux dominos ne partagent pas une même case, les arêtes recherchées doivent être disjointes. Il s'agit d'obtenir un couplage. Enn, comme il ne doit y avoir aucun déchet, cela se traduit par le fait que chaque sommet doit être l'extrémité d'une arête de ce couplage. Cela s'appelle un couplage parfait. On cherche donc un couplage parfait dans un graphe biparti non équilibré (où il y a strictement plus de sommets d'une couleur que de l'autre), ce qui est impossible.
Graphes et algorithmes
122
Chemins hamiltoniens orientés dans les tournois Un tournoi à n sommets est un graphe complet à n sommets dans lequel chaque arête u, v est remplacée soit par l'arc (u, v) orienté de u vers v , soit par l'arc (v, u) orienté de v vers u. Le nom de ces graphes orientés vient des compétitions sportives dans lesquelles des équipes ou des individus s'arontent dans des matches qui ont forcément un gagnant (et un perdant), par exemple au tennis ou au foot (avec tirs au but en cas d'égalité à la n du temps réglementaire). Un arc de u vers v indique la victoire de u sur v . Mais le sport n'est pas ce qui va nous intéresser ici. Nous allons rester dans le monde des graphes. Un chemin hamiltonien orienté d'un tournoi à n sommets est un chemin contenant les n sommets, composé de n − 1 arcs et dans lequel les arcs doivent être pris dans le sens des èches d'une extrémité à l'autre du chemin. Est-ce que le tournoi suivant contient un chemin hamiltonien orienté ?
Il en contient un, dessiné en pointillés dans ce qui suit.
Ce chemin est donc : 1 → 3 → 5 → 2 → 4 → 6. Attention, la séquence suivante n'est pas correcte : 1 → 6 → 2←3 → 5 → 4 car l'arc (3, 2) n'est pas orienté dans le sens du cheminement de 1 vers 4, les deux extrémités. La question générale est donc : est-ce que tout tournoi contient (au moins) un chemin hamiltonien orienté ? Je vous invite à y rééchir avant de lire la solution dans ce qui suit.
13. À vous de jouer !
123
Zone orange Prouvons par récurrence sur le nombre de sommets que tout tournoi a nécessairement un chemin hamiltonien orienté (CHO). Le résultat peut facilement être vérié à la main lorsque n = 3 et 4. Supposons la propriété vraie pour tout tournoi à n − 1 sommets. Considérons un tournoi G à n sommets. Soit u un sommet de G. Il est clair que G − u (G dans lequel le sommet u et ses arcs ont été supprimés) est aussi un tournoi, à n − 1 sommets. Par hypothèse de récurrence, il contient donc un CHO, noté u1 , u2 , . . . , un−1 .
Si G contient l'arc (u, u1 ) ou (un−1 , u), alors G a aussi un CHO.
Sinon, G contient les arcs (u1 , u) et (u, un−1 ). Observons maintenant les arcs entre ui et u pour i croissant. Cette suite commence avec un arc entrant en u (l'arc (u1 , u)) et se termine par un arc sortant de u (l'arc (u, un−1 )). Il existe donc un indice i tel que l'on ait les arcs (ui , u) et (u, ui+1 ) (un indice où la suite d'arcs entrants débutant en (u1 , u) stoppe et où commence une série d'arcs sortants de u). Cela permet de mettre à jour le CHO : u1 , . . . , ui , u, ui+1 , . . . , un .
Zone orange
Nombre minimal de pas dans un tournoi Cette partie est aussi dédiée aux tournois. Avant d'arriver à la question posée, il faut dénir un certain nombre de notions indispensables. Plaçons-nous à nouveau dans un tournoi G à n sommets. Le degré sortant d'un sommet u dans G est le nombre d'arcs sortants de u, c'est-à-dire d'arcs du type (u, v). Par exemple, dans le tournoi de la gure 13.1, l'arc (3, 4) est
124
Graphes et algorithmes un arc sortant de 3 (et entrant en 4), le degré sortant du sommet 4 est 2 (il n'y a que les arcs (4, 2) et (4, 6) qui sortent de 4). Pour chaque couple de sommets u et v de G, on note α(u, v) le plus petit nombre d'arcs à traverser pour aller de u à v (attention, un arc (a, b) doit être traversé de a vers b). S'il est impossible d'aller de u vers v , alors α(u, v) = ∞ (c'est le cas par exemple si u n'a aucun arc sortant). Notons que α(u, v) est toujours diérent de α(v, u). En eet, supposons qu'entre u et v , il y ait l'arc (u, v). Cela indique que α(u, v) = 1. Mais dans ce cas, l'arc (v, u) ne fait pas partie du tournoi et pour aller de v vers u, il faudra emprunter au moins deux arcs : α(v, u) ≥ 2. Illustrons ces notations dans le tournoi de la gure 13.1 : α(2, 3) = 1, α(3, 2) = 2, α(4, 7) = 3 (4 → 6 → 8 → 7). Notons β(u) la plus grande des valeurs α(u, v), c'est-à-dire la plus grande distance de u vers un autre sommet de G : β(u) = max{α(u, v) : v ∈ G} (éventuellement, β(u) peut être inni). Par exemple, dans le tournoi de la gure 13.1, β(3) = 2.
Figure 13.1: Un tournoi à huit sommets Soit G un tournoi quelconque à n sommets. Dans ce qui suit, on notera r le sommet qui a le plus grand degré sortant de G. Si plusieurs sommets
ont un degré maximal, en prendre un quelconque. Voici quelques questions auxquelles je vous encourage à rééchir avant de lire les réponses plus loin.
1. Est-ce que pour tout n ≥ 2, il existe un tournoi G à n sommets dans lequel β(r) = 1 ? 2. Est-ce que pour tout k ≥ 1, il existe un tournoi G dans lequel β(r) ≥ k ? La première question a une réponse simple. Il sut de construire un tournoi G à n sommets dans lequel un sommet n'a que des arcs sortants. Ce sommet est alors le sommet r de degré sortant maximal dans G et il est clair que α(r, u) = 1 pour tout u = r. La seconde question a une réponse moins évidente. Nous allons montrer que β(r) ne peut pas être arbitrairement grand. Considérons un tournoi G à n sommets et un sommet r de degré sortant maximal noté d. Notons v1 , . . . , vd
13. À vous de jouer !
125
les d sommets de G qui sont l'extrémité d'un arc du type (r, u), sortant de r. Notons N1 l'ensemble de ces sommets. Pour chaque vi de N1 , α(r, vi ) = 1. Si d = n − 1, alors il sut de traverser un seul arc pour passer de r à n'importe quel sommet de G (contexte de la question 1) et β(r) = 1. Supposons maintenant que d < n − 1. Les sommets autres que les d sommets vi et que r lui-même sont au nombre de n − 1 − d. Notons-les u1 , . . . , un−d−1 . Si chaque ui a un arc entrant venant d'un vj , alors tout sommet peut être atteint par un chemin de longueur au plus 2 à partir de r. Supposons maintenant le contraire. Cela veut dire qu'il existe un ui qui n'a aucun arc entrant en provenance d'un sommet de N1 . Le tournoi contient donc chaque arc (ui , vj ) pour chaque vj ∈ N1 . Le sommet ui a donc tous les d sommets de N1 comme voisins sortants, plus le sommet r lui-même (sinon le tournoi contiendrait l'arc (r, ui ) et, dans ce cas, ui serait dans l'ensemble N1 ). Donc ui a un degré sortant au moins d + 1, ce qui contredit le fait que le degré sortant maximal dans G est d. Ce cas ne peut donc pas se présenter. Conclusion : dans n'importe quel tournoi, β(r) ≤ 2. Les sommets de degré sortant maximal sont assez centraux dans un tournoi, ce qui n'est pas une surprise, mais cela demandait à être démontré.
Regroupons les amis et séparons les ennemis Imaginons la situation suivante. Des chefs d'État sont réunis pour un congrès. Un banquet est prévu pour une des soirées. Il faut donc faire un plan de table en évitant impérativement que deux ennemis soient assis à la même table et en faisant en sorte que deux amis/partenaires soient assis à la même table. Comment savoir si une telle organisation est possible (on ne prend pas en compte ici la taille des tables) ? Attention, ce n'est pas parce que A est ami avec B et B est ami avec C que A est ami avec C (la diplomatie est parfois mystérieuse). Voici comment représenter cela sous forme de graphe.
Représentation sous forme d'un graphe signé.
Dans cette partie chaque arête est dite soit positive soit négative. Un tel graphe est dit signé. Une arête u, v positive (respectivement négative) relie deux amis/partenaires (respectivement ennemis) qui doivent impérativement être assis (respectivement ne pas être assis) à la même table. On va chercher ici à partager l'ensemble V des sommets (des diplomates) en ensembles V1 , . . . , Vk (chaque Vi va représenter l'ensemble des personnes assises à la même table numéro i) de telle manière que : chaque sommet u est dans exactement un ensemble Vi : une personne est assise à une table ;
126
Graphes et algorithmes chaque arête positive u, v a ses deux extrémités dans un même ensemble (u ∈ Vi et v ∈ Vi ) : deux amis doivent être assis à la même table ; chaque arête négative u, v a ses deux extrémités dans deux ensembles diérents (u ∈ Vi , v ∈ Vj et i = j ) : deux ennemis ne doivent pas être à la même table.
Le nombre k des sous-ensembles et la taille de chacun d'eux ne sont pas importants ici. On cherche juste un découpage satisfaisant ces contraintes. Dans ce qui suit, nous faisons abstraction de la signication des sommets et des arêtes pour nous concentrer uniquement sur le problème de graphe. Avant de lire la suite, essayez de rééchir à une solution.
ILLUST RAT ION Considérons le graphe signé de la gure 13.2 (a) où les arêtes positives sont vertes (pleines) et les négatives sont en pointillés. Regroupons les sommets de manière à vérier les contraintes énoncées plus haut.
Figure 13.2: (a) Un graphe signé. (b) Un regroupement possible
13. À vous de jouer !
127
Prenons V1 = {4, 5, 7, 8}, V2 = {1, 2, 6} et V3 = {3}. Pour visualiser ce partage, tous les sommets d'un même ensemble ont été coloriés de la même couleur sur la gure 13.2 (b). De plus, les sommets ont été regroupés pour mieux faire ressortir chaque Vi . Dans cette solution particulière, le sommet 3 est tout seul, ce qui n'est pas interdit (même si dans l'exemple des chefs d'État, cela semblerait étrange d'avoir une table avec une seule personne). On vérie facilement que chaque arête verte pleine a ses deux extrémités dans le même ensemble et chaque arête en pointillés est entre deux ensembles diérents. Essayez maintenant de faire la même chose avec le graphe signé de la gure 13.3. Vous devriez avoir plus de mal.
Figure 13.3: Les arêtes positives vertes pleines et les arêtes négatives en pointillés
Ce n'est pas toujours possible.
Si vous avez essayé de résoudre le problème pour le graphe signé de la gure 13.3, vous n'avez pas trouvé de solution. Mais ne pas trouver de réponse ne veut pas dire qu'il n'y en a pas. Il faudrait prouver qu'il n'y en a pas. Voici comment dans ce cas. Observons sur la gure 13.4 une portion du graphe signé de la gure 13.3. Seuls les sommets 1, 9, 7, 13, 10 et les arêtes entre eux ont été conservés.
Figure 13.4: Une portion du graphe signé de la gure 13.3
On constate ici clairement que 1 doit nécessairement être dans le même ensemble que 9 (car l'arête 1, 9 est positive), que 9 doit être dans le même ensemble que 7, que 7 et 13 doivent aussi être dans le même ensemble, et que 13 et 10 aussi. Ainsi, ces cinq sommets doivent obligatoirement être dans le même ensemble à cause des arêtes vertes entre eux. Mais, en même temps, 1 et 10 ne doivent pas être dans le même ensemble (l'arête 1, 10 est négative). Il n'y a donc pas de solution au problème.
Graphes et algorithmes
128
Quand est-ce possible ?
Les points précédents ont montré que certains graphes signés ont une décomposition et d'autres pas. Comment les distinguer ? Voici une méthode. 1. Construire Gp , le graphe obtenu en ne gardant que les arêtes positives et tous les sommets de G. L'ensemble des sommets de chaque partie connexe (on parle de composante connexe) de Gp devient un ensemble de type Vi . 2. Examinons chaque arête négative de G. (a) S'il y en a une qui a ses deux extrémités dans le même ensemble Vi , alors il n'y a pas de solution. (b) Sinon les ensembles V1 , . . . , Vk forment bien une solution au problème.
ILLUST RAT ION Examinons le graphe signé de la gure 13.5 et construisons le graphe composé de tous ses sommets et des arêtes positives uniquement. Cela donne le graphe Gp de la gure 13.6.
Figure 13.5: Un graphe signé Chaque partie connexe de Gp donne naissance à un ensemble Vi , ce qui donne ici :
V1 = {1, 4, 7}, V2 = {2, 3, 6, 10, 11}, V3 = {5, 8}, V4 = {9}. L'arête négative 2, 10 de G est alors entre deux sommets du même ensemble V2 . L'algorithme dit qu'il n'y a pas de solution. On peut constater que c'est eectivement le cas, car en isolant de G les sommets 2, 3 et 10, on obtient deux arêtes positives 2, 3 et 3, 10 qui obligent les sommets 2 et 3 à être dans le même ensemble, alors que l'arête négative 2, 10 interdit cela. Aucune solution possible.
13. À vous de jouer !
129
Figure 13.6: Le graphe Gp composé uniquement des arêtes positives du graphe signé G de la gure 13.5
Des applications multiples.
Pour en revenir à l'application diplomatique évoquée au début, cette méthode permet facilement de savoir s'il y a ou non une solution (une table est ici un ensemble Vi ). Mais d'autres applications de ce résultat sont envisageables. Dans le secteur de la logistique par exemple, prenons le cas d'une entreprise faisant de l'export par bateaux. Certaines marchandises doivent impérativement partager un même conteneur car elles sont destinées au même client nal ou doivent être conditionnées de manière spéciale, pour des raisons de conservation ou de sécurité. En revanche, d'autres marchandises ne doivent absolument pas partager le même conteneur (pour des raisons de compatibilité chimique ou d'émissions radioactives, etc.). Le fait de savoir s'il est possible d'organiser les expéditions en respectant ces contraintes est donné par une solution au problème (un conteneur est ici un ensemble Vi ). Notons que la taille et le nombre des ensembles Vi n'est ici pas du tout pris en compte, ce qui peut poser d'autres problèmes pratiques si les tables ou les conteneurs ont des tailles limitées.
14
Des problèmes très diciles à résoudre
Dans les chapitres précédents, nous avons étudié des problèmes qui peuvent être résolus grâce à des algorithmes performants : construire un arbre couvrant d'un graphe connexe (avec le parcours en largeur ou en profondeur), calculer des distances dans un graphe (parcours en largeur ou algorithme de Dijkstra), construire un arbre couvrant de poids minimal (algorithme de Prim), construire un parcours cyclique eulérien, construire un couplage stable, etc. Ces premiers chapitres pourraient donner l'illusion que tout problème a un algorithme adapté ecace qui permet de le traiter. Il n'en est rien. Cela peut paraître surprenant car un graphe est un objet vraiment très simple. Dans les prochains chapitres, nous verrons des problèmes faciles à décrire mais très diciles à résoudre. Nous verrons aussi un moyen de contourner en partie cette diculté. Pour expliquer précisément cette diérence entre facile et dicile , il faudrait entrer dans des détails de complexité algorithmique assez pointus. Ce n'est pas le sujet de ce livre. Nous allons donc simplement donner quelques indications informelles de ce que cela veut dire et surtout décrire quels impacts pratiques tout cela peut avoir. Un problème est facile si l'on sait le résoudre avec un algorithme ecace, c'est-à-dire dont le nombre de pas ou d'opérations algorithmiques élémentaires 1 n'est pas trop grand. Bien sûr, cette quantité est, en général, d'autant plus grande que la taille du graphe à traiter est importante. Ce nombre d'opérations pour construire un résultat est une fonction de la taille x du graphe donné en entrée et sert à mesurer la complexité en temps de l'algorithme. La théorie de la complexité considère qu'un algorithme est raisonnable si sa complexité est un polynôme en x, par exemple 2x3 − 4x + 17 ou 6x − 4. On distingue ainsi d'un côté les problèmes faciles, pouvant être résolus avec un algorithme de complexité polynomiale, de tous les autres pour lesquels aucun algorithme ecace n'est disponible (ce qui ne veut pas dire qu'il n'en existe pas).
1. Par exemple l'aectation d'une valeur dans une variable, un test, le calcul d'une expression arithmétique simple, etc.
132
Graphes et algorithmes
Parmi ces autres problèmes, les problèmes NP-complets sont ceux qui seront qualiés (de manière très simpliée) dans la suite de diciles. Nous n'allons pas donner une dénition précise de dicile mais voici quelques conséquences découlant de cette notion. 1. À l'heure actuelle, personne ne connaît d'algorithme de complexité polynomiale permettant de résoudre de tels problèmes. Cela fait plusieurs décennies que les chercheurs en algorithmique du monde entier essaient d'en inventer, en vain à ce jour. 2. Ces problèmes diciles ont des relations profondes entre eux, ils sont équivalents dans le sens où : (a) si un seul peut être résolu avec un algorithme polynomial, alors il existe un algorithme polynomial permettant de résoudre chacun des autres ; (b) une preuve démontrant qu'il est impossible d'en résoudre un avec un algorithme polynomial montrerait, par équivalence, que c'est impossible aussi pour les autres problèmes diciles. Il existe des centaines de tels problèmes. Le livre de référence sur ce sujet (Garey et Johnson, Computer and Intractability, Freeman and Company) date de 1979 et en recense déjà environ 270. Or, de nouveaux sont découverts chaque année car il existe des techniques pour démontrer qu'un problème est dicile (NP-complet) en prouvant qu'il est équivalent à un autre problème dicile. Il n'y a pas que des problèmes de graphes mais ces derniers occupent une place centrale. Il est tentant de penser que ce ne sont que des problèmes théoriques obscurs et que cela n'a pas du tout de conséquences pratiques. Il n'en est rien. La résolution ecace de ces problèmes diciles permettrait à nombre de logiciels d'être considérablement plus performants. L'enjeu est double : pratique et théorique, à tel point qu'il a été mis à prix par l'Institut de mathématiques Clay aux États-Unis sous la forme d'un problème du millénaire, dont la résolution permettrait au gagnant d'empocher la confortable somme d'un million de dollars et probablement aussi le prix Turing, l'équivalent d'un prix Nobel en informatique. La situation est donc la suivante : certains problèmes ont un énoncé simple, sur des objets simples, et sont pourtant très diciles à résoudre.
Le problème du chemin hamiltonien. Voici un exemple de problème dicile. La question consiste à savoir si un graphe G = (V, E) a ou non un chemin hamiltonien, c'est-à-dire un chemin contenant les n sommets de G et ayant exactement n−1 arêtes. La gure suivante représente deux graphes. Le premier a un chemin hamiltonien (arêtes en pointillés) et le second n'en a pas. On demande ici simplement un algorithme qui répond OUI (si G a un chemin hamiltonien) ou NON (s'il n'en a pas). À ce jour, personne ne connaît un tel algorithme, ayant une complexité polynomiale, résolvant ce problème pour n'importe quel graphe.
14. Des problèmes très diciles à résoudre
133
Relations avec le chapitre 9.
Le chapitre 9 est consacré à un problème très proche, celui du cycle hamiltonien, c'est-à-dire savoir s'il existe ou non un cycle à n arêtes contenant les n sommets de G. Ce problème est lui aussi dicile dans sa généralité. Mais le chapitre 9 aborde cette question dans des familles particulières de graphes, dans lesquelles on sait prouver qu'il y a un tel cycle (les conditions de Ore). L'enjeu était alors de construire de manière eective, dans ces graphes très spéciaux, des cycles hamiltoniens. Dans le présent chapitre, la question porte sur les chemins hamiltoniens (et non sur les cycles) mais elle est surtout plus large puisqu'elle concerne n'importe quel graphe .
L'algorithme exhaustif.
Imaginons que l'on veuille absolument résoudre ce problème malgré tout. Voici un algorithme (assez naïf) pour y parvenir dont nous verrons un peu plus loin le prix . Cet algorithme génère, l'une après l'autre, toutes les listes possibles des n sommets du graphe G = (V, E). Pour chaque liste L = [u1 , . . . , un ], il teste si elle correspond ou non à un chemin de G, c'est-à-dire s'il y a une arête entre u1 et u2 , et une arête entre u2 et u3 , etc., et une arête entre un−1 et un . Si c'est le cas, L contient un chemin hamiltonien, donc l'algorithme répond OUI (il peut aussi fournir L). Si ce n'est pas le cas, il examine la prochaine liste. Si après l'examen de toutes les listes possibles, aucune ne correspond à un chemin hamiltonien, alors G n'en contient pas. L'algorithme répond donc NON.
ILLUST RAT ION Appliquons cet algorithme sur le graphe de la gure 14.1. Considérons la liste [1, 2, 3, 4]. Elle ne représente pas un chemin hamiltonien car il n'y a pas d'arête entre 1 et 2. Continuons et étudions la liste suivante [1, 2, 4, 3]. Ici encore, il n'y a pas d'arête entre 1 et 2. La liste suivante est [1, 3, 2, 4]. Cette liste représente un chemin hamiltonien car les trois arêtes 1, 3, 3, 2 et 2, 4 sont bien dans le graphe. On peut donc stopper et répondre OUI.
Figure 14.1: Cherchons un chemin hamiltonien
134
Graphes et algorithmes
Évaluons la complexité. Dans le meilleur des cas, avec de la chance, la première liste peut être la bonne. Le résultat est obtenu alors très rapidement. En revanche, si G n'a pas de chemin hamiltonien, cet algorithme ne va répondre NON qu'après avoir examiné toutes les listes possibles. Or si G a n sommets, il y a n × (n − 1) × · · · × 2 × 1 listes (le premier élément de la liste est n'importe lequel des n sommets ; le deuxième est n'importe lequel des n − 1 qui restent ; le troisième est n'importe lequel des n −2 sommets non encore pris en première ou deuxième position, etc.). Cette valeur n × (n − 1) × · · · × 2 × 1 est notée plus simplement n!. Par exemple 4! = 4×3×2×1 = 24. Lorsqu'une liste est créée, il faut aussi vérier si elle correspond ou non à un chemin hamiltonien. Cela va demander un certain nombre d'opérations qui vont être fonction de la manière dont le graphe est stocké en mémoire. Pour être plus concret, imaginons que l'on ait une machine capable de construire et tester nblps listes par seconde. Évaluons le temps total d'examen de ces n! listes en fonction de diérentes valeurs de ce paramètre nblps qui représente donc, de manière très simpliée, la puissance de l'ordinateur sur lequel cette méthode pourrait être exécutée. Dans la suite, nous supposerons que dans une année il y a 60 × 60 × 24 × 365 = 31 536 000 secondes. Nous ne distinguerons pas les années bissextiles, l'idée étant simplement d'avoir des ordres de grandeur. Avec n = 50 (ce n'est pas un très gros graphe) et nblps = 10 000 000 (une machine capable de tester dix millions de listes par seconde), il faudra 96 442 456 880 000 000 000 000 000 000 000 000 000 000 000 000 000 ans avant d'obtenir la réponse. Avec une machine largement plus puissante, nblps = 100 000 000 000 000 000 000, il faut encore 9 644 245 688 000 000 000 000 000 000 000 000 000 ans. Avec n = 30 et nblps = 100 000 000 000 000 000 000, il faut 84 111 ans de calculs. Toutes ces durées sont gigantesques, très largement au-delà de l'espérance de vie humaine et de la durée de fonctionnement des ordinateurs. Le message de cette partie est donc : attention aux solutions trop simples, elles peuvent, parfois, vous conduire directement dans le mur. Bien sûr, on peut tenter de raner cet algorithme trop brut en remarquant par exemple que toute liste L et sa liste inversée représentent le même chemin hamiltonien potentiel. Inutile de les tester toutes les deux. Cela ne divise l'espace de recherche que de moitié. À la vue des durées exprimées juste avant, il est clair que cela ne va pas améliorer signicativement les choses. On peut imaginer d'autres ranements, voire d'autres algorithmes totalement diérents. Il en existe mais aucun ne permet de répondre au problème en faisant un nombre polynomial d'étapes quel que soit le graphe en entrée. Les algorithmes d'approximation. Le problème du chemin hamiltonien est un problème de décision : la réponse attendue est soit OUI, soit NON. Ce qui va nous intéresser dans plusieurs des chapitres à venir, ce sont des problèmes d'optimisation qui se présentent sous la forme générique suivante : étant donné un graphe (plus d'autres informations éventuelles), construire telle ou telle chose dans ce graphe, qui minimise une certaine mesure. Nous avons vu au chapitre 5 un tel problème : étant donné un graphe connexe pondéré G = (V, E), construire un arbre couvrant T de poids minimal. Il se trouve que l'algorithme de Prim permet de construire ecacement un
14. Des problèmes très diciles à résoudre
135
tel arbre. Le problème de l'arbre couvrant de poids minimal n'est pas un problème dicile. Mais une simple généralisation de ce problème, étudiée au chapitre 19, devient dicile : étant donné un graphe G = (V, E) connexe pondéré et M un sous-ensemble de sommets de G, construire un arbre dans G contenant tous les sommets de M (dit arbre de Steiner de M ) de poids minimal. Dans cette version, l'objectif n'est plus nécessairement de couvrir par un arbre tous les sommets du graphe, mais seulement une sous-partie (l'algorithme de Prim ne permet de résoudre ce problème que dans le cas particulier où M = V ). Personne ne sait construire un tel arbre T ∗ optimal. Nous allons contourner le problème en proposant au chapitre 19 un algorithme ecace (de complexité polynomiale) qui ne construit pas l'arbre optimal T ∗ de poids w(T ∗ ), mais un arbre T , de poids w(T ), qui couvre lui aussi M et pour lequel on arrive à prouver que : w(T ) ≤ 2w(T ∗ ). Cette dernière inégalité garantit que le poids de l'arbre T construit est toujours d'au plus deux fois le poids optimal, jamais plus. w(T ) est compris entre w(T ∗ ) et 2w(T ∗ ). L'algorithme peut parfois construire la solution optimale et parfois en être plus éloigné, mais la solution n'est jamais arbitrairement loin de la solution optimale ; il s'agit d'un point important. Cette valeur 2 est appelée le rapport (ou facteur) d'approximation . Plus une méthode a un petit facteur d'approximation, moins l'incertitude est grande sur l'intervalle dans lequel la solution construite se trouve. Les algorithmes d'approximation sont un sujet de recherche actuellement actif et plusieurs projets universitaires ont vu le jour sur ce thème. Ils orent un espoir de mettre au point des algorithmes (donc des logiciels) qui ont à la fois une complexité acceptable et qui garantissent la qualité des résultats obtenus. Les solutions qu'ils proposent peuvent aussi servir de point de départ à des méthodes d'améliorations heuristiques (ce point est évoqué au chapitre 23). Car comme nous l'avons illustré plus haut, vouloir résoudre un problème dicile de manière exacte est actuellement totalement hors de notre portée.
15
Colorions les graphes
Dans ce chapitre, notre objectif va être de colorier chaque sommet d'un graphe G = (V, E) de telle manière que deux sommets voisins n'aient pas la même couleur, ce qui est appelé une coloration propre. Il en existe toujours une ; il sut de donner une couleur diérente à chaque sommet. Mais en général, il est possible d'utiliser moins de couleurs. Par exemple, la gure 15.1 (b) présente une coloration propre du graphe de la gure 15.1 (a) utilisant quatre couleurs (alors qu'il a sept sommets). La coloration de la gure 15.1 (c) en utilise une de moins. Vériez que chaque arête a bien ses deux extrémités de couleurs diérentes. Ce sont bien deux colorations propres du graphe de la gure 15.1 (a). D'après vous, est-ce que ce graphe a une coloration propre utilisant moins de trois couleurs ? Réponse un peu plus loin.
Figure 15.1: (a) Un graphe. (b) Une coloration propre à quatre couleurs. (c) Une coloration propre à trois couleurs.
138
Graphes et algorithmes Les couleurs elles-mêmes ne sont pas importantes. Coloration optimale.
On pourrait inverser le bleu et le rouge, ou utiliser du violet à la place du jaune. Cela n'a aucune importance. Seul compte le nombre de couleurs diérentes utilisées. Une coloration propre d'un graphe G utilisant le nombre minimal de couleurs sera dite optimale. Personne ne sait construire une coloration optimale d'un graphe quelconque en un nombre raisonnable (polynomial) d'étapes. C'est un problème dicile (au sens du chapitre 14) dont la résolution permettrait la création de plannings optimaux. Nous illustrerons cette application pratique après avoir étudié un algorithme dont l'analyse va nous donner une borne intéressante sur le nombre minimal de couleurs. Mais avant cela, faisons le lien avec ce qui a déjà été vu dans ce livre.
Un cas particulier facile. Si vous avez lu l'intégralité du chapitre 3, vous y avez découvert un algorithme ecace pour déterminer si un graphe peut (ou non) être colorié avec seulement deux couleurs (partie sur les graphes bipartis ). La condition est : G a une coloration propre à deux couleurs si, et seulement si, il ne contient aucun cycle avec un nombre impair de sommets. Ce résultat permet de bien cerner ce cas particulier. Il permet parfois d'en dire un peu plus. Le graphe de la gure 15.1 (a) contient un cycle de longueur impaire, celui composé des sommets 4, 5 et 3. Il est donc impossible de colorier ce cycle avec seulement deux couleurs (sinon deux sommets voisins vont avoir la même couleur). Le graphe G dans son ensemble n'a donc pas de coloration propre à deux couleurs. La coloration de la gure 15.1 (c), qui utilise trois couleurs, est donc optimale. Le cas très particulier des graphes complets. Comme le problème général est dicile, il n'y a pas d'algorithme ecace construisant une coloration optimale pour n'importe quel graphe. C'est le problème dans sa globalité qui est dicile. Mais l'étude de familles particulières de graphes peut permettre d'en déterminer des colorations optimales. Faisons-le pour les graphes complets (chaque sommet est voisin de tous les autres).
Figure 15.2: Un graphe complet. Toute coloration propre doit utiliser cinq couleurs Dans un tel graphe, chaque sommet doit impérativement avoir une couleur diérente des autres sinon la coloration ne sera pas propre. Examinez le graphe complet à
15. Colorions les graphes
139
cinq sommets de la gure 15.2. Si vous le coloriez avec moins de cinq couleurs, alors deux sommets, par exemple 4 et 3 pour xer les idées, vont avoir la même couleur et comme le graphe est complet, ils sont voisins. Cette arête 4, 3 aura donc ses deux extrémités de la même couleur. Toute coloration de ce graphe à moins de cinq couleurs n'est donc pas propre.
Quel est le nombre optimal de couleurs ?
Dans certains graphes, deux couleurs susent (les graphes bipartis). Si le graphe ne contient aucune arête, une seule couleur sut, mais c'est un cas vraiment très spécial. À l'autre extrémité du spectre, certains graphes (complets) ont besoin d'autant de couleurs qu'il y a de sommets. Il serait agréable d'avoir une jolie formule qui donne ce nombre optimal de couleurs. On sait juste qu'il peut varier entre 1 et le nombre n de sommets du graphe. Il est possible d'être un peu plus précis grâce à l'analyse d'un algorithme.
L'algorithme de liste : présentation.
Nous allons présenter un algorithme pour créer une coloration propre d'un graphe G = (V, E) à n sommets. Préparons donc n couleurs (on ne les utilisera peut-être pas toutes) et numérotons-les : couleur 1, couleur 2, etc., couleur n (les couleurs elles-mêmes n'ont pas d'importance, seuls leurs numéros sont importants). Considérons maintenant une liste quelconque L des n sommets de G. Traitons chaque sommet de G dans l'ordre de cette liste. Au début, aucun sommet n'est colorié. La règle de coloration du sommet courant u est la suivante : u prend la plus petite couleur parmi celles non utilisées par ses voisins déjà coloriés.
ILLUST RAT ION Illustrons cet algorithme sur le graphe de la gure 15.3 à sept sommets.
Figure 15.3: Un graphe Les couleurs et leur numéro vont être : rouge 1, bleu 2, vert 3, jaune 4, etc. Pour colorier, utilisons la liste L = [1, 2, 3, 4, 5, 6, 7]. Pour faciliter les choses, la gure 15.4 représente les sommets alignés dans l'ordre de L (les arêtes restent les mêmes).
Graphes et algorithmes
140
Figure 15.4: Le graphe de la gure 15.3 représenté suivant la liste [1, 2, 3, 4, 5, 6, 7] Le sommet 1 prend la plus petite couleur, rouge 1. Le sommet suivant 2 ne peut pas prendre cette même couleur car il est voisin de 1 qui est déjà colorié en rouge 1. Il prend donc celle de plus petit numéro non utilisée par son (seul) voisin déjà colorié (sommet 1) : bleu 2. Le sommet 3 ne peut pas prendre la couleur du sommet 1. La plus petite couleur qu'il peut prendre est, comme le sommet 2, la couleur bleu 2. La gure 15.5 donne le résultat de ces trois premières étapes.
Figure 15.5: Début de la coloration Le sommet 4 est ensuite examiné. Son seul voisin qui a déjà une couleur est 2. Il prend donc la couleur de plus petit numéro non pris pas ses voisins, c'est-à-dire rouge 1. Le sommet 5 est voisin de 1 et de 2. Il ne peut donc prendre aucune de leurs deux couleurs. Il est obligé de prendre la couleur non encore utilisée vert 3. La gure 15.6 donne le résultat de ces deux étapes.
Figure 15.6: Suite de la coloration Le sommet 6 prend alors la couleur rouge 1. Le sommet 7 est maintenant traité. Il a des voisins de chacune des couleurs rouge 1, bleu 2 et vert 3. Il est donc obligé d'utiliser la couleur non encore utilisée jaune 4. Le résultat nal est représenté à la gure 15.7.
Figure 15.7: Fin de la coloration
15. Colorions les graphes
141
L'utilisation de cette liste particulière [1, 2, 3, 4, 5, 6, 7] conduit à une coloration propre à quatre couleurs (vériez qu'elle est bien propre, c'est-à-dire que chaque arête a ses deux extrémités de couleurs diérentes). Essayons avec une autre liste, par exemple [3, 5, 4, 6, 2, 1, 7]. La gure 15.8 représente toujours le même graphe, mais avec les sommets triés dans l'ordre de cette nouvelle liste (toujours pour simplier la lecture du déroulement de l'algorithme). On rappelle les couleurs : rouge 1, bleu 2, vert 3, jaune 4, etc. Dans ce cas, la coloration n'utilise plus que trois couleurs. La liste a bien sûr un impact sur le nombre de couleurs qui vont être utilisées.
Figure 15.8: La coloration obtenue en utilisant la liste [3, 5, 4, 6, 2, 1, 7]
L'algorithme de liste : analyse. La première question à se poser est : est-ce que cet algorithme construit toujours une coloration propre ? Oui car à chaque étape, le sommet u traité prend nécessairement une couleur diérente de ses voisins déjà coloriés. Avec la règle de coloration, chaque voisin de u non encore traité prendra (plus tard, lorsqu'il sera traité) une couleur diérente de celle de u. Ainsi chaque sommet u a une couleur diérente de ses voisins. La coloration est donc propre. Remarquons que si un sommet u a d voisins dans le graphe, le numéro de la couleur que lui aecte l'algorithme sera toujours au plus d + 1. En eet, au moment où le sommet u est traité, au plus d de ses voisins ont déjà une couleur (ceux qui ont déjà été traités avant u). Examinons les deux cas qui peuvent se présenter. 1. Toutes les couleurs entre 1 et d sont déjà prises par les voisins de u. La règle de coloration donne la couleur d + 1 à u (c'est le cas pour le sommet 7 dans l'exemple précédent, voir gure 15.7). 2. Une des couleurs de numéro entre 1 et d n'est prise par aucun voisin de u (parce que tous les d voisins n'ont pas encore été traités ou parce que deux voisins de u ont la même couleur). Dans ce cas, le sommet u reçoit la plus petite couleur non encore attribuée, qui est donc une couleur dont le numéro est au plus d. Dans l'exemple précédent, c'est le cas du sommet 6 qui a comme voisins l'un de couleur bleu 2 et l'autre de couleur vert 3 (le troisième n'a pas encore de couleur au moment où 6 est traité). Il prend donc la couleur rouge 1 (voir gure 15.7) dont le numéro (1) est plus petit que 2 + 1 = 3 (car le sommet 6 est de degré d = 2). Dans tous les cas, un sommet ayant d voisins reçoit une couleur dont le numéro est au plus d + 1. Cet algorithme permet donc de colorier un graphe G avec au plus ΔG + 1 couleurs où ΔG est le degré maximal de G, c'est-à-dire le plus grand degré
142
Graphes et algorithmes
de G. Dans l'exemple précédent, la coloration suivant la liste [1, 2, 3, 4, 5, 6, 7] utilise quatre couleurs alors que ΔG = 4. L'analyse de l'algorithme permet de montrer que quelle que soit la liste, le nombre de couleurs utilisées sera d'au plus ΔG + 1. Cela montre au passage que tout graphe G admet une coloration propre utilisant au plus ΔG + 1 couleurs. Attention, cela ne dit pas que toute coloration propre utilise au plus ΔG + 1 couleurs. Par exemple, une coloration du graphe de la gure 15.3 qui donne une couleur diérente par sommet est bien propre, mais utilise sept couleurs en tout alors que ΔG = 4. Le résultat de l'analyse dit qu'il existe au moins une coloration propre à au plus ΔG + 1 couleurs. Il dit aussi comment en construire une (en appliquant l'algorithme). Nous avons vu un peu plus haut dans le chapitre le cas des graphes complets à n sommets (voir la gure 15.2 pour mémoire) pour lesquels il faut nécessairement utiliser n couleurs pour avoir une coloration propre. Cela est-il cohérent avec le résultat précédent ? Calculons le degré maximal d'un tel graphe complet à n sommets. Chaque sommet u a exactement n − 1 voisins (les n − 1 autres sommets du graphe sont voisins de u). Ainsi ΔG = n − 1 et ΔG + 1 = n. Dans le cas particulier des graphes complets, la borne de ΔG + 1 couleurs est atteinte. Il faut exactement ΔG + 1 couleurs. Cela n'est pas en contradiction avec ce qui précède, c'est simplement un cas particulier où le au plus devient égal .
Application de la coloration à la création de plannings.
La coloration de graphes (la version étudiée ici ou des variantes) intervient dans plusieurs problèmes concrets. Nous allons en examiner un ici, en le simpliant. Imaginons qu'une entreprise décide d'organiser des formations pour ses salariés sur divers thèmes comme Initiation à la comptabilité analytique , Finance et mécénat , Initiation à la gestion de projets , Apprentissage des outils bureautiques , Introduction à la théorie des graphes , etc. Les intitulés des thèmes eux-mêmes ne nous intéressent pas beaucoup. En revanche, leur nombre est important. Disons qu'il y a en tout n thèmes. Chacun sera enseigné lors d'un cours. Chaque cours a la même durée, quel que soit le thème enseigné. Pour xer les idées, disons que chaque cours dure une heure. Supposons aussi qu'il y a un enseignant propre à chaque thème et que l'ordre dans lequel les cours seront donnés n'a pas d'importance. Donnons un nom de code à chaque cours/thème : C1 , C2 , . . . , Cn . L'entreprise a loué un bâtiment composé de nombreuses salles indépendantes, toutes très grandes. Il n'y a aucun problème de place. L'objectif va être d'organiser la session des n cours sur une période de temps la plus courte possible. Potentiellement, la session peut durer une seule heure si tous les cours peuvent avoir lieu en même temps (il y a assez de salles et d'enseignants). Mais cela est impossible si certains salariés doivent suivre plusieurs cours. Par exemple, Patrick Dupond doit suivre le cours C4 , le cours C6 et le cours C7 . Il est donc impossible que deux de ces trois cours aient lieu en même temps car sinon M. Dupond ne pourra pas assister aux trois. Cela pose des contraintes. La liste des cours que chaque salarié doit suivre est connue. Les k salariés seront désignés maintenant par un numéro s1 , s2 , . . . , sk . La liste des cours que le salarié si
15. Colorions les graphes
143
doit suivre est notée Li . Donnons un exemple pour xer les idées. Imaginons qu'il y ait n = 8 cours C1 , C2 , . . . , C8 et qu'il y ait k = 12 salariés s1 , s2 , . . . , s12 avec les listes suivantes : L1 = [C2 , C8 ], L2 = [C2 , C5 , C6 ], L3 = [C1 , C3 , C4 ], L4 = [C1 , C2 , C7 ], L5 = [C1 , C2 , C6 ], L6 = [C1 , C2 , C7 ], L7 = [C1 , C4 , C6 ], L8 = [C4 ], L9 = [C1 , C3 ], L10 = [C2 , C6 ], L11 = [C1 , C2 , C7 ], L12 = [C8 ]. Dans cet exemple, on peut remarquer que deux salariés ont la même liste de cours : L6 = [C1 , C2 , C7 ] et L11 = [C1 , C2 , C7 ], ce qui n'est pas interdit. Le salarié s12 n'a qu'un cours à suivre. Dans cet exemple, aucun salarié n'a plus de trois cours à suivre. On pourrait en conclure un peu hâtivement que la session peut se dérouler en seulement trois heures. Il n'en est rien.
Construction du graphe des contraintes.
Chaque cours Ci sera représenté par un sommet Ci. Une arête est placée entre deux sommets Ci et Cj si (et seulement si) une personne doit assister aux deux cours Ci et Cj . Par exemple, on placera une arête entre les sommets C2 et C8 car le salarié s1 doit suivre ces deux cours. De même, on placera une arête entre C1 et C2 car s4 doit les suivre tous les deux. C'est le cas aussi de s11 qui doit les suivre tous les deux. À partir du moment où au moins un salarié doit suivre deux cours, on place une arête entre les deux sommets associés à ces deux cours. En faisant cela systématiquement avec les listes précédentes, le graphe obtenu est celui de la gure 15.9.
Figure 15.9: Le graphe des contraintes Une arête entre deux cours Ci et Cj indique donc que ces deux cours ne peuvent pas avoir lieu en même temps . C'est une contrainte qui doit être respectée. Notons au passage que le graphe des contraintes n'est pas forcément connexe (celui donné en exemple à la gure 15.9 est connexe, mais si l'entreprise a plusieurs catégories de personnels qui doivent suivre des formations distinctes, ce ne sera pas le cas). Essayons maintenant de déterminer combien d'heures va durer la session (tous les cours). Le graphe de la gure 15.9 contient un graphe complet à quatre sommets : C1, C2, C6 et C7. La session durera donc au moins 4 heures car chacun de ces quatre cours doit être placé à un horaire diérent des trois autres.
Un créneau horaire = une couleur dans le graphe.
Imaginons que la session débute à 8 h 00. Le matin, il y a quatre créneaux d'une heure (de 8 h 00 à 12 h 00) et l'après-midi aussi (de 14 h 00 à 18 h 00 par exemple). Si cela ne sut pas,
144
Graphes et algorithmes
on recommence le lendemain, puis le surlendemain, etc. On va numéroter ces créneaux d'une durée d'une heure chacun : H1, H2... Notre objectif est de déterminer ici un planning dans lequel tous les cours sont dispensés et qui utilise le plus petit nombre possible de créneaux.
Faire une coloration propre à partir d'un planning. Examinons ce qu'impose le fait de placer plusieurs cours dans un même créneau horaire, par exemple 10 h11 h. Ces cours ayant lieu exactement au même moment, aucun d'eux ne doit partager une arête avec aucun des autres de ce même créneau. D'un planning qui respecte les contraintes, on peut tirer une coloration propre en donnant à chaque créneau une couleur qui lui est propre et en coloriant tous les cours qui ont lieu dans ce créneau avec cette couleur. Faire un planning à partir d'une coloration propre. Imaginons que l'on ait une coloration propre du graphe G des contraintes. Dans ce cas, tous les cours qui ont la même couleur peuvent être donnés dans le même créneau horaire. La durée de la session correspond alors exactement au nombre de couleurs de la coloration propre. Il faut bien sûr ensuite décider de l'heure de début de chaque créneau. Mais cela n'est pas dicile. On peut décider arbitrairement par exemple que les cours de couleur 1 auront lieu dans le créneau H1 , ceux de couleur 2 dans le créneau H2 , etc. ILLUST RAT ION Donnons un exemple de la correspondance entre coloration et planning. Reprenons le graphe des contraintes de la gure 15.9. Colorions-le grâce à la méthode de liste vue plus haut en choisissant la liste [C1, C2, C3, C4, C5, C6, C7, C8] et en prenant les couleurs suivantes : rouge 1, bleu 2, vert 3, jaune 4. Le résultat de la coloration par cette liste est donné à la gure 15.10. Un planning possible correspondant à cette coloration propre est aussi donné. On peut passer de l'un à l'autre et de l'autre à l'un.
Figure 15.10: Une coloration propre et un planning associé
15. Colorions les graphes
Conclusion.
145
La coloration est un problème très important de la théorie des graphes dont la maîtrise pourrait aider à résoudre des problèmes pratiques (comme la création de plannings optimaux). De nombreux travaux y sont encore consacrés, des variantes sont étudiées. Ces dernières sont souvent des généralisations du problème de base vu ici et sont donc au moins aussi diciles.
16
Des couplages
Dans ce chapitre, nous allons étudier un problème assez simple à comprendre et (très) dicile à résoudre de manière exacte. Nous verrons aussi que la situation n'est pas totalement désespérée. Avant d'aller plus loin, voici quelques notions préliminaires.
Un couplage. La taille d'un couplage. (Si vous savez déjà ce qu'est un couplage, vous pouvez sauter ce paragraphe de rappel.) Considérons un graphe G = (V, E). Un couplage M de G est un sous-ensemble d'arêtes du graphe, deux à deux disjointes, c'est-à-dire n'ayant aucun sommet commun. La taille de ce couplage est le nombre d'arêtes qu'il contient. Dans le graphe de la gure 16.1, les ensembles suivants d'arêtes sont des couplages. M1 = {1, 3, 5, 6}. Ces deux arêtes ne partagent aucune sommet. M1 est bien un couplage, de taille 2 (il contient deux arêtes). M2 = {2, 8, 4, 3}. Ces deux arêtes ne partagent aucun sommet. Attention, les dessins de ces deux arêtes se croisent mais elles sont bien disjointes car elles contiennent des sommets (ici 2, 8, 4 et 3) diérents. Ce couplage est lui aussi de taille 2. M3 = {1, 2, 3, 5, 6, 7} est un couplage de taille 3. Un ensemblene contenant aucune arête ( ∅) ou une seule arête est aussi un couplage.
Figure 16.1: Un graphe dans lequel il y a de multiples couplages
Graphes et algorithmes
148
L'ensemble {1, 2, 3, 5, 6, 7, 3, 4} n'est pas un couplage car deux arêtes ( 3, 5 et 3, 4) partagent un même sommet (le sommet 3), elles ne sont pas disjointes.
Les couplages maximaux.
Un couplage M du graphe G = (V, E) est dit (pour être complet, il faudrait plutôt dire ) s'il n'est pas possible de le faire grossir . En lui ajoutant n'importe quelle nouvelle arête de G, ce n'est plus un couplage. Dit autrement : pour toute arête u, v de G qui n'est pas dans M , alors M plus l'arête u, v n'est plus un couplage. Dans le graphe de la gure 16.2, M = {1, 2, 3, 4, 5, 6} est clairement un couplage maximal car tous les sommets de G sont déjà dans G. Toute arête de G ajoutée à M touchera au moins une arête déjà présente dans M . Mais M = {2, 3, 5, 6} est un couplage maximal. En eet, essayez d'ajouter une arête, n'importe laquelle, à M . Elle aura nécessairement un sommet commun avec au moins une arête déjà dans M . C'est donc bien aussi un couplage maximal. Cet exemple montre qu'un couplage maximal est un couplage qui ne peut plus grossir, mais ce n'est pas forcément le plus gros couplage de G. Certains petits couplages sont maximaux.
maximal
maximal pour l'inclusion
aussi
Figure 16.2: {1, 2, 3, 4,5, 6} et {1, 2,5, 6} sont deux couplages maximaux
Construction d'un couplage maximal : un problème facile. Étant donné un graphe G = (V, E), comment construire un couplage maximal ? Avant de lire la suite, essayez de trouver par vous-même une méthode pour le faire, en vous aidant d'un exemple éventuellement. La méthode est très simple, voici comment faire. Au départ, le couplage M ne contient rien (aucune arête). À chaque étape, ajouter à M une nouvelle arête qui ne partage aucun sommet avec les arêtes déjà dans M . Stopper la construction dès qu'il n'est plus possible d'ajouter une nouvelle arête. Le résultat de l'algorithme est alors l'ensemble M ainsi construit. ILLUST RAT ION Illustrons cela sur le graphe de la gure 16.2. Débutons avec M vide. Comme M est vide pour l'instant, on peut choisir n'importe quelle arête, par exemple 2, 3. Maintenant M = {2, 3}. À la deuxième étape, on doit choisir une arête disjointe de celle qui est dans M , c'est-à-dire 2, 3. Cela exclut de choisir 1, 2, 2, 4, 3, 4. Reste donc 4, 6 ou 5, 6. Choisissons par exemple l'arête 4, 6, ce qui donne M = {2, 3, 4, 6}. Il devient maintenant impossible d'ajouter une nouvelle arête. L'algorithme stoppe. Le résultat de ces choix successifs particuliers est le couplage M = {2, 3, 4, 6} dont
16. Des couplages
149
on peut facilement vérier qu'il est bien maximal. En faisant d'autres choix, le couplage aurait pu être diérent. Par exemple, choisir 1, 2, puis 3, 4, puis enn 5, 6 aurait donné {1, 2, 3, 4, 5, 6}. Dans tous les cas (quel que soit le graphe), cet algorithme construit un couplage maximal (on peut le démontrer assez facilement). Bien sûr, les choix faits à chaque étape vont avoir un impact sur ce qui est construit, mais le résultat sera toujours un couplage dont la propriété est d'être maximal.
Construction d'un couplage maximal de taille minimale : un problème dicile. Comme cela a été illustré plus haut, un graphe peut contenir des cou-
plages maximaux de diverses tailles. Cherchons ici celui contenant un nombre minimal d'arêtes. Attention, ce n'est pas le plus petit couplage mais le plus petit couplage maximal. Ce n'est pas la même chose. Parmi tous les couplages qui ne peuvent pas être augmentés, c'est celui qui contient le plus petit nombre d'arêtes. Construire un couplage maximal de taille minimale est un problème dicile, au sens vu au chapitre 14. Actuellement, personne ne sait le faire avec un algorithme ayant un nombre d'étapes élémentaires raisonnable (c'est-à-dire un polynôme en la taille du graphe). Voilà donc un problème assez simple à exprimer et à comprendre, mais très dicile à résoudre de manière exacte.
Tous les couplages maximaux d'un graphe ont des tailles assez proches les unes des autres. Considérons un graphe G = (V, E) et deux couplages maxi-
maux quelconques M1 de taille t1 et M2 de taille t2 . Supposons que t1 ≤ t2 . Nous allons montrer que M2 ne peut pas être arbitrairement plus grand que M1 (par exemple, il est impossible que t2 ≥ 100t1 ). M2 a une taille au plus deux fois plus importante que celle de M1 , c'est-à-dire t2 ≤ 2t1 .
Zone orange Montrons cette propriété. M1 étant maximal, toute arête de G a au moins une extrémité commune avec une arête de M1 (sinon il serait possible d'ajouter une nouvelle arête à M1 tout en conservant un couplage). C'est donc aussi le cas pour n'importe quelle arête e de M2 . Notons alors u, un sommet qui est dans e de M2 et dans une arête e de M1 . Ce sommet u ne peut pas être dans une autre arête de M1 (sinon M1 ne serait pas un couplage). Chaque arête e de M2 peut ainsi être associée à un sommet (d'une arête) de M1 (u) qui n'est associé à aucune autre arête de M2 . Le nombre d'arêtes t2 de M2 est donc inférieur ou égal au nombre de sommets qui sont des extrémités des arêtes de M1 , c'est-à-dire exactement 2t1 , d'où t2 ≤ 2t1 . Ce petit résultat étant valable pour tout couplage maximal M1 , il est vrai aussi pour M0 , un couplage maximal de taille minimale t0 . Cela montre qu'en construisant n'importe quel couplage maximal M de G de taille t, on a la garantie que t ≤ 2t0 . Il est donc impossible d'avoir des couplages maximaux arbitrairement grands par rapport à M0 .
Graphes et algorithmes
150
Dans certains graphes,
.
t = 2t0 Il vient d'être établi que t ≤ 2t0 . Montrons que dans certains graphes, t = 2t0 . La gure suivante donne une telle situation.
En pointillés épais, les arêtes d'un couplage maximal de taille 4 et en pointillés ns, les arêtes d'un autre couplage maximal de taille 2 seulement. L'algorithme brut qui consiste à prendre n'importe quel couplage maximal a donc un facteur d'approximation 2. Cet algorithme étant rapide, rien n'empêche, en pratique, de l'exécuter plusieurs fois en faisant des choix aléatoires et en mémorisant celui de taille minimale trouvé au fur et à mesure. Ici la situation est particulière : n'importe quelle solution (couplage maximal) a une taille qui est au plus deux fois celle optimale. L'univers des couplages maximaux est donc assez étroit (les solutions sont de tailles proches les unes des autres) mais le problème consistant à trouver celui de taille minimale reste malgré tout dicile.
Conclusion.
Les problèmes concernant les couplages sont nombreux. Certains peuvent être résolus avec des algorithme ecaces (parfois assez compliqués à décrire). Au chapitre 10, l'algorithme présenté permet de trouver un couplage parfait (chaque sommet est l'extrémité d'une arête du couplage) stable (n'engendrant pas de frustration ) dans un graphe Kn,n (biparti complet à 2n sommets). Un autre algorithme, vu au chapitre 11, permet de construire un couplage de taille maximale dans un graphe biparti, en utilisant un réseau à ots. D'autres problèmes, comme celui présenté ici, sont diciles et ne peuvent pas être résolus de manière exacte ecacement (du moins à ce jour).
17
Une petite couverture
Dans ce chapitre, nous allons étudier un autre problème, la couverture de graphe, simple à comprendre et dicile à résoudre de manière exacte. Heureusement, nous verrons comment des solutions approchées peuvent être construites de manière ecace. Ce sera aussi l'occasion de montrer qu'il faut se méer des méthodes trop simples : nous détaillerons un algorithme intuitif qui semble a priori performant, mais qui donne parfois de très mauvaises solutions. La n du chapitre sera consacrée à une variante (ajout d'une contrainte de connexité) un peu plus compliquée.
Une couverture d'un graphe. Une couverture optimale. Soit G = (V, E) un graphe quelconque. On dit qu'un ensemble S de sommets de G couvre une arête u, v si u ou v est dans S (les deux peuvent être dans S ). L'ensemble S est une couverture (le terme anglais est vertex cover ) de G si chaque arête de G est couverte par S . C'est donc une couverture de toutes ses arêtes par certains de ses sommets. Sa taille est le nombre de sommets qu'elle contient. Remarquez que tout graphe a au moins une couverture qui est l'ensemble de tous ses sommets. Dans ce cas, chaque arête est couverte (elle est même couverte par ses deux extrémités). Notre objectif ici est de chercher une couverture de taille minimale que l'on nommera couverture optimale de G. ILLUST RAT ION
Examinons un exemple. Considérons le graphe G de la gure suivante. Voici quelques couvertures de G.
S1 = {1, 2, 3, 4},
l'ensemble de tous les sommets. S1 est de taille 4.
Graphes et algorithmes
152
S2 = {1, 4}, de taille 2. Notez que chaque arête de G est bien couverte (par au moins une de ses deux extrémités) par un sommet de S2 . L'arête 1, 4 est couverte par 1 et par 4 alors que l'arête 2, 4 n'est couverte que par 4. S2 est une couverture optimale de G car il n'en existe pas de taille 1. S3 = {1, 2, 3}, de taille 3.
Construire une couverture optimale : un problème dicile.
Il a été démontré que construire une couverture optimale est un problème dicile, au sens vu au chapitre 14. Ce problème est essentiellement théorique et fait partie des classiques qui intriguent, car il est très simple à comprendre mais très dur à résoudre de manière exacte. Une application potentielle peut être mentionnée : placer un nombre minimal de gardiens ou de gendarmes à des intersections (sommets) de manière à ce que chaque couloir ou rue (arête) soit surveillé(e). Chaque sommet où est placé un surveillant surveille toutes ses arêtes .
Figure 17.1: Les trois sommets colorés surveillent (couvrent) toutes les arêtes
Explorons une piste. Attention aux idées trop simples.
Avant d'aller plus loin, prenez quelques minutes pour rééchir par vous-même à la manière de construire une couverture de petite taille. N'hésitez pas à dessiner des graphes et à essayer de voir comment vous vous y prendriez. Une idée naturelle qui vient spontanément à l'esprit de beaucoup de gens est la méthode suivante que l'on va nommer MDG (pour Maximum Degree Graph). Il s'agit d'ajouter à la solution courante, à chaque étape, le sommet couvrant le nombre maximal d'arêtes non encore couvertes. Détaillons un peu plus. La solution S est construite sommet par sommet (au départ S = ∅). Le c÷ur de l'algorithme est le suivant. Tant qu'il y a au moins une arête du graphe non encore couverte par S , ajouter dans S un sommet qui couvre le nombre maximal d'arêtes non encore couvertes. Stopper la construction lorsque S couvre toutes les arêtes. La solution produite par cet algorithme est l'ensemble S à ce moment-là. Appliquons cette méthode au graphe de la gure suivante.
17. Une petite couverture
153
La stratégie MDG consiste à choisir en priorité le sommet couvrant le nombre maximal d'arêtes. Dans l'exemple, c'est le sommet 2, qui couvre quatre arêtes. Aucun autre sommet de G n'en couvre autant. La gure suivante représente la situation à cette étape de l'algorithme : 2 a été ajouté dans S (2 est coloré pour le marquer) et ses arêtes sont maintenant couvertes (pour marquer cela graphiquement, elles sont dessinées en pointillés).
Comme il reste des arêtes non encore couvertes par S = {2}, examinons les autres sommets. Le sommet 4, par exemple, ne couvre que deux arêtes non encore couvertes, 4, 6 et 4, 7, l'arête 4, 2 étant déjà couverte (par 2). Le sommet 6 couvre trois arêtes non encore couvertes et c'est le seul sommet qui en couvre autant. Il est donc ajouté dans S qui est maintenant S = {2, 6}. La nouvelle situation est représentée à la gure suivante.
Il reste encore des arêtes non couvertes. Continuons en examinant les sommets non colorés. Le sommet 5 ne couvre aucune arête non encore couverte. En revanche, 1, 3, 4 et 7 en couvrent chacun une. Lequel choisir ? L'algorithme ne dit rien. Vous pouvez choisir le sommet que vous voulez. Par exemple le sommet 7 et ainsi S = {2, 6, 7}. Il faut choisir ensuite soit 1 soit 3. Choisissons 1, par exemple, ce qui donne S = {1, 2, 6, 7}. La situation courante est représentée à la gure suivante. Chaque arête est maintenant couverte. L'algorithme se termine avec S = {1, 2, 6, 7}.
154
Graphes et algorithmes
Cette stratégie gloutonne semble donc fonctionner. On peut montrer qu'elle construit bien une couverture de G quel que soit G. Mais qu'en est-il de sa taille ? Est-elle optimale ? Probablement pas puisque le problème est dicile et que MDG est un algorithme très simple qui a un nombre d'étapes au plus égal au nombre n de sommets dans G. Ce serait trop simple. Mais ce n'est pas une preuve. Comment le prouver ? Il sut d'avoir un contre-exemple, c'est-à-dire ici un graphe dans lequel MDG construit une solution non optimale. En voici un à la gure 17.2. Clairement, MDG va d'abord choisir 1, puis il va choisir aux étapes suivantes un des deux sommets de chacune des trois arêtes 2, 5, 3, 6 et 4, 7. Il va donc construire S = {1, 2, 6, 4} ou S = {1, 3, 4, 5} ou S = {1, 2, 3, 4}, etc., c'est-à-dire toujours une solution de taille 4 avec obligatoirement le sommet 1 (choisi à la première étape) et trois autres sommets. Or, S0 = {2, 3, 4} est une couverture de taille 3 que MDG est incapable de construire.
Figure 17.2: MDG est incapable de trouver une solution optimale ici MDG peut produire des solutions très mauvaises. En réalité, la situation est bien pire que celle présentée juste avant. On peut montrer que dans certains cas, MDG peut construire une couverture dont la taille est beaucoup plus grande que celle d'une couverture optimale. Donnons une idée de ces graphes dans lesquels MDG peut se tromper . En voici un exemple à la gure 17.3. Les six sommets blancs 1, 2, 3, 4, 5, 6 forment une couverture de taille 6, ce qui est optimal ici (nous pourrons le montrer bientôt). Pour faciliter la lecture du graphe, les autres sommets ayant le même nombre de voisins (le même degré ) sont coloriés avec la même couleur. Suivons MDG dans sa construction. Il choisit obligatoirement en premier le sommet 14 qui a six voisins car aucun autre sommet n'en a autant ou plus. À l'étape 2, il choisit obligatoirement le sommet 13 qui a le plus grand nombre d'arêtes
17. Une petite couverture
155
non encore couvertes. Puis il choisit obligatoirement 12. Ensuite, il a le choix entre 10 et 11, mais doit de toute façon les prendre tous les deux, dans un ordre ou dans l'autre. Ensuite, il prend 7, 8, 9 dans un ordre quelconque. À ce moment-là, toutes les arêtes sont couvertes. Le résultat est donc S = {7, 8, 9, 10, 11, 12, 13, 14}, de taille 8.
Figure 17.3: Dans ce type de graphe, MDG construit une couverture de taille supérieure à l'optimal Cet exemple peut être généralisé pour obtenir des diérences de plus en plus grandes entre ce qui est construit et la solution optimale. Le rapport entre la taille de la solution construite et la taille optimale tend vers l'inni lorsque le nombre de sommets tend vers l'inni. Bref, MDG n'est pas un bon algorithme pour garantir que dans toutes les situations, la solution renvoyée est de taille proche de la solution optimale. Cette conclusion peut paraître étonnante car MDG fait le meilleur choix à chaque étape (il inclut le sommet qui couvre le plus d'arêtes non encore couvertes). Pourtant, le résultat nal construit peut être (dans certains cas) désastreux. Il faut donc se méer des idées trop intuitives. Comme MDG n'est pas satisfaisant, essayons d'emprunter d'autres pistes qui vont nous conduire à des résultats garantis.
On ne peut pas résoudre le problème directement ? Empruntons un chemin détourné. Nous n'allons pas chercher ici à résoudre le problème de
manière exacte. Nous voulons un algorithme dont le nombre d'étapes de calculs est raisonnable (comme MDG par exemple) mais dont la taille de la couverture qu'il
156
Graphes et algorithmes
construit est la plus proche possible de celle de la solution optimale (ce que ne permet pas MDG ). C'est-à-dire une méthode dont on puisse prouver qu'elle renvoie une solution proche de l'optimal. Mais pour cela, il faudrait avoir la solution optimale pour la comparer à la solution construite par la méthode. Comment faire ? C'est ce que nous allons expliquer dans ce qui suit.
Les couplages : un lien entre l'optimal et ce que l'on peut construire.
Avant d'aller plus loin, voyons une notion qui va nous permettre de faire une liaison entre ce que l'on sait construire et l'inaccessible optimal. Ce lien repose sur les couplages, déjà rencontrés dans d'autres chapitres. Pour vous éviter de tourner les pages, rappelons la dénition d'un couplage d'un graphe G : c'est un ensemble M d'arêtes de G deux à deux disjointes (ne partageant aucun sommet). Un couplage M est dit maximal s'il est impossible de le faire grossir en lui ajoutant une arête de G qui n'est pas déjà dans M . Dit autrement, pour toute arête u, v de G qui n'est pas dans M , l'ensemble M auquel on ajoute u, v n'est pas un couplage. Un couplage maximal peut facilement être construit avec un algorithme glouton. Les détails se trouvent au chapitre 16 mais il sut ici de savoir qu'il existe. Pour xer les idées, la gure 17.4 présente deux couplages maximaux dans un graphe. La taille (c'est-à-dire son nombre d'arêtes) de l'un est 3 et celle de l'autre est 2.
Figure 17.4: Un graphe et deux couplages maximaux : l'un de taille 3 et l'autre de taille 2
Le lien entre un couplage M et une couverture S . Après cette parenthèse pour rappeler ce qu'est un couplage (maximal ou non), revenons à notre propos : construire une couverture de petite taille. Considérons un graphe G = (V, E) et un couplage M (pas nécessairement maximal) de G. Considérons aussi une couverture S quelconque de G. Comme S couvre toutes les arêtes, elle couvre donc toutes les arêtes de M . Chaque arête u, v de M est donc couverte par u ou par v (ou les deux). Supposons qu'elle soit couverte par u. Cela veut donc dire que u est un sommet de S (u ∈ S ). Or, ce sommet u de S qui couvre u, v ne peut couvrir que cette arête de M , sinon M aurait deux arêtes partageant le même sommet, ce qui est impossible car M est un couplage. Ainsi, à chaque arête e de M est associé (au moins) un sommet de S (un sommet u de S qui couvre e) qui lui est propre (u ne couvre que e dans M ). Le nombre d'arêtes de M , que l'on note ici |M |, ne peut donc pas être plus grand que le nombre de sommets dans S , que l'on note |S|. On obtient ainsi : |M | ≤ |S|.
17. Une petite couverture
157
Toute couverture a donc une taille supérieure ou égale à celle de n'importe quel couplage. Attention, rappelons ici que la taille |S| de S est son nombre de sommets, alors que la taille |M | du couplage M est son nombre d'arêtes. Utilisons immédiatement ce résultat pour montrer que dans le graphe de la gure 17.3, la couverture S = {1, 2, 3, 4, 5, 6}, de taille 6, est bien optimale. Pour cela, il sut de trouver un couplage contenant six arêtes. En voici un : M = {1, 7, 2, 10, 3, 8, 4, 11, 5, 9, 6, 14}.
Construire une couverture avec un couplage maximal.
Une couverture ne peut donc jamais être de taille strictement inférieure à un couplage. Très bien mais après ? Que peut-on faire de ce résultat ? C'est ce que nous allons voir. Voici un algorithme, que nous nommerons ED, très simple. Construire un couplage M de G (grâce à un algorithme ecace vu au chapitre 16. Si vous ne vous en souvenez pas, ce n'est pas très important. Sachez seulement que c'est possible). Extraire l'ensemble S des extrémités des arêtes de M . Cet ensemble S est le résultat de l'algorithme. Pourquoi S , le résultat de ED, serait-il une couverture de G ? Supposons le contraire. Cela veut dire qu'une arête u, v de G n'est couverte par S . L'arête u, v ne partage donc aucune extrémité avec une arête de M . Ajouter u, v à M donne donc un couplage. Or cela contredit le fait que M est un couplage .
maximal
pas
maximal
ILLUST RAT ION Illustrons le fonctionnement de l'algorithme ED grâce aux deux couplages maximaux de la gure 17.4. L'un est composé des trois arêtes 1, 2, 3, 5 et 4, 6. L'extraction des sommets extrémités de ce couplage maximal donne {1, 2, 3, 4, 5, 6}, c'est-à-dire tous les sommets, ce qui est bien une couverture. L'autre couplage maximal n'est composé que des arêtes 2, 5 et 3, 4, qui donne ici la solution {2, 3, 4, 5} de taille 4, qui est bien aussi une couverture.
Comparons la taille de M avec celle d'une couverture optimale. Le c÷ur de l'argument est ici. Notons S0 une couverture (de taille |S0 |) optimale de G. S0 n'est pas connue mais elle existe. Notons S la couverture construite par ED et notons MS le couplage maximal qui a été construit par ED et dont les sommets extrémités ont été extraits pour construire S . La relation |M | ≤ |S| vue plus haut étant valable pour n'importe quelle couverture S et n'importe quel couplage M , elle est aussi valable pour S0 et pour le couplage MS : |MS | ≤ |S0 |.
Or, S étant l'ensemble des extrémités des arêtes de MS qui est un couplage, on a : |S| = 2|MS |.
En combinant, on obtient :
|S| ≤ 2|S0 |.
Graphes et algorithmes
158
ED est un algorithme d'approximation de rapport 2. L'inégalité précédente montre que l'algorithme ED construit toujours une couverture dont la taille ne peut pas être arbitrairement éloignée de celle de S0 . Il procure la garantie analytique, prouvée, que |S| ne sera jamais plus grand que 2|S0 |. Ce facteur 2 est son rapport d'approximation. Attention à une mauvaise interprétation possible de ce résultat. Il ne dit pas que la solution construite par ED est deux fois plus grosse que l'optimale. Il dit qu'elle n'est pas de taille strictement deux fois plus grande. Ce facteur 2 garantit donc que la solution S construite a une taille qui se situe entre |S0 | et 2|S0 |.
Le rapport 2 peut être atteint. Peut-on améliorer l'analyse précédente ? Peut-être qu'en faisant une analyse plus ne de ED, nous pourrions montrer que le rapport d'approximation est en réalité, non pas 2, mais seulement 1, 5 par exemple. Il n'en est (hélas !) rien. Pour montrer que ce rapport de 2 peut être atteint, il sut de trouver un graphe dans lequel |S| = 2|S0 |. Le plus simple est d'étudier le graphe de la gure 17.5. Ici, clairement, S0 = {1} et toute solution construite par ED contient au moins les deux extrémités d'une arête.
Figure 17.5: Ici |S| = 2|S0 | Y a-t-il un algorithme meilleur que ED ? ED garantit un rapport d'approximation de 2. Peut-on faire mieux ? Il faut préciser la question. Vous pouvez vous dire : je peux faire mieux que ED, il sut par exemple d'appliquer ED puis de supprimer de la solution S construite certains sommets qui sont clairement inutiles. Par exemple, sur le graphe de la gure 17.5, si ED construit S = {1, 2}, il sut d'enlever 2 qui est clairement en trop. On obtient d'ailleurs dans ce cas particulier la solution optimale. Reste alors à savoir comment dénir un sommet clairement inutile . Rappelez-vous qu'un algorithme a pour vocation d'être programmé et exécuté par un ordinateur. Ces machines ne voient rien si on ne leur dit pas explicitement où regarder et quoi faire. Peut-être avez-vous une idée précise de ce que vous voulez faire. Vous pourrez sans doute imaginer d'autres variantes, voire d'autres algorithmes avec des principes totalement diérents. Je vous encourage à le faire ! Mais attention, nous traitons ici d'algorithmes d'approximation. Il faut garantir la qualité de la solution construite. Il faut le prouver, il faut une démonstration et pas simplement se contenter d'intuition (même si l'intuition est bien sûr très importante, ne serait-ce que pour décider ce que l'on va essayer de démontrer). Votre algorithme doit aussi faire un nombre raisonnable de calculs (un nombre de pas de calculs qui soit au plus un polynôme en n, le nombre de sommets de G). Tout cela n'est pas simple. Il est actuellement
17. Une petite couverture
159
conjecturé qu'il n'existe pas d'algorithme d'approximation ayant un rapport d'approximation constant strictement inférieur à 2. Quelques algorithmes ont un rapport d'approximation de la forme 2 − f (n), mais lorsque n tend vers l'inni, la fonction f (n) tend vers 0 et ce rapport tend vers 2, il est aussi proche de 2 que l'on veut. En revanche, si vous trouvez un algorithme d'approximation de rapport 1, 97 ou même 1, 99, alors vous aurez montré que la conjecture est fausse.
Un algorithme pour construire une couverture optimale.
Actuellement, personne ne sait construire une couverture optimale en un nombre raisonnable de pas de calculs, c'est-à-dire polynomial en la taille du graphe. Mais comment faire si l'on veut vraiment une solution optimale, sans regarder à la dépense ? Le prix à payer va alors être le temps passé à la construire. Voici un algorithme que nous allons analyser. Une couverture étant un sousensemble des sommets de G, il sut d'examiner chaque sous-ensemble, par ordre croissant de taille. Le premier qui couvre toutes les arêtes de G permet de stopper l'algorithme. Cet ensemble est bien une couverture optimale de G. Essayons cela sur le graphe de la gure suivante.
Construisons et vérions les sous-ensembles des sommets de G par ordre croissant de taille. 1. L'ensemble vide ne contenant aucun sommet n'est pas une couverture de G. 2. Aucun des cinq ensembles contenant un seul sommet n'est une couverture. 3. Aucun des ensembles contenant deux sommets n'est une couverture. Vériez-le. 4. L'ensemble S = {2, 3, 4} est bien une couverture. Comme il n'en existe pas de taille inférieure (on a vérié juste avant), celle-là est optimale. Combien d'ensembles ont été examinés ? 1 ensemble à 0 sommet, 5 ensembles à 1 sommet, 10 ensembles à 2 sommets et 1 ensemble à 3 sommets (si S est le premier ensemble à 3 éléments à être examiné). Cela fait un total de 1 + 5 + 10 + 1 = 17 essais. Comment évaluer le nombre d'essais à faire en général ? Si la taille de la solution optimale est toujours petite alors peut-être que le nombre d'essais sera petit lui aussi. Ce n'est pas le cas. Considérons le graphe complet qui a n sommets et toutes les arêtes possibles. La gure 17.6 en représente un avec sept sommets.
160
Graphes et algorithmes
Figure 17.6: Un graphe complet à sept sommets Dans un tel graphe complet à n sommets, la plus petite couverture est de taille n − 1. En eet, tout sous-ensemble S à au plus n − 2 sommets ne contient pas au moins deux sommets u et v . Or comme le graphe est complet, l'arête u, v ne sera pas couverte par S . L'algorithme décrit ci-dessus teste, sans succès, tous les sous-ensembles jusqu'à une taille de n − 2. Ensuite, le premier sous-ensemble à n − 1 sommets examiné donne une solution optimale qui est renvoyée. Combien d'ensembles sont examinés ? Si n est le nombre de sommets, il y a en tout 2n ensembles, de toutes les tailles entre 0 et n (de ∅ à l'ensemble contenant les n sommets). Or il y a exactement un ensemble contenant tous les sommets et exactement n ensembles en contenant n − 1. En tout, il en examine donc : 2n − n − 1 + 1 = 2n − n. Cela ne vous parle peut-être pas beaucoup. Donnons quelques valeurs numériques, ça sera plus clair. Par exemple, avec n = 7, l'algorithme examine déjà 27 − 7 = 121 ensembles. Si n = 20, il en examine 1 048 556. Si n = 50, il en examine 1 125 899 906 842 574. Imaginons qu'en moyenne, notre ordinateur puisse générer et examiner 1 000 ensembles par seconde. Pour arriver à bout de ces 1 125 899 906 842 574 ensembles, il faudrait un peu plus de 35 000 ans ! Même si la machine était capable d'en traiter 1 000 000 par seconde, il faudrait encore environ 35 ans ! Bref, vous êtes probablement persuadé maintenant que cette méthode exhaustive est vraiment trop lente et que la complexité des algorithmes peut prendre un sens très concret . Entendons-nous bien : ce n'est pas parce que l'algorithme exhaustif précédent n'est pas performant que le problème est dicile. Ce n'est qu'un (mauvais) moyen de le résoudre. Cet algorithme a été présenté pour illustrer le fait qu'examiner toutes les solutions pour ne garder que la meilleure n'est pas toujours une bonne stratégie.
Construire une couverture optimale dans un graphe biparti : attention au piège. Étudions le problème dans une famille restreinte de graphes, les
graphes bipartis (voir le chapitre 3 dans la partie application du parcours en largeur). On rappelle qu'un graphe G = (V, E) est biparti si V peut être découpé en deux ensembles disjoints V1 et V2 (pas forcément de même taille) de telle manière que toute arête de G a une extrémité dans V1 et l'autre dans V2 .
17. Une petite couverture
161
Il serait tentant de dire que dans un graphe biparti où V1 et V2 sont connus, il sut de prendre le plus petit des deux ensembles pour obtenir une couverture optimale. Pour xer les idées, imaginons que V1 a moins de sommets que V2 . V1 est bien sûr une couverture de G. De plus, chaque arête est couverte par seulement une extrémité. Cela plaide donc en faveur de cette solution. Mais cette méthode est trop simple. Elle donne parfois des résultats très mauvais. Considérons par exemple le graphe de la gure suivante. Les sommets de V1 et de V2 sont colorés diéremment. Chaque arête a bien ses deux extrémités de deux couleurs diérentes. Ce graphe est donc bien biparti. Ici la taille de V1 est la même que la taille de V2 . L'ensemble V1 est eectivement une couverture de taille 5. Mais la solution optimale est ici de taille 2 seulement en prenant les sommets 1 et 2. Ici aussi, l'intuition n'est pas bonne conseillère.
Sachez qu'il est possible de construire de manière ecace une couverture optimale dans un graphe biparti en utilisant des ots, de manière un peu similaire à ce qui est fait dans le chapitre 11 pour construire un couplage de taille maximale.
Une petite couverture connexe.
Voici une variante du problème. On se donne toujours un graphe G = (V, E), on veut toujours construire une couverture S de taille la plus petite possible, mais on veut en plus ici qu'elle soit connexe. Cette dernière condition de connexité impose que pour n'importe quels sommets u et v de S , il existe un chemin entre u et v composé uniquement de sommets de S . Dit autrement, en gardant uniquement les sommets de S et les arêtes de G entre sommets de S , le graphe obtenu doit être connexe. Nous allons supposer dans tout le reste de cette partie que le graphe G est lui-même connexe. Avec cette hypothèse, l'ensemble de tous les sommets de G est bien une couverture connexe de G. Bien sûr, ce n'est pas forcément celle qui a la plus petite taille. En trouver une de taille minimale est un problème dicile.
ILLUST RAT ION La gure 17.7 donne un exemple de couverture non connexe et d'une autre connexe du même graphe connexe initial.
Graphes et algorithmes
162
Figure 17.7: Une couverture non connexe et une couverture connexe Ce sont bien deux couvertures (chaque arête est bien couverte). Celle de gauche n'est pas connexe car, par exemple, il n'y a pas de chemin composé uniquement de sommets colorés entre le sommet 1 et le sommet 3. Tout chemin entre 1 et 3 (ou entre 2 et 3 ou entre 1 et 2) contient nécessairement un sommet non coloré. En revanche, la couverture de droite est connexe car en supprimant les sommets non colorés, le graphe obtenu, représenté à la gure 17.8, est bien connexe (c'est un arbre ).
Figure 17.8: La couverture de la gure 17.7 est bien connexe
Un algorithme pour construire une couverture connexe approchée.
Voici les grandes étapes de l'algorithme.
1. Choisir un sommet r quelconque dans G. 2. Grâce au parcours en profondeur vu au chapitre 4, construire T = (V, ET ), un arbre couvrant G (T contient tous les sommets de G et ses arêtes sont des arêtes de G) à partir de r. 3. Renvoyer comme solution l'ensemble S des sommets de T qui ont au moins un enfant dans T . Si vous n'avez pas lu le chapitre 4 (ou si vous ne vous en souvenez plus), le point important à noter est que le parcours en profondeur à partir de r construit bien un arbre T à partir de r. De plus, on peut montrer (mais on ne le fera pas ici) que T a la propriété suivante : les feuilles (sommets n'ayant pas d'enfant dans T ) F de T forment un stable (ou indépendant ) dans G (c'est-à-dire qu'il n'y a aucune arête de G
17. Une petite couverture
163
entre les sommets de l'ensemble F ). L'ensemble S des autres sommets de T est alors renvoyé comme solution.
ILLUST RAT ION Pour illustrer cet algorithme et ses diérents points, considérons le graphe connexe de la gure suivante et prenons par exemple r = 7 (cet exemple est celui qui a été choisi dans le chapitre 4).
Le parcours en profondeur de G à partir de r donne le résultat illustré à la gure 17.9 (a). Les èches indiquent les relations entre parents et enfants. Chaque sommet a au moins un enfant, sauf les sommets 1 et 9 (qui sont colorés). L'algorithme décrit plus haut renvoie donc comme solution tous les sommets sauf 1 et 9 qui forment l'ensemble F . On constate sur cet exemple que 1 et 9 ne sont pas voisins dans G, l'ensemble F est bien un stable de G. La gure 17.9 (b) représente la solution construite dans le graphe initial (sommets colorés). Vous pouvez constater qu'il s'agit bien d'une couverture de G (chaque arête a au moins une de ses deux extrémités dans S ) et qu'elle est connexe (en supprimant les sommets non colorés ( 1 et 9), le graphe restant est connexe).
Figure 17.9: (a) Un parcours en profondeur. (b) La solution construite
Graphes et algorithmes
164
Zone orange Pourquoi est-ce une couverture de
G ? La méthode précédemment décrite renvoie un ensemble S de sommets de G. Il faut être sûr qu'il s'agit bien d'une couverture de G. Montrons-le. Tous les sommets de G sont dans S , sauf les feuilles F de T . Considérons une arête u, v quelconque de G et montrons qu'elle est couverte par S . Si u ou v sont dans S alors, bien sûr, l'arête u, v est couverte par S . L'autre cas à examiner serait celui où u et v ne sont tous les deux pas dans S . Cela veut donc dire qu'ils sont tous les deux dans F . Or, par construction, F est un stable. Il ne peut donc pas y avoir une arête u, v avec u et v tous les deux hors de S .
Pourquoi est-ce connexe ? En supprimant les sommets de F , seules sont enlevées les extrémités de l'arbre construit par le parcours en profondeur. Les supprimer permet donc de garder encore un arbre, qui est connexe. Les sommets de S sont bien connectés par un arbre dans G, ce qui garantit la connexité. Zone rouge Calcul du rapport d'approximation de cet algorithme.
Nous allons maintenant montrer que la solution construite par l'algorithme précédent a une taille au plus égale à deux fois celle de la solution optimale. Pour cela, posons bien les éléments. Le graphe initial est G = (V, E) et il est connexe. Le sommet de départ est r. L'arbre couvrant construit est noté T = (V, ET ). L'ensemble des sommets n'ayant pas d'enfant dans T est noté F . La solution S renvoyée est composée de tous les sommets de G sauf ceux de F . Notons Sc∗ la couverture connexe de taille minimale de G (que l'on ne sait pas construire de manière exacte car le problème est dicile) et OP Tc désigne sa taille. Notons aussi OP T la taille d'une couverture (pas forcément connexe) de taille minimale de G. Comme Sc∗ est aussi une couverture de G :
OP T ≤ OP Tc . Voici quelques manipulations intermédiaires. Dans l'arbre T , considérons r comme une racine. Notons A l'ensemble des sommets à distance paire de r dans T et B les autres. Pour chaque sommet ayant au moins un enfant, choisir une arête quelconque vers un de ses enfants. L'ensemble des arêtes ainsi sélectionnées à partir d'un sommet de A est noté MA et l'autre ensemble est noté MB . Illustrons cela sur l'arbre du parcours en profondeur de la gure 17.9 (a) dessiné à la gure 17.10 (a) en oubliant les arêtes qui ne sont pas dans T . Les sommets de l'ensemble A et les autres sont colorés diéremment (on rappelle que r = 7 ici). Les arêtes de MA sont représentées en pointillés ns et celles
17. Une petite couverture
165
de MB en pointillés épais à la gure 17.10 (b). Par exemple, le sommet 5 n'a qu'un seul ls (le sommet 6). Son arête dans MB est donc nécessairement 5, 6. En revanche, le sommet 4 a deux ls dans l'arbre : les sommets 3 et 5. On choisit une de ces deux arêtes de manière quelconque, par exemple l'arête 4, 3.
Figure 17.10: (a) L'arbreT et A et B. (b) Les deux couplages MA et MB Les arêtes de MA sont deux à deux indépendantes, elles ne partagent aucune extrémité. Il en est de même pour les arêtes de MB . Les ensembles d'arêtes MA et MB forment donc deux couplages dans T et aussi dans G. Or on sait que la taille d'un couplage de G est au plus OP T . Ainsi :
|MA | ≤ OP T et |MB | ≤ OP T. Chaque sommet de S a au moins un ls dans T (c'est le critère pour être dans S ), il est soit dans A soit dans B et son arête associée est soit dans MA soit dans MB . Ainsi |S| = |MA ∪ MB |. La combinaison de tous ces éléments donne :
|S| = |MA ∪ MB | ≤ |MA | + |MB | ≤ 2OP T ≤ 2OP Tc
Graphes et algorithmes
166
ce qui montre que l'algorithme proposé construit une couverture connexe dont la taille est inférieure à deux fois celle de la taille d'une solution optimale. Remarquons pour nir que la chaîne d'inégalités précédente contient |S| ≤ 2OP T , qui montre que l'algorithme construit une couverture 2-approchée. Celle-là est, de plus, connexe (contrairement à celles construites par ED qui ne le sont pas nécessairement).
Conclusion.
Couvrir les arêtes d'un graphe avec un nombre minimal de sommets est un problème facile à comprendre mais dicile à résoudre. Ce chapitre a été l'occasion d'étudier une manière d'approcher la solution optimale, sans forcément pouvoir l'atteindre. La variante où les sommets doivent, en plus, être connectés a été présentée. Une méthode faisant appel au parcours en profondeur (vu au chapitre 4) permet d'approcher l'optimal à un facteur de 2. Les mêmes problèmes existent en version pondérée, plus générale. Dans ce cas, chaque sommet a un poids qui lui est propre et l'objectif est alors de couvrir toutes les arêtes du graphe avec un ensemble de sommets dont la somme des poids est minimale. Il est ici aussi possible d'approcher la solution optimale à un facteur 2. Nous reviendrons au chapitre 22 sur ce problème dans un contexte diérent. Les sommets et les arêtes du graphe ne seront pas connus dès le début mais dévoilés bout par bout . Ce qui va compliquer les choses, comme vous pouvez vous en douter.
18
Le problème du voyageur de commerce
Dans ce chapitre, nous allons faire voyager notre personnage guide Graphix. Nous allons le placer dans un graphe G = (V, E) complet à n sommets, c'est-àdire un graphe dans lequel il y a une arête entre chaque paire de sommets. Ici chaque arête e ∈ E a un coût, noté w(e), strictement positif : w(e) > 0. Chaque fois que Graphix traverse une arête e, il doit en payer le prix w(e). Son objectif est de faire le tour de tous les sommets et de revenir à son sommet de départ en faisant en sorte que ça lui coûte le moins cher possible. Cela est connu sous le nom du problème du voyageur de commerce qui doit faire la tournée de n villes d'un vaste territoire en faisant ses trajets en avion en revenant, à la n, à sa ville de départ. Pour des questions de budget, il devra faire en sorte que les n vols lui coûtent le moins cher possible. De manière plus précise, on cherche un cycle hamiltonien de coût minimal noté H ∗ , c'est-à-dire un cycle contenant les n sommets du graphe (contenant exactement n arêtes) dont le coût total (la somme des coûts de ses arêtes) soit minimal (parmi tous les cycles hamiltoniens). Comme le nombre de cycles hamiltoniens est immense dans un graphe complet, il est hors de question de tous les générer pour en mesurer le coût et retenir le moins cher. Il faut faire autrement. Ce problème fait partie de la catégorie des problèmes diciles (au sens du chapitre 14). Nous n'allons pas chercher à le résoudre de manière exacte, mais nous allons décrire une méthode qui permet de construire en temps raisonnable un cycle hamiltonien dont le coût n'est pas trop éloigné de celui de H ∗ (il faudra quantier cela plus précisément, ce que nous ferons plus loin). L'inégalité triangulaire : aller directement de u à v est moins cher que de faire un détour par x. Nous supposerons dans la suite que les coûts des arêtes
vérient l'inégalité suivante :
w(u, v) ≤ w(u, x) + w(x, v).
L'interprétation est simple : si Graphix veut aller de u à v (ou de v à u), on suppose qu'il est moins coûteux de traverser directement l'arête u, v que de faire un détour par un autre sommet x.
Graphes et algorithmes
168
Comparons à ce que nous connaissons déjà. Les poids et les coûts.
Considérons un cycle hamiltonien H ∗ de poids minimal. En supprimant une arête de H ∗ , ce qui est obtenu n'est plus un cycle mais un chemin, notons-le P , qui contient tous les sommets de G. Or un chemin est un arbre particulier (P est connexe et sans cycle). En tant qu'arbre couvrant le graphe G, le chemin P a un poids w(P ) (somme des poids de ses arêtes) plus grand que le poids d'un arbre couvrant de coût minimal que l'on note TP rim : w(TP rim ) ≤ w(P ). L'arbre TP rim peut être construit par l'algorithme de Prim vu au chapitre 5 en prenant le coût de chaque arête comme son poids. Même si vous n'avez pas lu ce chapitre, vous pouvez continuer à lire ce qui suit. Dites-vous simplement que TP rim peut être construit de manière ecace. Nous utiliserons maintenant le terme poids ou coût de manière interchangeable. Ce n'est qu'une valeur associée à une arête. Enn, w(P ) ≤ w(H ∗ ) car P est obtenu à partir de H ∗ en l'amputant d'une arête. Ainsi en combinant les deux inégalités, on obtient :
w(TP rim ) ≤ w(H ∗ ).
Obtenir à partir de
.
H TP rim Nous allons décrire une méthode pour construire un cycle hamiltonien H dans un graphe complet pondéré. Ensuite, nous comparerons le coût w(H) de H avec le coût de H ∗ en utilisant, entre autres choses, l'inégalité précédente. La première opération consiste à construire un arbre couvrant noté TP rim de poids minimal (voir chapitre 5).
Parcourons
pour obtenir une liste des sommets.
Avant de TP rim construire le cycle hamiltonien H que nous cherchons, nous avons besoin d'une liste des sommets du graphe (ou de TP rim car il couvre le graphe) qui a certaines propriétés. Pour l'obtenir, nous allons laisser la construire. Entendons-nous bien : le résultat de cette phase n'est pas encore H . Il faudra faire un petit traitement ensuite pour l'obtenir. En attendant, plaçons sur un sommet r de TP rim et laissons-le parcourir TP rim en passant de sommet en sommet en traversant les arêtes. Les règles qu'il se xe sont les suivantes.
Graphix Graphix
Lorsqu'il arrive pour la première fois sur un sommet u à partir d'un de ses voisins v , en traversant l'arête u, v de v vers u, mémorise que v sera son point de sortie nal de u. Lorsqu'il est sur un sommet u, il va toujours vers un voisin v de u (en traversant u, v) par lequel il n'est pas encore passé, si c'est possible. Sinon, si u n'a pas (ou plus) de voisin non visité, quitte dénitivement le sommet u en allant vers le point de sortie nal de u. Règle de n : lorsque est de retour en r et qu'il n'y a plus de voisin non visité à explorer, sa visite est terminée. Il s'arrête. note scrupuleusement la liste des sommets par lesquels il passe, dans l'ordre dans lequel il y passe. Cette liste débute donc par r et se termine aussi par r.
Graphix
Graphix
Graphix
Graphix
18. Le problème du voyageur de commerce
169
Graphix
ILLUST RAT ION
Nous allons illustrer ici le parcours de dans TP rim . Les poids des arêtes de cet arbre n'ont pour l'instant pas d'importance, il s'agit juste d'illustrer le parcours. Prenons un arbre T quelconque, par exemple celui de la gure 18.1. Un point important est que T est sans cycle.
Figure 18.1: Un arbre que
Graphix
Graphix va parcourir
Imaginons que parte de r = 1 (mais ce n'est pas obligatoire). Sa liste est pour l'instant réduite à L = [1]. Il va vers un voisin de 1 non encore visité. Il a le choix. Imaginons qu'il aille vers 3. Comme il arrive en 3 pour la première fois, il marque 1 comme étant le point de sortie nal de 3 et il ajoute 3 à sa liste qui est donc maintenant L = [1, 3]. En 3, il va en priorité vers un voisin qui n'a pas encore été visité. Ce sont tous les voisins de 3, sauf 1. Imaginons qu'il aille vers 6. Ce faisant, il marque 3 comme le point de sortie nal de 6 et ajoute 6 à L. En 6, il n'a plus le choix, il doit aller en 2, le seul voisin non encore exploré. Il y va, marque 6 comme le point de sortie nal de 2 et ajoute 2 à la liste qui devient L = [1, 3, 6, 2]. En 2, il n'a plus de voisin non encore exploré. Selon les règles, il repart vers le point de sortie nal de 2, c'est-à-dire qu'il traverse l'arête 2, 6 et revient donc en 6 (qu'il ajoute à L). Comme 6 n'a, lui non plus, pas de voisin non encore exploré, il va vers le point de sortie nal de 6 (qui est 3), c'est-à-dire qu'il traverse à nouveau l'arête 6, 3 (mais en sens inverse), ce qui le reconduit en 3 (qu'il ajoute à L). Terminons sa course en donnant maintenant moins de détails (l'ensemble de ce parcours est résumé de manière graphique un peu plus loin). Il va de 3 à 7 puis, par exemple, va à 10. Là, il n'a pas le choix, il remonte vers 7. Là non plus, il n'a pas le choix, il va à 9. Comme rien de nouveau n'est encore à explorer en 9, il retourne à 7 (point de sortie nal de 9). Il est alors obligé de revenir à 3 (point de sortie nal de 3), puis va obligatoirement à 4 (dernier voisin de 4 non encore exploré), puis retourne
Graphes et algorithmes
170
à 3 car 4 est un cul-de-sac. Le voisinage de 3 ayant été totalement exploré dans les étapes précédentes, il revient à 1 puis part explorer par exemple 8, puis retourne à 1 avant d'aller à 5. De retour à 1, son exploration est terminée. Fin. Avec ce parcours, a construit la liste :
Graphix
L = [1, 3, 6, 2, 6, 3, 7, 10, 7, 9, 7, 3, 4, 3, 1, 8, 1, 5, 1]. Cette liste n'est pas très facile à lire. La gure 18.2 représente la traversée de chaque arête par une èche en pointillés portant le numéro du pas que fait depuis son point de départ r = 1 :
Graphix
Figure 18.2: Le parcours de
Graphix
Si vous avez lu le chapitre 4, ce qui précède doit vous rappeler le parcours en profondeur. En réalité, c'est exactement ça, c'est un parcours en profondeur de TP rim . Ici la particularité est que l'on fait ce parcours dans un graphe sans cycle puisque c'est un arbre, en notant, en plus, les sommets visités.
Extraction de H à partir de la liste construite précédemment. Nous avons construit TP rim , puis nous avons construit la liste L de ses sommets dans l'ordre de visite. Maintenant, nous allons extraire de L le cycle hamiltonien H qui sera le résultat nal de la méthode. Pour cela, lisons la liste dans l'ordre dans lequel elle a été construite (de gauche à droite) et chaque fois que le nom d'un sommet est lu pour la première fois, il est ajouté à H . À la n, le sommet de départ est ajouté, pour fermer le cycle. Pour rappel, le sommet de départ pour la construction de L est noté r. ILLUST RAT ION Reprenons l'exemple précédent. La [1, 3, 6, 2, 6, 3, 7, 10, 7, 9, 7, 3, 4, 3, 1, 8, 1, 5, 1].
liste obtenue Lisons L de
était : L = gauche à droite.
18. Le problème du voyageur de commerce
171
Colorons et encadrons dans L la première occurrence de chaque sommet, ce qui donne :
L = [ 1 , 3 , 6 , 2 , 6, 3, 7 , 10 , 7, 9 , 7, 3, 4 , 3, 1, 8 , 1, 5 , 1]. En ajoutant le 1 nal pour marquer le fait que H est un cycle, on obtient :
H = 1, 3, 6, 2, 7, 10, 9, 4, 8, 5, 1. Cela sera le parcours produit par l'algorithme si l'arbre TP rim est celui de la gure 18.1. N'oubliez pas que le graphe sous-jacent est complet, c'est-à-dire qu'il y a une arête entre chaque paire de sommets. Les arêtes sélectionnées dans H existent donc. Elles ne sont pas représentées à la gure 18.1 pour ne pas encombrer le dessin qui n'est qu'un schéma. An d'intégrer l'ensemble des éléments, la gure 18.3 représente en pointillés les arêtes de H . Certaines arêtes de H sont les mêmes que celles de TP rim , comme par exemple 1, 3, 3, 6 ou 5, 1. D'autres permettent de prendre des raccourcis par rapport à TP rim , comme par exemple 2, 7 ou 4, 8.
Figure 18.3: Représentation de
H = [1, 3, 6, 2, 7, 10, 9, 4, 8, 5, 1] en pointillés
Comparons le coût de
à celui de
.
H H ∗ Maintenant, la construction de H est terminée. Quel est son coût w(H) ? En réalité, ce qui nous intéresse n'est pas tellement le coût absolu de ce tour H mais plutôt le coût relatif, par rapport à ce qu'il aurait pu payer au minimum, w(H ∗ ). Nous allons donc comparer w(H) à w(H ∗ ). Pour commencer, comparons w(H) à w(TP rim ). H a été obtenu par l'extraction de la liste L. Or, quel est le coût total de L ? Cela revient à faire la somme des coûts des arêtes que a traversées pendant la construction de L. C'est simple, il a traversé chaque arête de T exactement deux fois. L'arête u, v a été traversée une première fois par exemple de u vers v , pendant la phase de découverte des nouveaux sommets, ce qui a permis de découvrir v . Plus tard, elle a été traversée une seconde
Graphix
Graphes et algorithmes
172
fois, de v vers u, pendant la remontée , lorsqu'il n'y avait plus rien à découvrir à partir de v . Ce qui fait que le parcours L coûte exactement 2w(TP rim ), ce que nous notons : w(L) = 2w(TP rim ). Si vous n'êtes pas convaincus, regardez à nouveau la gure 18.2 qui montre bien que chaque arête est traversée deux fois. La solution H est extraite de L en prenant des raccourcis par rapport aux chemins dans l'arbre TP rim . Comme le coût des arêtes vérie l'inégalité triangulaire énonçant que toute arête directe est moins coûteuse qu'un chemin indirect, on obtient : w(H) ≤ w(L).
H est toujours de coût inférieur à 2w(H ∗ ). Résumons ce que nous avons obtenu jusqu'à présent. Nous avons les inégalités et égalités suivantes : w(H) ≤ w(L), w(L) = 2w(TP rim ) et w(TP rim ) ≤ w(H ∗ ) (cette dernière est l'inégalité obtenue au début). En les combinant, on obtient : w(H) ≤ 2w(H ∗ ). L'interprétation de cette inégalité est que le coût du cycle hamiltonien H construit avec cette méthode ne dépassera jamais deux fois le coût optimal. Cela ne dit pas que le coût de H est deux fois plus grand que celui de H ∗ . Cela dit qu'il est au plus deux fois plus grand. C'est un algorithme d'approximation dont le rapport d'approximation est 2.
Zone rouge L'algorithme de Christodes. Il est possible de raner l'algorithme précédent et d'améliorer le rapport d'approximation pour le faire passer à 3/2. Cette partie est en zone rouge car elle fait explicitement appel à des notions, des algorithmes et des résultats vus tout au long du livre. Vous ne pourrez vraiment en proter que si vous êtes déjà familiarisé avec tout cela. Voici quelques éléments préalables. Nous avons vu au chapitre 13 que dans n'importe quel graphe, le nombre de sommets de degré impair est nécessairement pair (ou nul). Ainsi, dans l'arbre TP rim , il y a un nombre pair non nul de sommets de degré impair (car un arbre avec au moins deux sommets a toujours au moins deux sommets de degré 1). Algorithme de Christodes. 1. Construire TP rim , un arbre couvrant de poids minimal de G. 2. Notons I l'ensemble des sommets de degré impair dans TP rim : I = {degTP rim (u) = 2k + 1 : u ∈ V }. 3. Notons G[I] le graphe (complet) induit par I dans G (les sommets de G[I] sont les sommets de l'ensemble I et les arêtes sont toutes les arêtes de G entre ces sommets de I ).
18. Le problème du voyageur de commerce
173
4. Construire M un couplage parfait de poids minimal de G[I]. Rappel : un couplage est un ensemble d'arêtes deux à deux disjointes. Un couplage M est parfait si chaque sommet du graphe est dans une arête de M .
Construire G le multigraphe constitué de tous les sommets de G, des arêtes de TP rim ainsi que des arêtes du couplage M . Dans G , une arête u, v peut éventuellement être présente en deux exemplaires si elle fait partie de M et de TP rim . 6. Renvoyer l'ordre préxe circulaire d'un parcours eulérien de G (le chapitre 8 explique ce qu'est un parcours eulérien). C'est-à-dire parcourir le parcours eulérien de G en ajoutant dans H (initialement vide) un sommet visité pour la première fois. Ajouter à la n le sommet de départ (pour fermer le cycle). Remarques G[I] est un graphe complet pondéré avec un nombre pair de sommets. Il contient donc des couplages parfaits. Construire un couplage parfait de poids minimal M peut être fait grâce à un algorithme ecace (non détaillé dans ce livre). G peut être un multigraphe, car aux arêtes de TP rim sont ajoutées les arêtes de M ; certaines arêtes de M peuvent déjà être présentes dans TP rim . G est connexe (car il contient TP rim , un arbre couvrant) et chaque sommet est maintenant de degré pair (car une arête est ajoutée à chaque sommet de degré impair de TP rim ). Ainsi, d'après un résultat du chapitre 8, G contient bien un parcours eulérien que l'on peut construire en temps polynomial. L'algorithme de Christodes a une complexité polynomiale. Ce point n'est pas montré mais provient du fait que chacune de ses étapes a une complexité polynomiale. L'algorithme renvoie bien un cycle hamiltonien H de G car tous les sommets sont visités au moins une fois et ne sont insérés qu'une seule fois dans H (sauf le premier qui est ajouté pour fermer le cycle).
5.
ILLUST RAT ION
La gure 18.4 donne une illustration de la situation sous forme d'un schéma simplié (ni les pondérations ni les autres arêtes de G ne sont décrites). Ici I = {1, 2, 3, 4, 5, 6, 7, 8}. Les arêtes en pointillés représentent un possible couplage parfait M de poids minimal.
Graphes et algorithmes
174
Figure 18.4: Illustration du multigraphe constitué de TPrim et des arêtes du couplage M (arêtes en pointillés) Un parcours eulérien du multigraphe G (qui est le graphe de la gure 18.4 avec les arêtes normales et celles en pointillés épais) peut être par exemple :
1, 2, 3, 5, 4, 8, 6, 7, 6, 4, 9, 2, 1. Repérons (en l'encadrant) dans ce parcours la première apparition de chaque sommet : 1 , 2 , 3 , 5 , 4 , 8 , 6 , 7 , 6, 4, 9 , 2, 1. En partant de 1, l'algorithme construit donc ici : H = 1, 2, 3, 5, 4, 8, 6, 7, 9, 1, ce qui est représenté à la gure suivante.
Un rapport d'approximation 3/2. L'algorithme de Christodes construit un cycle hamiltonien H de G tel que : w(H) ≤
3 w(H ∗ ). 2
Donnons une idée de la preuve de ce beau résultat en reprenant toutes les notations précédentes. Le cycle H construit par l'algorithme en faisant un ordre préxe d'un parcours eulérien du multigraphe G emprunte donc soit directement des arêtes de G (comme l'arête 8, 6 dans l'exemple) soit des raccourcis (l'arête 7, 9 ou 9, 1 dans l'exemple), c'est-à-dire des arêtes u, v directes de G qui ne sont pas dans G . Grâce à l'inégalité triangulaire,
18. Le problème du voyageur de commerce
175
ces arêtes directes ont un poids inférieur au poids de n'importe quel chemin entre u et v dans G . Mais si, au lieu de suivre ces arêtes directes provenant de l'extraction préxe des sommets, on suit les chemins de G , on parcourt exactement une fois chaque arête du parcours eulérien de G , c'est-à-dire chaque arête de G exactement une fois. Cela revient donc à dire que : w(H) ≤ w(G ). Par ailleurs, par construction du multigraphe G , w(G ) = w(TP rim )+ w(M ). On en tire donc :
w(H) ≤ w(TP rim ) + w(M ). Comparons maintenant w(M ) à w(H ∗ ). Notons p = |M | la taille du couplage M contenant les 2p sommets de l'ensemble I . Notons u1 , . . . , u2p l'ordre de ces 2p sommets rencontrés pour la première fois en parcourant le cycle hamiltonien optimal H ∗ à partir de u1 . La gure suivante donne une illustration schématique de cela. Imaginons que les six sommets colorés soient ceux de I . Ils sont numérotés de 1 à 6 dans l'ordre dans lequel ils sont rencontrés dans H ∗ qui est le cycle représenté ici.
Considérons le cycle A = u1 , . . . , u2p , u1 de sommets pris dans cet ordre. Pour illustrer, complétons la gure précédente en représentant en pointillés les arêtes de A.
176
Graphes et algorithmes
2p Par l'inégalité triangulaire, w(A) = i=1 w(ui , ui+1 ) ≤ w(H ∗ ) (en notant, pour simplier, u2p+1 = u1 ). En eet, on peut reprendre un raisonnement similaire à ce qui précède et constater que soit l'arête ui , ui+1 est dans H ∗ (comme l'arête 5, 6 dans l'illustration), soit elle est de poids inférieur au poids de la partie de H ∗ entre ses deux extrémités (par exemple dans l'illustration, l'arête 6, 1 est un raccourci par rapport au chemin entre 6 et 1 dans H ∗ ). Les arêtes de ce cycle A peuvent être séparées en deux couplages parfaits arête-disjoints : M1 = u1 , u2 , u3 , u4 , . . . , u2p−1 , u2p et M2 = u2 , u3 , u4 , u5 , . . . , u2p , u1 . Pour prolonger l'illustration précédente, seuls les six sommets sont représentés. Les arêtes de M1 sont en traits pleins et ceux de M2 sont en pointillés.
On a donc : w(M1 ) + w(M2 ) = w(A) et ainsi w(M1 ) + w(M2 ) ≤ w(H ∗ ).
18. Le problème du voyageur de commerce
177
Par ailleurs, comme M est un couplage parfait de poids minimal de G[I], on a : w(M ) ≤ w(M1 ) et w(M ) ≤ w(M2 ). De tout cela, on tire 2w(M ) ≤ w(M1 ) + w(M2 ) = w(A) ≤ w(H ∗ ), d'où :
w(M ) ≤
w(H ∗ ) . 2
En reprenant les inégalités obtenues jusqu'à présent et en utilisant le fait que w(TP rim ) ≤ w(H ∗ ), on obtient :
w(H) ≤ w(TP rim ) + w(M ) ≤
3 w(H ∗ ). 2
On peut montrer que dans certaines situation ce rapport d'approximation est (pratiquement) atteint.
Conclusion.
Le problème du voyageur de commerce est un classique de la théorie des graphes et de l'optimisation combinatoire. Il est dicile à résoudre de manière exacte mais l'algorithme de Christodes permet d'en trouver une bonne approximation. Cet algorithme n'est pas très simple à comprendre si vous débutez. La preuve de la garantie de la qualité de ce qu'il renvoie fait appel à plusieurs résultats vus avant. Cependant, la version du début de chapitre est plus simple, même si la garantie qu'elle apporte est moins ne. L'inégalité triangulaire sur les poids (ou les coûts), hypothèse posée dès le début, aide grandement à maîtriser les coûts de ces parcours. Si elle n'est plus vériée, le problème devient alors très dicile et il n'est plus possible d'avoir des garanties aussi fortes. Une partie du chapitre 23 aborde ce sujet.
19
Retour sur l'arbre léger
Les graphes considérés ici sont connexes et pondérés : chaque arête u, v a un poids strictement positif, noté w(u, v). On se donne aussi un sous-ensemble M de sommets du graphe. Soit G = (V, E) un tel graphe et M un tel ensemble de sommets de G. Un arbre de Steiner de M dans G est un arbre T = (VT , ET ) dans G qui couvre tous les sommets de M (c'est-à-dire que tous les sommets de M sont dans T ). Le poids de cet arbre T , noté w(T ), est la somme des poids de ses arêtes. Un arbre de Steiner T est optimal s'il est de poids minimal (parmi tous les arbres dans G qui couvrent les sommets de M ). Voici quelques remarques générales pour débuter, avant d'illustrer ces notions. Un arbre de Steiner T = (VT , ET ) de M doit contenir tous les sommets de M . Il peut contenir aussi d'autres sommets de G (M ⊆ VT ) pour assurer sa connectivité. Comme G est connexe, il existe toujours au moins un arbre de Steiner de M (car tout graphe connexe contient au moins un arbre couvrant ). Construire un arbre de Steiner optimal est un problème dicile (au sens du chapitre 14) lorsque M est quelconque. Le cas particulier où les sommets à couvrir représentent tous les sommets de G (M = V ) peut être résolu facilement grâce à l'algorithme de Prim vu au chapitre 5. ILLUST RAT ION
Considérons le graphe connexe pondéré de la gure 19.1 (a). Prenons l'ensemble M = {1, 3, 5, 8} à connecter (les sommets sont colorés pour faciliter lecture). Les gures 19.1 (b) et (c) représentent deux arbres de Steiner de M (les arêtes de ces arbres sont en pointillés). Vous pouvez constater que ces deux arbres ont comme sommets l'ensemble M plus quelques sommets additionnels. Le poids du premier est 32 tandis que le poids du second est 23.
Graphes et algorithmes
180
Figure 19.1: (a) Un graphe connexe pondéré. (b) Un arbre de Steiner de arbre de Steiner de M
M. (c) Un autre
L'étude des arbres de Steiner (et ses multiples variantes) a pris de l'importance ces dernières années dans la problématique de la construction de sous-réseaux de coûts minimaux, connectant des utilisateurs répartis géographiquement, louant des liens de communication (le coût d'un lien représente le poids de l'arête). Dans de telles applications réelles, il faut prendre en compte d'autres paramètres, comme les distances induites entre les utilisateurs dans la structure de connexion. Il faut aussi prévoir des mécanismes de protection/redondance des chemins de connexion en cas de rupture de lien pour assurer la continuité du service. Tout cela fait l'objet d'études spéciques que nous ne décrirons pas dans ce livre. Ce chapitre aborde uniquement le problème de la minimisation de la somme des coûts des arêtes/liens nécessaires à la connexion.
Zone orange Utilisons l'algorithme de Prim.
Nous allons maintenant décrire un algorithme qui permet de construire dans un graphe G = (V, E) un arbre de Steiner de M dont le poids est comparable à celui de l'arbre de Steiner
19. Retour sur l'arbre léger
181
optimal. Cet algorithme a plusieurs étapes que nous allons décrire pas à pas. Dans un premier temps, nous allons le ramener au cas que nous savons déjà résoudre, c'est-à-dire celui où il faut connecter tous les sommets. Pour cela, procéder de la manière suivante : 1. Calculer la distance distG (u, v) pour chaque paire de sommets u et v de M . Cela peut se faire en utilisant l'algorithme de Dijkstra vu au chapitre 3 en prenant le poids de chaque arête comme sa longueur. Dans ce chapitre, distG (u, v) est donc le poids minimal d'un chemin entre u et v . 2. Construire un graphe complet G1 dont les sommets sont uniquement ceux de M . Chaque arête u, v de G1 est pondérée par distG (u, v). En quelque sorte, cette arête résume un plus court chemin entre u et v dans G. Le graphe G1 sera appelé le graphe complet des distances (des sommets de M ). 3. Dans le graphe complet G1 , construire un arbre couvrant noté G2 , de poids minimal. Cela peut être fait grâce à l'algorithme de Prim vu au chapitre 5. La suite de l'algorithme sera vue plus loin, mais avant illustrons ce qui vient d'être dit.
ILLUST RAT ION Pour simplier les illustrations, nous allons prendre ici un exemple de graphe G dont toutes les arêtes ont un poids de 1. Ce poids unitaire n'est pas représenté sur le dessin de la gure suivante.
Figure 19.2: Un graphe et un ensemble
M de sommets (colorés)
L'ensemble M est ici {2, 6, 10, 13, 14}. Les distances entre ces sommets sont faciles à calculer (grâce au parcours en largeur). La gure suivante représente le graphe complet des distances G1 entre les sommets de M . Ses pondérations sont les distances calculées juste avant.
182
Graphes et algorithmes
Figure 19.3: Le graphe complet des distances G1 associé au graphe de la gure 19.2 Dans ce graphe complet des distances G1 , utilisons l'algorithme de Prim pour construire un arbre couvrant de poids minimal. Partons par exemple du sommet 10 qui a quatre arêtes. Celle de poids minimal est 10, 2 qui est ajoutée dans l'arbre. Ensuite, l'arête de poids minimal incidente à 10 ou à 2 est l'arête 2, 6, de poids 2. L'arbre contient donc maintenant les arêtes 10, 2 et 2, 6. À cette étape, il y a le choix entre l'arête 10, 13 ou 10, 14 (toutes deux de poids 4). Imaginons que l'arête 10, 13 soit ajoutée. Les deux arêtes de poids minimal sortant de l'arbre en cours de construction sont 10, 14 et 13, 14, toutes deux de poids 4. Il faut faire un choix entre les deux. Imaginons que l'arête 10, 14 soit ajoutée à l'arbre qui couvre maintenant les cinq sommets.
Figure 19.4: Un arbre couvrant de poids minimal du graphe complet des distances de la gure 19.3 L'arbre obtenu est représenté à la gure 19.4 (ses arêtes ont été colorées). Son poids est 11.
19. Retour sur l'arbre léger
183
Déployons les arêtes de l'arbre de Prim dans le graphe .
G Le graphe G2 obtenu à l'étape précédente est l'arbre de Prim du graphe complet des distances. Nous allons maintenant déployer cet arbre G2 dans le graphe G initial. Pour cela, chaque arête u, v de G2 (de poids distG (u, v)) est associée à un chemin de poids distG (u, v) dans G. L'union de ces chemins donne un nouveau graphe que l'on notera G3 . ILLUST RAT ION Repartons de l'arbre de Prim de la gure 19.4. Pour chacune de ses arêtes, il faut associer un plus court chemin de G entre ses deux extrémités. Cela peut donner les chemins suivants. Pour l'arête 2, 6, on associe un chemin de G de longueur 2 entre les sommets 2 et 6, par exemple le chemin 2, 1, 6. Pour l'arête 2, 10 de poids 1, le seul chemin possible de cette longueur est celui composé uniquement de l'arête 2, 10 du graphe G. Concernant les deux dernières arêtes, 10, 13 et 10, 14, toutes deux de poids 4, plusieurs chemins sont possibles. En vue d'illustrer un peu plus loin la suite de l'algorithme, nous allons prendre les deux chemins 10, 9, 8, 12, 13 et 10, 7, 8, 11, 14. En regroupant ses quatre chemins, le graphe G3 obtenu est représenté à la gure 19.5.
Figure 19.5: Graphe obtenu en déployant dans G l'arbre de Prim de la gure 19.4
Faisons le ménage pour obtenir un arbre.
Le graphe G3 n'est pas nécessairement un arbre (cela est visible dans l'illustration précédente). Les opérations suivantes sur G3 permettent d'en obtenir un. 1. Supprimer les éventuels cycles de G3 . Il sut de construire un arbre couvrant G3 avec un algorithme de parcours (en largeur ou en profondeur) vu en début d'ouvrage. 2. Élaguer les branches mortes du graphe précédent, c'est-à-dire supprimer les branches de l'arbre qui ne conduisent à aucun sommet de M . On appellera T l'arbre obtenu après ces deux dernières opérations. C'est le résultat nal de l'algorithme qui est bien un arbre couvrant tous les sommets de M . Toutes les opérations décrites peuvent être réalisées en adaptant les algorithmes de parcours de graphes vus dans les premiers chapitres.
184
Graphes et algorithmes ILLUST RAT ION Repartons du graphe de la gure 19.5 qui a un cycle. Pour le couper, il sut, par exemple, de supprimer l'arête 9, 10. Le résultat est montré à la gure 19.6 (a). On constate alors qu'il y a une branche morte , qui est l'arête 8, 9 ne conduisant à aucun sommet de M . Cette branche inutile est élaguée. C'est ce qui est fait à la gure 19.6 (b) (le sommet 9 et l'arête ont été laissés en pointillés ns pour montrer la partie supprimée).
Figure 19.6: (a) Suppression du cycle. (b) Élagage L'arbre nal a neuf arêtes. Son poids est donc 9 (chaque arête de G a un poids unitaire dans cette illustration).
Comparons le poids de et celui de l'arbre de Steiner optimal.
T Lorsque G et M sont donnés, l'algorithme précédent permet d'obtenir un arbre T = (VT , ET ) qui couvre M et qui est de poids w(T ). T est donc un arbre de Steiner de M dans T . Mais est-ce que son poids w(T ) est proche ou non de w(T ∗ ), le poids d'un arbre de Steiner optimal T ∗ de M dans G ? On peut démontrer que : w(T ) ≤ 2w(T ∗ ).
19. Retour sur l'arbre léger
185
Cela indique que le poids de l'arbre construit par l'algorithme ne sera jamais plus grand que deux fois celui de l'arbre optimal. C'est un algorithme d'approximation dont le facteur d'approximation est 2.
Zone rouge Analyse de l'algorithme.
La preuve du facteur d'approximation 2 est un peu compliquée. Nous allons en voir les grandes lignes dans cette zone rouge en reprenant les notations précédentes.
Le poids de l'arbre nal T est plus petit que celui de l'arbre de Prim G2 . Reprenons les étapes de l'algorithme en commençant par la n.
L'arbre nal T est obtenu en supprimant éventuellement des arêtes ou des sommets du graphe G3 . Il est donc clair que w(T ) ≤ w(G3 ). Le graphe G3 lui-même est obtenu en déployant l'arbre de Prim G2 dans le graphe G. À chaque arête u, v de G2 de poids w(u, v) = distG (u, v) est associé un chemin de G entre u et v de poids distG (u, v), ainsi w(G3 ) ≤ w(G2 ). En combinant les deux inégalités, on obtient :
w(T ) ≤ w(G2 ). Cette inégalité nous sera utile plus tard.
T ∗ pour construire L. Nous allons maintenant comparer le poids w(G2 ) de l'arbre de Prim et w(T ∗ ), celui de l'arbre de Steiner optimal T ∗ de M dans G. Dans ce qui suit, nous allons utiliser et parcourir l'arbre de Steiner optimal. Attention, notre objectif ici est de démontrer un résultat, pas de donner un algorithme. Nous pouvons donc imaginer que T ∗ est à notre disposition. Partons d'un sommet r et faisons un parcours en profondeur de T ∗ à partir de r. Le parcours en profondeur d'un graphe est expliqué au chapitre 4. La façon de l'exploiter ici est similaire à ce qui est fait dans le chapitre 18. Lorsque le parcours passe pour la première fois sur un sommet de M , son numéro est noté dans une liste, qui ne contient initialement que le sommet de départ r. Une fois le parcours terminé, il ne faut retenir de cette liste que la première occurrence des sommets de M , ce qui donne la liste nale L.
Parcourons en profondeur l'arbre
ILLUST RAT ION Pour illustrer cela, prenons comme support l'arbre de la gure 19.7 sur laquelle les autres sommets et arêtes de G ne sont pas dessinés. Les sommets de M sont colorés. Notez que les sommets de degré 1 (les feuilles ) de T sont des éléments de l'ensemble M . Partons de r = 4 et parcourons l'arbre
Graphes et algorithmes
186
en profondeur. Le parcours va visiter tout l'arbre. L'ordre de passage sur les sommets peut être l'ordre suivant par exemple :
[ 4 , 3, 5 , 3, 2, 1, 7 , 8, 9, 10 , 9, 8, 11 , 12 , 11 , 8, 7 , 1, 6 , 1, 2, 3, 4 ]. Les sommets de M ont été encadrés pour les distinguer des autres. Remarquez que l'on passe plusieurs fois par chaque sommet et que cet ordre de parcours suit ici le dessin de l'arbre dans le plan mais ce n'est pas obligatoire. Cet ordre de visite a été choisi pour faciliter la compréhension de la suite. Gardons maintenant de cette trace de passage uniquement les sommets de M , ce qui donne : [4, 5, 7, 10, 11, 12, 11, 7, 6, 4]. Enn, retenons dans cette liste uniquement la première occurrence de chaque sommet de M lors du parcours :
[4, 5, 7, 10, 11, 12, 6]. C'est la liste L qui était recherchée.
Figure 19.7: Un schéma d'arbre de Steiner des sommets de
M colorés
La liste L représente un arbre couvrant particulier du graphe complet des distances G1 . La liste L = [u1 , u2 , . . . , um ] contenant les
m sommets de M , construite à l'étape précédente, peut être vue comme un chemin hamiltonien que l'on note H , du graphe complet des distances G1 . À ce titre, c'est un arbre couvrant G1 . Son poids est donc supérieur (ou égal) au poids de l'arbre couvrant de Prim G2 : w(G2 ) ≤ w(H). w(H) ≤ 2w(T ∗ ). Notons L = [u1 , u2 , . . . , um ] et H le chemin hamiltonien associé. Nous devons comparer w(H) à w(T ∗ ). Par dénition, w(H) est la somme des poids des arêtes du type ui , ui+1 de L. Or L est une trace partielle du passage du parcours en profondeur de T ∗ et le poids d'une arête ui , ui+1 de H est, par construction, égal à distG (ui , ui+1 ). On sait par ailleurs que distG (ui , ui+1 ) est le poids d'un chemin de poids minimal entre ui et ui+1 dans G. Le chemin entre ces deux sommets dans T ∗ a donc un poids supérieur, c'est-à-dire distG (ui , ui+1 ) ≤ distT ∗ (ui , ui+1 ).
Montrons que
19. Retour sur l'arbre léger
187
En parcourant à nouveau T ∗ en suivant l'ordre des sommets de L, on passe au plus deux fois par chaque arête de T ∗ (voir l'illustration un peu plus loin). En combinant tout cela, on obtient :
w(H) ≤ 2w(T ∗ ). ILLUST RAT ION Reprenons le schéma d'arbre de Steiner de la gure 19.7 et la liste associée L = [4, 5, 7, 10, 11, 12, 6]. Superposons à la gure 19.8 l'arbre de Steiner et les arêtes du graphe complet des distances entre les éléments de M suivant la liste L. Ces dernières sont représentées en pointillés. Les poids ne sont pas indiqués car il s'agit seulement d'un schéma.
Figure 19.8: Arbre de Steiner et le chemin hamiltonien correspondant à la liste L = [4, 5, 7, 10, 11, 12, 6]
En suivant les sommets dans l'ordre L = [4, 5, 7, 10, 11, 12, 6], prendre les arêtes directes (en pointillés) sera moins long que de faire les détours par l'arbre. En faisant les détours par des arêtes de l'arbre, on ne passe qu'au plus deux fois par chacune d'elles. Pour mieux visualiser cela, les arêtes en pointillés ont été déformées à la gure 19.9 pour suivre le parcours induit par la liste L.
Figure 19.9: La même représentation qu'à la gure 19.8 avec les arêtes en pointillés déformées pour épouser le contour de T ∗
Graphes et algorithmes
188
Concluons que w(T ) ≤ 2w(T ∗ ).
Rappelons les inégalités établies jusqu'à présent : w(T ) ≤ w(G2 ), w(G2 ) ≤ w(H) et w(H) ≤ 2w(T ∗ ). En combinant, cela donne le résultat nal :
w(T ) ≤ 2w(T ∗ ) qui montre que le poids de l'arbre T construit ne dépassera jamais deux fois celui de l'arbre de Steiner optimal, ce que l'on cherchait à faire. En anant l'analyse, un résultat un peu plus précis peut être montré (avec m, le nombre de sommets dans M ) : 1 w(T ) ≤ 2 1 − w(T ∗ ). m
Zone orange Dans certains cas, w(T ) ≈ 2w(T ∗ ).
La garantie précédente indique que w(T ) ≤ 2w(T ∗ ). Mais dans certains cas particuliers, w(T ) est très proche de 2w(T ∗ ). Pour montrer cela, considérons une roue qui est un graphe composé d'un cycle à k sommets et d'un sommet central r directement connecté à chaque sommet du cycle. Le poids de chaque arête r, u est p et le poids de chaque arête du cycle est 2p − 1. Les sommets de M à connecter sont les k sommets du cycle. La gure 19.10 présente un exemple d'un tel graphe avec les paramètres k = 7 et p = 10.
Figure 19.10: Une roue pondérée. Les sommets de
M sont colorés
Appliquons l'algorithme sur cette conguration avec les paramètres k et p. Les arêtes du graphe complet des distances ont pour poids 2p ou 2p − 1. La gure 19.11 donne ce graphe complet des distances associé à la conguration
19. Retour sur l'arbre léger
189
de la gure 19.10. Les arêtes sont colorées en fonction de leurs poids pour une meilleure lisibilité.
Figure 19.11: Le graphe complet des distances associé Dans le graphe complet des distances, l'algorithme de Prim est appliqué. En partant d'un sommet quelconque, l'arête de poids minimal est de poids 2p − 1. Il en est de même pour le sommet qui vient d'être intégré, etc. L'arbre construit par l'algorithme de Prim est un chemin contenant les k sommets et composé uniquement d'arêtes de poids 2p − 1. Cet arbre est donc de poids (k − 1)(2p − 1). Le résultat de l'algorithme de Prim dans le cas particulier de la gure 19.11 est donné à la gure 19.12.
Figure 19.12: Un arbre de poids minimal construit dans le graphe complet de la gure 19.11
Graphes et algorithmes
190
Un autre arbre de Steiner possible dans G est obtenu en connectant les p sommets de M via le sommet central r. Cet arbre est de poids exactement kp et c'est l'arbre de Steiner optimal T ∗ de M dans ce graphe. La gure suivante superpose ces deux arbres qui n'ont aucune arête commune. L'arbre T construit est en pointillés et T ∗ en arêtes pleines.
Comparons w(T ) et w(T ∗ ) :
w(T ) (k − 1)(2p − 1) 2kp − k − 2p + 1 1 2 1 = = =2− − + . w(T ∗ ) kp kp p k kp w(T )
La fraction w(T ∗ ) devient aussi proche de 2 que l'on veut lorsque k et p sont susamment grands. Cet algorithme fait des choix gloutons dans le graphe complet des distances où le sommet central n'apparaît pas. En économisant une unité de poids à chaque étape dans la création de l'arbre de Prim, le déploiement des chemins devient non optimal car chaque chemin créé ne réutilise aucune des portions déjà utilisées pour d'autres chemins, ce qui explique la diérence.
Conclusion.
Le problème de l'arbre de Steiner optimal fait partie de la longue liste des problèmes diciles. Pourtant, le chapitre 5 a présenté un cas particulier pouvant être résolu avec un algorithme ecace (Prim), lorsque les sommets à connecter par un arbre sont tous les sommets du graphe. Le problème devient dicile lorsque cet ensemble est quelconque. Il est intéressant de constater que l'on peut se ramener au cas simple (via le graphe complet des distances) et proposer ainsi un algorithme dont le rapport d'approximation est 2. La preuve du rapport d'approximation fait appel à un parcours en profondeur d'un arbre et à l'inégalité triangulaire, d'une manière assez similaire à ce qui a été fait pour les preuves des algorithmes du problème du voyageur de commerce au chapitre 18. Les parcours (en profondeur et Prim) sont dans ces deux cas utilisés comme simples outils pour faire des opérations de plus haut niveau. Les algorithmes avancés sont souvent des assemblages d'autres algorithmes plus basiques.
20
Un arbre couvrant minimisant la somme des distances
Zone rouge Ce court chapitre est en zone rouge car il manipule plusieurs notions vues dans d'autres chapitres. De plus, l'analyse de la qualité du résultat de l'algorithme présenté, sans être très dicile, est un peu pointue et n'est pas facile à aborder si vous n'avez pas bien assimilé le reste.
Nous allons considérer dans ce chapitre des graphes connexes (le problème et sa solution sont valables plus largement pour des graphes pondérés, c'est-àdire dont chaque arête a une longueur qui lui est propre. Mais pour simplier la présentation et les exemples, nous nous cantonnerons ici à l'étude des graphes non pondérés). Voici quelques rappels utiles pour la suite. La longueur d'un chemin est son nombre d'arêtes. La distance distG (u, v) entre u et v dans G est la longueur du plus court chemin entre u et v dans G. Un arbre T = (V, ET ) couvrant G est un arbre qui contient tous les sommets de G (il couvre G) et qui est composé d'un certain nombre des arêtes de G (ET ⊆ E ). Tout graphe connexe contient au moins un arbre couvrant. Somme des distances d'un graphe. Soit H = (V, E) un graphe quelconque connexe. On note C(H) la somme des distances de H , entre chaque couple de sommets de H , c'est-à-dire : C(H) =
distG (u, v).
u∈V v∈V
Arbre optimal : arbre couvrant, de somme des distances minimale. Soit G = (V, E) un graphe quelconque connexe. L'objectif est de trouver l'arbre T ∗ = (V, ET ), couvrant G, de somme des distances minimale ∗
Graphes et algorithmes
192
(parmi tous les arbres couvrant G). Un tel arbre sera dit optimal dans la suite. Ce problème étant dicile (au sens du chapitre 14), nous allons proposer un algorithme d'approximation. Il fait partie de la longue lignée des problèmes qui consistent à trouver un arbre dans un graphe ayant telle ou telle propriété. Certains sont étudiés dans ce livre (chapitres 3, 4, 5,19). En voici donc un autre qui n'a pas, à proprement parler, d'application pratique précise. Ce qui est intéressant est plutôt la méthode pour le résoudre de manière approchée.
Un sommet intéressant : le médian d'un graphe.
Un médian r de G = (V, E) est un sommet de G dont la somme des distances entre lui même et tous les autres sommets de G est minimale : u∈V distG (r, u). On sait facilement calculer toutes les distances distG (r, u) entre r et les autres sommets grâce au parcours en largeur ou à l'algorithme de Dijkstra (si le graphe est pondéré). Pour les détails, vous pouvez revoir le chapitre 3, mais ce n'est pas indispensable pour lire la suite. Admettez simplement que l'on peut trouver un médian d'un graphe avec un algorithme ecace, à base de parcours de graphes. Notez qu'un médian dans un graphe n'est pas nécessairement unique. Par exemple, si le graphe est complet ou si c'est un cycle, chaque sommet est un médian. Mais dans tous les cas, il y en a toujours au moins un. S'il y en a plusieurs, il faut en choisir un quelconque, noté r, car la seule propriété qui sera utile plus tard est que pour tout sommet v : distG (r, u) ≤ distG (v, u). u∈V
u∈V
Description de l'algorithme.
Voici les principales étapes de l'algorithme, utilisant ce qui vient d'être vu. 1. Trouver un médian r de G. 2. Construire un arbre T = (V, ET ) couvrant G, dont les distances sont minimales par rapport à r (appliquer ici le parcours en largeur ou l'algorithme de Dijkstra à partir de r dans G). 3. Cet arbre T = (V, ET ) est le résultat de l'algorithme.
ILLUST RAT ION Considérons le graphe connexe de la gure 20.1 (a). Détaillons les calculs. u∈V distG (1, u) = distG (1, 2) + distG (1, 3) + distG (1, 4)+ distG (1, 5) + distG (1, 6) = 1 + 1 + 2 + 2 + 1 = 7. Par symétrie, on a aussi u∈V distG (3, u) = 7.
20. Un arbre couvrant minimisant la somme des distances
193
Par ailleurs : u∈V distG (2, u) = u∈V distG (5, u) = 9. Enn, u∈V distG (6, u) = u∈V distG (4, u) = 8. Il y a donc deux médians, les sommets 1 et 3. Choisissons r = 1. Il sut ensuite de construire un arbre couvrant en utilisant le parcours en largeur à partir de r. Un résultat est montré à la gure 20.1 (b).
Figure 20.1: (a) Un graphe connexe. (b) Un arbre couvrant de plus courts chemins à partir du médian r = 1
Analyse de la qualité du résultat.
Montrons que la somme des distances C(T ) de l'arbre construit T = (V, ET ) par l'algorithme est au plus 2C(T ∗ ), T ∗ = (V, ET ∗ ) étant l'arbre optimal. Dans la suite, n est le nombre de sommets de G (donc aussi de T et de T ∗ ). Les égalités et inégalités suivantes sont expliquées juste après. distT (u, v) (1) C(T ) = v∈V v∈V
≤
=
n.distT (u, r) +
u∈V
= 2n
distT (r, v)
v∈V
distT (u, r)
(4)
distG (u, r)
(5)
u∈V
= 2n
u∈V
≤2
distG (u, v)
v∈V u∈V
= 2C(G) ≤ 2C(T ∗ )
(2)
(distT (u, r) + distT (r, v))
u∈V v∈V
(7) (8)
(6)
(3)
Graphes et algorithmes
194
1. L'égalité (1) provient simplement de la dénition de C(T ). 2. L'inégalité (2) provient du fait que faire un détour est toujours plus long que d'aller en ligne droite (dit autrement, les distances vérient l'inégalité triangulaire ) : distT (u, v) ≤ distT (u, r) + distT (r, v). 3. L'égalité (3) est simplement une réécriture de ce qui précède en distribuant sur les deux éléments sommés et en remarquant, v∈V
pour le premier, que dans la formule
distT (u, r), la variable v
u∈V v∈V
de la somme n'intervient pas dans la valeur distT (u, r) à sommer. Ainsi, distT (u, r) = n.distT (u, r). v∈V
4. L'égalité (4) vient directement du fait que : n.distT (u, r) = n distG (u, r) et que
u∈V
u∈V v∈V
=n
u∈V
distT (r, v) =
v∈V u∈V
distT (r, v) =
n.distT (r, v)
v∈V
distT (r, v).
v∈V
5. L'inégalité (5) vient du fait que l'arbre T construit par l'algorithme respecte les distances entre r et tous les autres sommets de G : distT (r, u) = distG (r, u). 6. L'inégalité (6) vient du fait que r étant un médian de G, pour tout sommet v de G, on a : distG (u, r) ≤ distG (u, v). u∈V
u∈V
7. L'égalité (7) vient simplement de la remarque que la double somme est exactement l'expression de C(G). 8. Dans n'importe quel arbre couvrant G, la distance entre deux sommets est toujours supérieure ou égale à la distance dans G. Cela permet d'avoir la dernière inégalité (8). Résumons tout cela. L'analyse permet de montrer que C(T ) ≤ 2C(T ∗ ), ce qui garantit que l'arbre T , construit avec l'algorithme, a une somme de ses distances (C(T )) qui n'est jamais supérieure à deux fois la valeur optimale. C'est donc un algorithme d'approximation de rapport 2. Une remarque pour terminer. Il ne faut pas confondre ce problème avec celui vu au chapitre 5 (ou sa généralisation au chapitre 19), la mesure faite sur l'arbre n'est pas la même (pour l'un, c'est la somme des poids de ses arêtes et pour le problème posé ici, c'est la somme des distances entre tous les sommets).
21
Découper un graphe en deux grâce à une pièce de monnaie
Les algorithmes vus jusqu'à présent ont la particularité de décrire les opérations à eectuer pas à pas, sans inuence extérieure , en se basant uniquement sur les données du problème. Dans ce chapitre, nous allons introduire un élément perturbateur dans cette belle mécanique. Dans un algorithme probabiliste, les décisions prises à chaque étape dépendent non seulement du graphe traité mais aussi d'une (ou plusieurs) valeur obtenue par un tirage aléatoire. Cela peut sembler étrange à première vue, mais ces algorithmes sont parfois fort intéressants. Pour illustrer cette catégorie de méthodes, nous allons étudier un exemple assez simple. Nous allons voir aussi que son analyse peut conduire à l'obtention de résultats structurels. Mais avant d'en arriver là, introduisons le vocabulaire et quelques dénitions nécessaires. On se donne un graphe G = (V, E) pas nécessairement connexe (les résultats présentés dans la suite peuvent s'étendre aux graphes pondérés ). Considérons un partage quelconque de l'ensemble V des sommets de G en deux sous-parties disjointes : U et W (U ∪ W = V et U ∩ W = ∅). Le couple d'ensembles (U, W ) est une coupe de G et les arêtes de cette coupe sont les arêtes ayant une extrémité dans U et l'autre dans V . Une coupe de G sera dite optimale si elle a un nombre maximal d'arêtes (parmi toutes les coupes de G). Construire une coupe optimale est un problème dicile. ILLUST RAT ION
Considérons le graphe G de la gure 21.1 ainsi que les deux coupes suivantes qui sont représentées sur la gure. 1. La coupe U1 = {1, 2, 4, 8} et W1 = {3, 5, 6, 7, 9, 10} a seulement les arêtes 1, 9, 8, 7, 4, 9 et 4, 3 (en pointillés). 2. La coupe U2 = {1, 8, 5, 4, 6} et W2 = {9, 7, 10, 2, 3} a dix arêtes. Elle est donc meilleure que la première.
Graphes et algorithmes
196
Figure 21.1: Un graphe G et deux exemples de coupes de G Ce graphe a de nombreuses autres coupes. Il serait vraiment très long de toutes les générer pour compter les arêtes de chacune. Il faut une autre approche.
Jouons à pile ou face. L'algorithme que nous allons voir ici est très simple mais nécessite d'avoir un dispositif pour jouer à pile ou face. Vous pouvez bien sûr prendre une pièce (non truquée) pour faire des expériences à la main sur des petits graphes. En pratique, un générateur aléatoire est utilisé. Dans la plupart des cas, c'est un logiciel qui permet de simuler ces tirages 1 . Pour ce qui nous intéresse ici, le moyen concret d'obtenir un tirage n'est pas important, il sut que ce moyen assure que pile a autant de chances d'être tiré ( 50 %) que face (50 %). Voici donc l'algorithme pour obtenir une coupe (U, W ) de G. Pour chaque sommet u de G, faire un tirage pile ou face . Si c'est pile, alors mettre u dans l'ensemble U . Si c'est face, alors mettre u dans l'ensemble W . À la n, chaque sommet est soit dans U soit dans W . C'est bien une coupe de G. 1. On parle alors plutôt de générateur pseudo-aléatoire.
21. Découper un graphe en deux grâce à une pièce de monnaie
197
ILLUST RAT ION Reprenons le graphe de la gure 21.1 et imaginons que le tirage aléatoire donne les résultats suivants pour les sommets examinés dans l'ordre de leur numéro : P P F F P F F P P P (P = Pile et F = Face). Avec ce tirage, le sommet 1 va dans U , le sommet 2 va dans U , le sommet 3 va dans W , etc. La coupe obtenue est représentée à la gure 21.2.
Figure 21.2: Coupe obtenue avec les tirages aléatoires
Comment évaluer un tel algorithme ?
Notons w∗ le nombre d'arêtes d'une coupe optimale de G et notons m le nombre d'arêtes de G. Le nombre d'arêtes de la coupe produite par l'algorithme dépend bien sûr des résultats des tirages aléatoires qui ont été eectués. Dans le pire des cas, l'algorithme peut placer tous les sommets de G dans U et aucun dans W (ou le contraire) et la coupe obtenue ne contient aucune arête. Au contraire, avec de la chance, l'algorithme peut répartir les sommets suivant une coupe optimale dont le nombre d'arêtes est donc w∗ . Cela fait une forte amplitude de résultats possibles. Nous allons faire ici une évaluation en moyenne de cet algorithme. La preuve est en zone rouge un peu plus loin mais le résultat dit, de manière très informelle, qu'en moyenne, cet algorithme construit une coupe contenant au moins la moitié des arêtes de G, c'est-à-dire au moins m 2 arêtes. Comme w∗ ≤ m (dans une coupe, il y toujours au plus m arêtes), cet algorithme construit ∗ donc, en moyenne, une coupe dont le nombre d'arêtes est supérieur à w2 , c'est-à-dire au moins la moitié de la quantité optimale. Ici le terme en moyenne peut être interprété de la manière suivante : imaginons que tous les tirages possibles pile ou face soient faits et que la coupe correspondante soit construite pour chacun d'eux. w∗ Alors la moyenne des nombres des arêtes de toutes ces coupes vaut au moins m 2 ≥ 2 .
Zone rouge L'idée de la preuve du résultat.
Nous allons donner une idée de la preuve du résultat évoqué ci-dessus. Pour la comprendre, il faut avoir quelques connaissances en probabilité, c'est pourquoi cette partie est en zone rouge. Notons le graphe G = (V, E), m son nombre d'arêtes et w∗ le nombre d'arêtes d'une coupe optimale de G.
Graphes et algorithmes
198
Une variable aléatoire Xu,v est associée à chaque arête u, v de G. Elle vaut 1 si l'arête u, v est dans la coupe et vaut 0 sinon. Notons Z la variable aléatoire du nombre d'arêtes dans la coupe. On notera Esp(Y ) l'espérance d'une variable aléatoire Y . Par dénition de ces variables : Esp(Xu,v ). Esp(Z) = u,v∈E
Par dénition de Xu,v : Esp(Xu,v ) = 1 × P r(u, v dans la coupe) + 0 × P r(u, v hors de la coupe) = P r(u, v dans la coupe). Il faut donc déterminer maintenant quelle est la probabilité qu'une arête u, v de G soit ou non dans une coupe. Pour qu'elle le soit, il faut que u et v soient chacun dans un ensemble diérent. Examinons tous les cas possibles en fonction du tirage pile ou face . 1. Pile pour u et pile pour v . Dans ce cas, u, v n'est pas dans la coupe (car u et v sont tous les deux dans U ). 2. Face pour u et face pour v . Dans ce cas, u, v n'est pas dans la coupe (car u et v sont tous les deux dans W ). 3. Pile pour u et face pour v . Dans ce cas, u, v est dans la coupe (car u est dans U et v dans W ). 4. Face pour u et pile pour v . Dans ce cas, u, v est dans la coupe (car u est dans W et v dans U ). Dans la moitié des cas, l'arête u, v fait partie de la coupe et dans l'autre moitié des cas, elle n'en fait pas partie. On en déduit que P r(u, v dans la coupe) = 12 . Ce raisonnement est totalement indépendant de ce qui se passe pour les autres arêtes ou les autres sommets de G. On peut donc écrire (en reprenant ce qui précède) : m P r(u, v dans la coupe) = . Esp(Z) = 2 u,v∈E
La dernière égalité vient du fait qu'en faisant une somme de ces probabilités qui valent toutes 12 (P r(u, v dans la coupe) = 12 ), la valeur 12 est ajoutée autant de fois qu'il y a d'arêtes dans G, c'est-à-dire m fois. Cette analyse nous donne non seulement un résultat sur la taille moyenne de la coupe construite par l'algorithme, mais aussi une preuve que tout graphe G à m arêtes possède une coupe contenant au moins m 2 arêtes (car si ce n'était pas le cas, Esp(Z) serait nécessairement strictement plus petit que m 2 ). Ce résultat est structurel, général (vrai pour tout graphe) et il est pourtant démontré ici grâce à l'analyse d'un algorithme probabiliste.
21. Découper un graphe en deux grâce à une pièce de monnaie
Conclusion.
199
Les algorithmes qui utilisent des tirages aléatoires sont parfois de très bonnes alternatives aux algorithmes déterministes (qui n'en utilisent pas). Des résultats mathématiques du domaine des probabilités sont nécessaires pour montrer la qualité moyenne des solutions produites. En pratique, rien n'interdit de xer un entier k puis d'exécuter k fois de suite un tel algorithme en utilisant des tirages indépendants. La meilleure solution obtenue doit alors être sauvegardée au fur et à mesure. Dans certains cas, il est possible d'évaluer le nombre d'exécutions à eectuer pour garantir (avec forte probabilité) la qualité du résultat. Ces analyses probabilistes permettent aussi de montrer, parfois, l'existence d'objets ou de propriétés, comme ici le fait que dans tout graphe à m arêtes, il existe une coupe avec au moins m 2 arêtes.
22
Un avenir incertain
Dans les chapitres précédents de ce livre, les algorithmes décrits pouvaient proter de la connaissance intégrale du graphe pour travailler dessus. Ils avaient la possibilité de faire des choix à certaines étapes puis, plus tard, éventuellement de revenir sur ces choix pour en faire d'autres, en vue d'améliorer la solution. Nous allons nous placer ici dans un contexte beaucoup plus restrictif qui est celui des algorithmes en ligne, dans lesquels de telles modications ne sont plus possibles. La recherche de bons algorithmes en ligne a été motivée par diverses applications. La première est liée au domaine nancier : comment prendre les meilleures décisions de placements en fonction de ce que l'on connaît déjà du marché à l'instant t, des placements passés mais sans savoir comment vont évoluer les tendances ? La seconde concerne les systèmes informatiques qui sont maintenant largement connectés et constitués de multiples entités qui interagissent de manière permanente. Dans ce contexte, un serveur doit pouvoir répondre à des requêtes qui peuvent arriver n'importe quand. Il doit donc gérer sa mémoire de manière à pouvoir répondre le plus vite possible à des demandes de lecture ou d'écriture dans les données qu'il héberge. Derrière ces quelques situations se cachent une multitude de variantes plus ou moins (souvent plus que moins) complexes. L'objectif de ce chapitre est de vous présenter un problème de graphes dans un contexte en ligne assez simple et très strict. Commençons par détailler le problème particulier étudié.
Un graphe dévoilé sommet par sommet. Le graphe à manipuler n'est pas connu au début. À chaque étape, un nouveau sommet u est dévoilé ainsi que les arêtes qui le relient à certains des sommets déjà dévoilés. Que peut faire un algorithme ? Un ensemble S est initialement vide. À chaque nouveau sommet u dévoilé, avec d'éventuelles arêtes contenant u, l'algorithme ne peut faire qu'un seul des deux choix possibles : soit ajouter le sommet u dans S ; soit ne pas ajouter u dans S .
Graphes et algorithmes
202
La décision que prend l'algorithme est irrévocable, il n'a pas le droit de revenir sur un choix déjà fait. Il ne peut agir que sur le sommet révélé, soit en l'ajoutant à S (et il ne pourra plus jamais le supprimer de S ), soit en ne l'ajoutant pas à S (et il ne pourra plus jamais l'ajouter).
Quel est l'objectif ?
À la n de chaque étape, un graphe G = (V, E) a été dévoilé. Il est composé des bouts qui ont été dévoilés à chaque étape. L'objectif ici est que l'ensemble S couvre chaque arête u, v de G, c'est-à-dire que u ou v (ou les deux) soit dans S . Si vous avez lu le chapitre 17, vous reconnaîtrez que S est une couverture de G.
Un avenir totalement inconnu.
Entendons-nous bien : on ne connaît pas à l'avance le graphe, rien d'autre n'est connu que ce qui a déjà été dévoilé. Chaque fois qu'un nouveau sommet apparaît, il faut prendre une décision le concernant. Cette décision est irrévocable. On ne connaît même pas le nombre d'étapes pendant lesquelles un nouveau sommet va être dévoilé.
Un algorithme.
Avec de telles contraintes, il n'y a pas beaucoup de choix. Imaginons qu'à une certaine étape le sommet u soit dévoilé, ainsi que des arêtes le contenant. Pour xer les idées, supposons que trois arêtes u, v1 , u, v2 et u, v3 sont dévoilées en même temps que u. Les sommets v1 , v2 et v3 ont déjà été dévoilés à de précédentes étapes et deviennent donc, à cette étape, des voisins du nouveau sommet u. Deux cas sont à envisager. Si au moins l'un des trois sommets, imaginons v1 par exemple, n'a pas été placé dans S au moment où il a été dévoilé, alors il faut obligatoirement que u soit placé dans S sinon l'arête u, v1 ne sera pas couverte par S . Si les trois voisins de G sont déjà dans S alors l'algorithme a le choix, il peut ne pas ajouter u dans S . C'est ce que nous choisissons de faire ici. La règle de traitement de u est simple. L'algorithme ne l'ajoute que s'il est voisin d'un sommet (déjà dévoilé à une étape précédente) qui n'est pas dans S .
ILLUST RAT ION Illustrons ce contexte particulier et cet algorithme. Nous allons décrire une suite d'étapes de dévoilement et les réactions de l'algorithme à ces nouveautés. Pour clarier les illustrations, les sommets qui seront ajoutés dans la solution S (initialement vide) seront colorés. Au départ S est vide. Aucun sommet n'est encore connu. La première étape dévoile un sommet. Comme rien n'a été dévoilé avant, aucune arête n'est encore connue. Voici ce sommet.
22. Un avenir incertain
203
Ensuite un deuxième sommet est dévoilé :
On constate ici qu'il n'y a pas d'arête entre 1 et 2. L'algorithme n'insère pas le sommet 2 dans S . Le sommet 3 est ensuite dévoilé, ainsi que l'arête 1, 3. Dans ce cas, le sommet 3 a un voisin qui n'est pas dans S . L'algorithme l'ajoute donc à S .
À la quatrième étape, un sommet 4 est dévoilé, ainsi que l'arête 4, 3. Ici l'algorithme n'ajoute pas 4 dans S car l'arête 4, 3 est déjà couverte par 3.
À la cinquième étape, le sommet 5 et les arêtes 2, 5 et 3, 5 sont dévoilés. L'algorithme doit ajouter le sommet 5 dans S sinon l'arête 2, 5 ne sera jamais couverte.
L'algorithme pourrait se poursuivre. Notez qu'à la dernière étape, les sommets colorés forment bien une couverture du graphe déjà dévoilé. En réalité, cette propriété est vraie à la n de chaque étape. Reprenez les gures précédentes et vous constaterez que les sommets colorés forment toujours une couverture du graphe courant tel qu'il est à la n d'une étape donnée. Notez aussi que le modèle de dévoilement interdit d'ajouter à une future étape une arête entre deux sommets qui ont été dévoilés à une étape précédente. Par exemple à une étape 6, il n'est pas possible de dévoiler une nouvelle arête entre 1 et 2.
Notre algorithme construit bien une couverture de G. Malgré ces fortes contraintes, on peut facilement montrer que l'algorithme décrit juste avant permet de maintenir à chaque étape une couverture S du graphe G = (V, E) dévoilé jusqu'à cette étape. Comment ? Considérons une arête u, v quelconque de G. Supposons que u a été dévoilé avant v (pas forcément juste avant). Si u a été placé dans S , alors l'arête u, v est au moins couverte par u, ce qui est susant. Sinon, cela veut dire que lors de l'examen de u à son apparition, l'algorithme ne l'a pas ajouté à S . Par conséquent, lorsque, plus tard, v et l'arête u, v sont dévoilés, l'algorithme ajoute v à S (car v a bien un voisin qui n'est pas dans S ). Ainsi u, v est bien couverte par S .
204
Graphes et algorithmes
La taille de S . La diculté principale va être de comparer la taille de S (c'està-dire le nombre de ses sommets) et la taille de la plus petite couverture possible S ∗ de G. Ici nous sommes à nouveau dans une comparaison de taille par rapport à une solution optimale S ∗ . Seulement, nous devons lutter contre deux dicultés conjuguées. Le problème de la couverture optimale est un problème dicile (au sens du chapitre 14), même si tout le graphe est connu à l'avance. Or ici le graphe n'est pas connu à l'avance, il est dévoilé par bouts. Chaque décision est irrévocable. Prendre une mauvaise décision à une étape nous empoisonne jusqu'au bout. Impossible de revenir dessus. Avec tout cela, il serait très surprenant d'avoir de très bons résultats. Mais approfondissons un peu l'analyse.
Un cas le plus défavorable. Un pire cas est celui de la situation suivante. À l'étape 1, le sommet r est dévoilé. À chaque étape i suivante, un sommet, que nous noterons ui , est dévoilé ainsi que l'arête r, ui . L'algorithme va donc prendre les décisions suivantes : le sommet initial r n'est pas ajouté dans S car il n'a pas (encore) de voisin. Chaque sommet ui va ensuite impérativement être ajouté à S , sinon l'arête r, ui ne serait pas couverte. À la n de l'étape n + 1, S contient n sommets : u2 , . . . , un+1 , c'est-à-dire tous les sommets sauf r. Une illustration d'une telle situation avec n = 5 est donnée à la gure suivante.
Il faut comparer la taille de S (qui est n ici) à la taille d'une couverture optimale du graphe dévoilé. Or celui-là est composé d'un sommet r relié à chaque ui . Une couverture optimale est de taille 1 car r tout seul sut à couvrir toutes les arêtes. Notre algorithme peut donc se tromper beaucoup, dans certaines circonstances, avec un ordre de révélation le plus défavorable. Il a cependant de bonnes qualités qui sont exprimées dans ce qui suit. Avant d'aller plus loin, nous avons besoin de savoir ce qu'est une couverture minimale pour l'inclusion et d'en connaître une propriété.
Une couverte minimale pour l'inclusion d'un graphe. Une couverture S d'un graphe G = (V, E) est dite minimale pour l'inclusion si en enlevant de S n'importe lequel de ses sommets, ce n'est plus une couverture de G.
22. Un avenir incertain
205
ILLUST RAT ION Considérons le graphe de la gure 22.1.
Figure 22.1: Un graphe La gure 22.2 propose deux couvertures diérentes du graphe de la gure 22.1. L'une contient quatre sommets. Elle est bien minimale pour l'inclusion car en enlevant n'importe lequel de ses sommets, ce n'est plus une couverture de G. Par exemple, en lui supprimant le sommet 8, l'arête 5, 8 n'est plus couverte. Vous pouvez facilement le vérier pour chacun des autres sommets colorés. L'autre couverture contient cinq sommets et elle est, elle aussi, minimale pour l'inclusion. Par exemple, en enlevant de cette couverture le sommet 3, l'arête 3, 7 n'est plus couverte. En enlevant le sommet 6, l'arête 6, 7 n'est plus couverte, etc.
Figure 22.2: Deux couvertures minimales du graphe de la gure 22.1
206
Graphes et algorithmes Les couvertures minimales pour l'inclusion n'ont pas toutes la même taille.
Les sommets qui ne sont pas dans une couverture forment un indépendant. Considérons un graphe G = (V, E) et une couverture minimale pour
l'inclusion S quelconque de G. Notons I les autres sommets de G (ceux qui ne sont pas dans S ). Les deux propriétés suivantes sont vraies. Il n'y a pas d'arête entre deux sommets de I , sinon elle ne serait pas couverte par S . I est ce que l'on nomme un indépendant du graphe G, c'est-à-dire un ensemble de sommets entre lesquels il n'y a aucune arête. Les sommets hors de S forment donc nécessairement un indépendant de G. Chaque sommet u de S a au moins un voisin dans I . Supposons le contraire, c'est-à-dire supposons qu'il y a un sommet u de S qui n'est voisin d'aucun sommet hors de S . Tous les voisins de u sont donc dans S . En supprimant u de S , le résultat est encore une couverture de G (toutes les arêtes contenant u sont couvertes par les voisins de u qui sont toujours dans S ), ce qui contredit le fait que S est minimale.
ILLUST RAT ION Vous pouvez vérier ces deux propriétés sur les deux couvertures minimales pour l'inclusion à la gure 22.2. Pour la première couverture, les sommets 3, 4, 5 et 6 n'ont aucune arête entre eux. C'est l'ensemble indépendant I . Chaque sommet coloré a bien au moins un voisin dans I .
L'algorithme construit toujours une couverture minimale pour l'inclusion. Revenons maintenant à notre algorithme qui construit une couverture au fur
et à mesure que les bouts du graphe lui sont dévoilés. Considérons une étape donnée et notons G = (V, E) le graphe déjà dévoilé. Notons S l'ensemble construit. On sait (voir plus haut) que S est une couverture de G. Montrons qu'elle est minimale pour l'inclusion. Considérons n'importe quel sommet u de S . Comme u est dans S , il a été ajouté lorsqu'il a été dévoilé. Or, l'algorithme n'a ajouté u que parce qu'il avait un voisin v (déjà dévoilé) qui n'avait pas été ajouté à S . Ainsi, u est dans S , v dans I et il y a bien une arête entre les deux. Ainsi, S privé de u ne serait plus une couverture. Malgré toutes les contraintes qui pèsent sur lui, cet algorithme construit une couverture dans laquelle il n'y a pas de sommet superu que l'on pourrait enlever. C'est déjà une propriété intéressante.
Le cas le plus favorable. Nous avons vu plus haut que cet algorithme pouvait construire une couverture très grande par rapport à la meilleure possible. Montrons maintenant que si l'ordre de révélation est favorable, alors il peut construire une couverture optimale. Notons G le graphe à l'étape courante et S ∗ une couverture optimale de G. Notons I les sommets de G hors de S ∗ (qui forment un indépendant). Montrons que parmi tous les ordres de dévoilement possibles de G, certains permettent à l'algorithme de construire S ∗ . En voici un : dévoiler d'abord tous les sommets de I ,
22. Un avenir incertain
207
dans n'importe quel ordre puis, ensuite, tous les sommets de S ∗ dans n'importe quel ordre. Que va faire l'algorithme ? Il va voir d'abord les sommets de I et aucune arête car I est un indépendant. Il ne va donc ajouter aucun sommet de I dans S . Lorsque les sommets de I ont été dévoilés, S est toujours vide et aucune arête de G n'a encore été dévoilée. Ensuite, les sommets de S ∗ sont dévoilés l'un après l'autre. On sait grâce à un résultat précédent que chaque sommet u de S ∗ a au moins un voisin dans I qui n'est pas dans S . L'algorithme va alors ajouter nécessairement u dans S . À la n S = S ∗ . Il existe donc un ordre de dévoilement qui permet d'obtenir une solution optimale. Illustrons cela sur la couverture optimale S = {1, 2, 7, 8} de la gure suivante.
Un ordre de dévoilement permettant de trouver cette solution optimale est par exemple 5, 4, 3, 6, 1, 2, 8, 7. Un autre est 3, 6, 4, 5, 7, 2, 1, 8, etc.
Zone orange En moyenne ?
L'ordre de dévoilement des sommets a un impact très fort sur la taille de la couverture construite. Il peut conduire à construire une solution très mauvaise ou, au contraire, une solution optimale. Et en moyenne ? Il faut bien s'entendre sur les termes. Considérons un graphe G = (V, E) quelconque. Chaque ordre de dévoilement possible de G est pris en compte. Pour chacun, la taille de la couverture construite par l'algorithme est calculée. Ce qui est recherché ici est la moyenne des tailles de toutes ces couvertures. Quel est le résultat obtenu ? La réponse dans le cas général est un peu compliquée à exposer ici. Nous allons nous contenter de faire ce calcul sur un graphe particulier qui est une étoile à n sommets composée d'un sommet central r relié à n − 1 feuilles. La gure 22.3 représente une étoile à huit sommets.
208
Graphes et algorithmes
Figure 22.3: Une étoile à huit sommets Combien y a-t-il d'ordres de dévoilement possibles d'une étoile à n sommets ? Le premier sommet dévoilé est un quelconque parmi les n. Le deuxième sera un quelconque des n − 1 restants, etc. Pour le dernier dévoilé, il n'y a aucun choix : c'est celui qui n'a pas encore été dévoilé. Pour le premier il y a n choix, pour le deuxième n − 1 choix, pour le troisième n − 2 choix, etc. Pour le dernier, il ne reste qu'un seul choix. En tout, il y a : n(n−1)(n−2) . . . 1 ordres de dévoilement des n sommets. Cette quantité s'écrit n! (le signe ! fait partie de la notation). La valeur n! est donc le produit de tous les entiers entre 1 et n. Un ordre de dévoilement peut s'écrire comme une liste des n sommets du graphe qui représente l'ordre (de gauche à droite) dans lequel les sommets sont dévoilés. Par exemple, sur l'étoile de la gure 22.3, un ordre de dévoilement peut être [r, 1, 2, 3, 4, 5, 6, 7]. Un autre peut être [5, 2, 3, r, 1, 4, 7, 6] ou [7, 5, 6, 4, 3, 1, 2, r], etc. Il y en a en tout 8! = 8×7×6×5×4×3×2×1 = 40 320. Étudions un de ces 8! ordres de dévoilement. Par exemple l'ordre [r, 1, 2, 3, 4, 5, 6, 7]. Le sommet central r est dévoilé en premier. Comme il n'a pas (encore) de voisin, il n'est pas mis dans la solution. Ensuite, le sommet 1 est dévoilé, avec l'arête r, 1. Le sommet 1 a donc un voisin qui n'est pas dans la solution, il est ajouté à la solution courante. Il en sera de même pour les six prochains sommets dévoilés. Avec cette liste, la solution est de taille 7 (tous les sommets sauf r). Il en va de même pour la liste [r, 5, 2, 7, 4, 1, 6, 3] et [r, 1, 2, 6, 3, 5, 4, 7], etc. De manière plus générale, si la liste débute par r, alors la solution est de taille n − 1. Examinons maintenant le cas où r n'est pas en première position dans la liste : [. . . , r, . . . ]. Les premiers sommets dévoilés ne sont pas ajoutés dans la solution car ils n'ont pas (encore) d'arête. Ensuite, r est révélé, ainsi que les arêtes le liant à ces sommets déjà dévoilés qui ne sont pas dans la solution. L'algorithme ajoute donc r dans la solution. Ensuite, les derniers sommets sont dévoilés avec pour unique voisin le sommet r qui est présent dans la solution. Ils ne sont donc pas ajoutés. À la n, seul le sommet r est dans la solution (qui est ainsi la meilleure possible). Faisons le bilan. Si r est en début de liste, la solution obtenue est de taille n − 1. Si r n'est pas en début de liste, la solution obtenue est de taille 1.
22. Un avenir incertain
209
Or, il y a en tout (n−1)! listes ayant r comme premier élément (les (n−1)! listes possibles des n − 1 autres sommets auxquelles r est ajouté en premier). Toutes les autres listes n'ont pas r comme premier élément. Comme il y a en tout n! listes, il y a n! − (n − 1)! telles listes. (n − 1)! listes donnent une solution de taille n − 1 et n! − (n − 1)! listes donnent une solution de taille 1. La moyenne des tailles des solutions construites par l'algorithme est donc :
(n − 1)!(n − 1) n! − (n − 1)! (n − 1)!(n − 1) + n! − (n − 1)! = + n! n! n! 1 2 n−1 +1− =2− . n n n En moyenne, cet algorithme renvoie des solutions de taille environ 2, c'està-dire très petite. Mais le raisonnement précédent nous éclaire surtout sur le fait qu'il y a beaucoup plus d'ordres de dévoilement favorables (donnant une solution de taille 1) que d'ordres donnant une mauvaise solution (de taille n − 1). Un résultat semblable plus général peut être montré pour n'importe quel graphe. Cet algorithme n'est donc pas mauvais si les ordres de dévoilement d'un graphe G ont tous la même probabilité. =
Conclusion.
L'algorithmique en ligne étudie les problèmes d'organisation ou d'optimisation lorsque toutes les données à traiter ne sont pas connues à l'avance. Il ne s'agit pas que de problèmes de graphes, le domaine est bien plus vaste. Plusieurs modèles de dévoilement existent : on peut connaître une partie du futur comme le nombre total de données à traiter ou connaître une borne sur les valeurs qui seront dévoilées ou des probabilités sur les prochains éléments dévoilés, etc. Pour évaluer la robustesse et la qualité d'adaptation, l'algorithme peut être confronté à un adversaire qui va dévoiler les données en fonction des choix de l'algorithme (de manière à essayer de le tromper au maximum). En général, la solution construite est comparée à la meilleure solution possible si toutes les données étaient connues à l'avance, comme ce qui a été fait ici. Cela donne une analyse dite en pire cas , une sorte de rapport d'approximation dynamique (appelé rapport de compétitivité ). La nalité de ce type de démarches est d'obtenir des méthodes adaptables aux changements. Cette préoccupation est cruciale dans un monde où de nombreuses applications informatiques reçoivent des données à traiter, à agréger, à organiser, ou des requêtes à satisfaire de manière dynamique, au l de l'eau.
23
Autres problèmes et autres approches
Ce livre a été l'occasion de découvrir les graphes et quelques algorithmes classiques ainsi que d'autres, plus avancés. On distingue actuellement deux types de problèmes. Ceux que l'on sait résoudre de manière ecace et tous les nombreux autres. Parmi ces derniers, certains peuvent être traités par des algorithmes d'approximation. Puis il y a tous les autres, pour lesquels on ne sait pas faire grand-chose.
Que faire face à un problème que l'on ne sait pas résoudre ? La première question à se poser est : Est-ce que ce problème fait partie de la liste des problèmes diciles déjà connus ? Si ce n'est pas le cas, il faut se demander s'il est équivalent à un problème dicile connu. Il existe des techniques pour (tenter de) montrer qu'un problème est aussi dicile qu'un autre. Si vous n'y arrivez pas, peut-être que votre problème peut être résolu par un algorithme ecace mais que vous n'avez pas encore trouvé comment faire. De nombreuses stratégies de résolution ne sont pas intuitives, par exemple la construction d'un couplage de taille maximale dans un graphe biparti à partir d'un réseau à ots (voir chapitre 11). Ce livre n'a pas décrit toutes les méthodes de résolution, loin de là. Que faire face à un problème dicile ? Si vous avez la preuve que votre problème est dicile, voici quelques conseils. Dans un premier temps, demandez-vous si vous devez nécessairement le résoudre dans sa globalité ou uniquement dans des cas particuliers. Imaginons par exemple que vous deviez trouver un chemin hamiltonien dans un graphe. Ce problème est dicile en général. Mais il devient facile si les graphes que vous devez manipuler sont complets ou vérient les conditions de Ore ou de Dirac vues au chapitre 9. De même, le problème de la couverture optimale vu au chapitre 17 n'est plus un problème dicile si l'on se restreint aux graphes bipartis. En revanche si vous ne savez rien de particulier sur les instances que devra manipuler votre algorithme, alors vous devrez traiter le problème dans sa généralité. Dans ce cas, cherchez un algorithme d'approximation.
Graphes et algorithmes
212
Si vous êtes vraiment bloqué, une autre piste peut consister à utiliser une métaheuristique. Ici aussi, il faudrait un livre complet exclusivement consacré à ce sujet pour en faire le tour. Disons, de manière très simpliée, qu'une métaheuristique est une méthode (plus ou moins) générique pour représenter un problème d'optimisation de manière à pouvoir utiliser une stratégie elle aussi générique pour tenter de le résoudre. Le verbe tenter est important ; rien ne permet, en général, d'avoir des garanties solides que la méthode va bien (ou mal) marcher, aussi bien en termes de temps de calcul que de qualité du résultat obtenu.
Descente locale.
Pour illustrer ce type de méthodes, nous allons décrire le principe d'une qui n'est qu'une routine présente dans plusieurs métaheuristiques (en réalité, nous ne décrivons ici qu'une des d'une descente locale). Cela est utilisé lorsqu'il faut trouver une solution, qui doit avoir une certaine mesure, que l'on notera m(.), minimale (la mesure d'une solution dépend du problème ; elle peut être son poids, son nombre de couleurs, sa longueur, etc.). Une composante importante de cette méthode est la dénition du d'une solution s. C'est un ensemble de solutions qui sont proches (structurellement) de s (ce point dépend beaucoup des objets qui sont manipulés). Un exemple est donné un peu plus loin. La méthode de descente part d'une solution initiale s0 quelconque (éventuellement générée aléatoirement) de mesure m(s0 ). La mesure de chacune des solutions dans le voisinage de s0 est évaluée et, parmi celles-là, la solution qui a la plus petite mesure, que l'on note s1 , est sélectionnée. Le processus est répété en prenant comme nouvelle référence s1 . Cette itération se poursuit tant que le voisinage de la solution courante si contient une solution dont la mesure est que celle de si . La descente est stoppée si si est la solution de plus petite mesure de son voisinage. Dans ce dernier cas, si est un puisque c'est la solution qui a localement la plus petite mesure. Remarquons plusieurs points.
descente locale
variantes
voisinage
strictement plus petite
minimum local
Rien ne permet de garantir (en général) que ce qui est obtenu à la n est une solution optimale (une solution qui a la plus petite mesure parmi toutes les solutions possibles). Rien ne permet de garantir (en général) que ce qui est obtenu à la n est une solution ayant une mesure comparable à celle d'une solution optimale (pas de rapport d'approximation). La dernière solution renvoyée est un (relatif à la dénition du voisinage et à la solution initiale). Un moyen d'essayer de sortir de ces puits locaux est d'introduire des modications aléatoires sur la solution courante par exemple.
minimum local
Le nombre d'itérations (passages de la solution si à la suivante si+1 ) que devra faire cette méthode avant d'arriver à la dernière solution n'est pas connu . La complexité est dicile à évaluer.
priori
a
Il faut garantir que tous les éléments à l'intérieur d'un voisinage sont des solutions et qu'ils ne sont pas trop nombreux, sinon chaque itération prendra beaucoup de temps. Il ne faut pas que les voisinages soient trop petits non plus
23. Autres problèmes et autres approches
213
sinon le risque est de ne pas explorer assez de solutions. Trouver le bon équilibre est délicat.
ILLUST RAT ION La description précédente est très générale. Il est dicile de voir d'emblée à quoi tout cela peut correspondre concrètement. Nous allons l'illustrer à l'aide du problème du voyageur de commerce vu au chapitre 18 qui est rappelé brièvement. On se donne un graphe complet G = (V, E) à n sommets dont chaque arête u, v a un poids w(u, v) strictement positif. L'objectif est de trouver dans ce graphe un cycle hamiltonien (cycle à n arêtes contenant les n sommets de G) de poids minimal. Ici, une solution est un cycle hamiltonien et la mesure d'une solution est son poids (la somme des poids de ses arêtes). La solution de départ de la descente locale est un cycle hamiltonien quelconque (construit aléatoirement par exemple) s0 . Le voisinage d'une solution s considéré ici est l'ensemble des cycles hamiltoniens obtenus à partir de s en supprimant deux arêtes u, v et x, y, puis en reconnectant les deux bouts du cycle en ajoutant u, x et v, y ou u, y et v, x suivant la conguration obtenue après la suppression. La gure 23.1 représente un graphe complet à n = 9 sommets et un cycle hamiltonien (arêtes en pointillés). Les poids ne sont pas donnés ici car on cherche simplement à illustrer la notion de voisinage d'une solution, ce qui est sans rapport avec les poids.
Figure 23.1: Un graphe complet à neuf sommets et un cycle hamiltonien (pointillés) Le voisinage de ce cycle est l'ensemble de tous les cycles hamiltoniens obtenus à partir de celui-là en faisant l'opération décrite plus haut. La gure 23.2 présente deux éléments de ce voisinage. Les arêtes supprimées de s sont dessinées sous forme de points et les deux nouvelles arêtes sont représentées en pointillés.
Graphes et algorithmes
214
Figure 23.2: Deux solutions voisines de celle de la gure 23.1 Il devient alors possible d'appliquer une descente locale comme expliqué plus haut en partant d'un cycle initial quelconque et en utilisant à chaque pas un voisinage construit de cette manière. La stratégie consiste simplement à prendre le cycle de coût minimal dans ce voisinage comme point de départ de l'étape suivante de la descente. Cette dernière s'arrête lorsque le cycle courant a un coût minimal dans son voisinage (c'est alors un minimum localement, dans son voisinage).
Métaheuristiques et algorithmes d'approximation.
Une manière intéressante de faire cohabiter les deux approches est de construire une solution initiale à partir d'un algorithme d'approximation (s'il y en a un), puis de lui appliquer un processus d'amélioration sous forme de métaheuristique. Cela permet d'obtenir alors une solution de qualité garantie par rapport à l'optimale, qui est ensuite améliorée. En se xant une limite du nombre d'essais à réaliser (nombre de pas maximal de descentes par exemple), il est possible de contrôler le temps d'amélioration.
Les problèmes non approchables à un facteur constant.
Certains problèmes sont vraiment très diciles à résoudre. Parfois, on ne sait même pas les approcher à un facteur constant. Le facteur d'approximation peut être une fonction croissante non bornée du nombre n de sommets. Dans ce cas, il est évident que l'approximation ne donne pas de bons résultats pratiques car l'incertitude sur la qualité du résultat est trop importante. Parmi ces problèmes, citons ceux qui suivent. Extraire d'un graphe la clique (graphe complet) de taille maximale. Extraire d'un graphe l'indépendant (graphe sans arête) de taille maximale. Colorier le graphe avec un nombre minimal de couleurs de manière à ce que deux sommets voisins n'aient pas la même couleur (le chapitre 15 présente ce problème et décrit un algorithme mais sans garantie par rapport à l'optimal).
23. Autres problèmes et autres approches
215
Construire un cycle hamiltonien de poids minimal dans un graphe complet pondéré (voir chapitre 18) lorsque les poids des arêtes ne vérient pas l'inégalité triangulaire. Une idée de ce point est donnée dans la zone rouge juste après.
Zone rouge Le problème du cycle hamiltonien dans le cas où les coûts ne vérient pas l'inégalité triangulaire. Dans le chapitre 18, nous avons étudié
le cas où les coûts des arêtes vérient l'inégalité triangulaire : w(u, v) ≤ w(u, x) + w(x, v) quels que soient les sommets u, v et x. Cette propriété a souvent été utilisée dans l'analyse de la qualité du résultat construit par les algorithmes d'approximation. Imaginons maintenant que les coûts sur les arêtes ne vérient plus nécessairement cette inégalité. L'objectif est donc de résoudre ce problème sur des familles plus larges de graphes (toujours complets) pondérés. On peut montrer que si P = N P , il n'y a plus d'algorithme raisonnable (de complexité polynomiale) ayant un rapport d'approximation constant, même gros . Donnons une idée de ce résultat. Imaginons qu'il existe un algorithme d'approximation A pour ce problème avec un rapport d'approximation constant r. Cela veut dire que si G est un graphe complet pondéré (avec pondérations quelconques), alors l'algorithme A est capable, avec une complexité polynomiale, d'extraire de G un cycle hamiltonien C dont le poids w(C) est au plus r fois le poids d'un cycle hamiltonien optimal, que l'on notera w∗ . Ce qui se traduit par : w(C) ≤ rw∗ . Nous allons voir que faire cette hypothèse de l'existence d'un tel algorithme A conduit à contredire un résultat connu. Considérons maintenant un graphe H = (V, E) connexe quelconque, non pondéré, à n sommets. Construisons un graphe complet G qui a les mêmes sommets V que H et dans lequel toute arête u, v pour toute paire u et v de sommets (car G est complet) : a un poids égal à 1 s'il y a une arête entre u et v dans H et a un poids égal à r.n + 1 sinon (où n est le nombre de sommets de H et r est le rapport d'approximation de l'algorithme A). La gure 23.3 donne un exemple d'un graphe H à n = 5 sommets et du graphe complet correspondant (où les arêtes qui ne sont pas dans H de poids r.n + 1 = 5r + 1 ont été colorées pour les distinguer).
216
Graphes et algorithmes
Figure 23.3: (a) Un graphe H à n=5 sommets. (b) Le graphe complet G associé Le graphe G peut facilement être construit à partir du graphe H initial. Remarquons maintenant que si H a un cycle hamiltonien C , alors le cycle hamiltonien de poids minimal de G est de poids exactement n. Il sut en eet de prendre dans G les arêtes de C qui sont aussi des arêtes de G et qui sont de poids 1. C'est le cas dans l'exemple de la gure 23.3. H a un cycle hamiltonien 1, 3, 2, 4, 5, 1 qui est aussi un cycle hamiltonien de poids n = 5 de G (gure 23.3 (b)). Il est clair que G ayant cinq sommets, un cycle hamiltonien est de poids au moins 5, ce qui montre l'optimalité. D'un autre côté, si H n'a pas de cycle hamiltonien, alors tout cycle hamiltonien du graphe complet G devra utiliser une arête u, v qui n'est pas une arête de G et qui est de poids r.n + 1 par construction. Dans ce cas, tout cycle hamiltonien de G sera de poids au moins : n − 1 + r.n + 1 > r.n. Munis de ces résultats préliminaires, faisons les opérations suivantes. À partir du graphe H = (V, E), construisons le graphe complet pondéré G et appliquons-lui l'algorithme A qui permet d'extraire un cycle hamiltonien C de poids w(C). Si H a un cycle hamiltonien, il a été vu plus haut que G en a un de poids n, ce qui est optimal. Comme l'algorithme A renvoie une solution r-approchée, le cycle construit est de poids au plus r.n : w(C) ≤ r.n. Or on a vu que si un cycle hamiltonien de G contient une arête qui n'est pas une arête de H , alors son poids est nécessairement strictement supérieur à r.n. Le cycle C construit par cette méthode ne possède donc aucune arête de ce type, il est donc de poids total exactement égal à n. Si H ne contient aucun cycle hamiltonien, alors tout cycle hamiltonien de G, donc aussi C , est de poids strictement supérieur à r.n : w(C) > r.n. Les manipulations précédentes permettent donc d'avoir un algorithme, utilisant A (comme sous-routine), de complexité polynomiale, et qui permet, en prenant n'importe quel graphe H de savoir s'il a ou non un cycle hamil-
23. Autres problèmes et autres approches
217
tonien, simplement en comparant le poids de C à la valeur r.n. Cela veut dire que cet algorithme polynomial permet de résoudre de manière exacte le problème du cycle hamiltonien qui est dicile. Si la conjecture selon lequelle tous les problèmes diciles ne peuvent pas être résolus par des algorithmes polynomiaux est vraie (si P = N P ), alors cela permet de montrer que le problème du cycle hamiltonien de poids minimal dans les graphes complets pondérés (sans hypothèses particulières sur les poids) ne peut pas être résolu avec un algorithme d'approximation de rapport r constant.
Des algorithmes d'approximation pour des problèmes polynomiaux.
Certains problèmes peuvent être résolus grâce à des algorithmes polynomiaux. Mais ces algorithmes sont parfois diciles à mettre en ÷uvre, ou ont une complexité élevée (même si elle reste polynomiale). Cela peut être pénalisant si le logiciel qui utilise cet algorithme doit donner une réponse rapidement. Parfois la réponse attendue n'a pas besoin d'être extrêmement précise, une approximation (contrôlée) surait. C'est pour répondre à ce type de préoccupations que des algorithmes d'approximation ont aussi été proposés pour certains problèmes faciles . Dans ce cas, il faut contrôler et donner des garanties analytiques non seulement sur la qualité de la solution construite (rapport d'approximation), mais aussi sur la complexité de la méthode qui doit être bien moins élevée que celle donnant la solution optimale.
Conclusion.
Les algorithmes d'approximation sont une façon de contourner la diculté de résolution de certains problèmes. Ils permettent de construire des solutions dont on arrive à prouver qu'elles sont dans un certain intervalle. La diculté est bien souvent analytique : comment faire la démonstration du rapport d'approximation ? Il n'y a pas une seule méthode universelle. Nous en avons vu quelques-unes. Bien d'autres existent mais seraient très diciles à exposer dans un ouvrage d'introduction. Il faut aussi être conscient que de nouveaux algorithmes d'approximation sont créés chaque année, que des doctorants travaillent sur ces sujets dans des laboratoires de recherche en informatique, que des projets académiques sont nancés en France et ailleurs pour faire avancer ces connaissances et ces méthodes. L'objectif est double. Le premier est fondamental et consiste à mieux comprendre pourquoi certains problèmes sont diciles en essayant de les résoudre. Cette question de l'informatique fondamentale P = NP ? (mise à prix : un million de dollars par l'Institut de mathématiques Clay aux États-Unis) intrigue (voir chapitre 14). À l'heure actuelle, personne ne sait y répondre. Le second objectif est pratique et provient du besoin concret d'algorithmes à la fois rapides et précis pour créer des logiciels performants. Certains problèmes résistent et personne ne sait comment les résoudre de manière performante. En ce qui concerne les graphes en eux-mêmes, ils sont susamment universels pour intervenir dans de nombreux domaines. Ils font maintenant partie de la culture scientique de base que personne ne devrait ignorer. Ce petit livre avait pour vocation non pas de faire de vous des experts, mais de vous faire découvrir ce domaine passionnant qui a de multiples facettes. J'espère qu'il aura au moins réussi ce modeste pari.
24
Quelques références et compléments
Cet ouvrage sera réussi s'il vous a donné envie d'aller plus loin, d'en savoir plus, d'approfondir. Voici quelques références pour cela.
Quelques livres. Les livres sur les graphes ou les algorithmes sont nombreux mais pas toujours simples à lire pour un débutant. On se restreint ici à quelques ouvrages qui peuvent encore être trouvés en librairie ou dans des bibliothèques (universitaires). La liste qui suit n'est pas du tout exhaustive. Un livre que l'on trouve encore, bien que publié en 1985, est Introductory Graph Theory de G. Chartran, éditeur : Dover Publications Inc. Livre en anglais mais pas très cher et lisible par un débutant. D'autres livres d'introduction aux graphes sont disponibles chez cet éditeur mais je n'ai lu que celui-là. Graph Theory de R. Diestel chez l'éditeur Springer-Verlag, livre en anglais. Le niveau est avancé. L'auteur fait des mises à jour de son contenu assez régulièrement. Ce livre est consultable gratuitement (et légalement) sur Internet. Un autre livre dont le titre est aussi Graph Theory de A. Bondy et J. Murty, lui aussi édité chez Springer-Verlag, également en anglais (version de 2007). Ce livre est souvent cité dans les articles. Une sorte de classique mais là aussi d'un niveau assez avancé. Voici quelques livres dont une ou plusieurs parties sont consacrées aux graphes ou aux algorithmes de graphes. Commençons par la bible dans le domaine des algorithmes : Algorithmique de T. Cormen, C. Leiserson, R. Rivest et C. Stein, édition de 2010 parue chez Dunod. Ce livre en français (traduction depuis l'anglais/américain par des universitaires informaticiens) de presque 1 300 pages est un traitement très complet de l'algorithmique, dont une partie seulement est consacrée aux algorithmes de graphes. À lire impérativement si vous devez programmer ces algorithmes. Une large part de l'ouvrage est consacrée aux structures de données qui sont des moyens de représenter et de manipuler les données dans la mémoire d'un ordinateur. On se rapproche ici de problématiques de logiciels.
Graphes et algorithmes
220
Algorithmes d'approximation de V. Vazirani, éditeur : Springer Éditions, 2010.
Raisonnements divins : Quelques démonstrations mathématiques particulièrement élégantes de G. Ziegler et K. Hofmann, éditeur : Springer-Verlag France,
Livre en français (traduction) consacré aux algorithmes d'approximation dont de nombreux pour les graphes. Attention, niveau avancé.
2013 (pour la 3e édition). On y trouvera quelques beaux résultats avec des preuves élégantes dans plusieurs domaines des mathématiques dont la combinatoire et la théorie des graphes. Les divers livres en français et en anglais de mon collègue Vangelis Th. Paschos du laboratoire parisien LAMSADE sont aussi une source intéressante (mais assez avancée) d'information sur les algorithmes d'approximation et de manière plus large sur l'optimisation discrète. Les livres (et articles) de vulgarisation en informatique théorique et en mathématiques de Jean-Paul Delahaye aux éditions Belin, en français, contiennent aussi parfois des chapitres sur les graphes. J'en conseille la lecture.
Quelques logiciels
Pour manipuler un gros graphe, un papier et un crayon ne sont pas les outils les plus adaptés. Il faut des logiciels. En voici quelques-uns (liste absolument non exhaustive). Ces outils informatiques sont en perpétuelle évolution. C'est pour cela qu'aucune mention de version n'est indiquée dans ce qui suit.
Maple
(http://www.maplesoft.com ) et Mathematica (http://www.wolfram. com/mathematica) sont deux logiciels très puissants qui permettent, entre beaucoup d'autres choses, de manipuler des graphes (les créer, les combiner, les afcher, les modier, appliquer des algorithmes dessus, etc.). Ce sont des outils professionnels dont des versions moins chères sont en général disponibles pour les étudiants.
Sage
(http://www.sagemath.org ) est lui aussi un tel outil mais totalement gratuit.
Le logiciel yed (http://www.yworks.com/en/products/yfiles/yed ) est d'usage beaucoup plus restreint que les précédents puisqu'il permet simplement (mais c'est déjà très bien !) d'éditer des graphes, de les dessiner, de les colorier, d'ajouter des étiquettes mais aussi de les exporter dans divers formats (comme le pdf par exemple), tout cela avec une souris, de manière assez intuitive. À l'heure où ce livre est écrit, ce logiciel est gratuit.
Quelques pages web.
Les pages Wikipedia consacrées aux graphes sont en général pertinentes mais pas toujours très détaillées. En revanche, on pourra y trouver les références aux articles originaux (qui ne sont pas toujours disponibles gratuitement). Les pages en anglais sont souvent plus complètes. Quelques blogs sont consacrés aux graphes. Mais la pérennité dans le temps de ces publications n'est pas assurée. Vous devrez donc passer par un moteur de recherche pour les débusquer. Aucune garantie n'est donnée sur la validité de ce qu'ils présentent.
24. Quelques références et compléments
221
Quelques revues.
Le magazine Tangente (pôle éditions) propose parfois des articles sur les graphes ou la combinatoire. En particulier, le hors-série numéro 54 de ce magazine y est entièrement consacré. La rubrique de Jean-Paul Delahaye dans le magazine Pour la Science traite parfois de problèmes de graphes. Ces rubriques sont éditées sous forme de livres aux éditions Belin (voir plus haut). À côté de ces quelques revues destinées au grand public, auxquelles on peut ajouter La Recherche, il existe de nombreuses revues professionnelles, universitaires, consacrées aux mathématiques discrètes, à la combinatoire et aux graphes. Les prix et le niveau de ces revues font qu'elles sont réservées aux laboratoires de recherche. Vous pourrez peut-être les consulter dans la section recherche d'une bibliothèque universitaire. Inutile de les chercher dans le kiosque à journaux du coin de la rue.
Comment représenter un graphe dans la mémoire d'un ordinateur ?
Après avoir manipulé des graphes à la main dans les chapitres précédents, certains lecteurs peuvent se demander comment un tel objet peut être représenté dans la mémoire d'un ordinateur. Il existe plusieurs moyens pour cela. Pour les décrire, il faudrait entrer dans des détails pointus de programmation qui ne sont pas l'objet de ce livre. Nous allons simplement en détailler un assez simple à comprendre. Prenons un graphe G = (V, E) à n sommets. Chaque sommet doit être associé à un numéro unique entre 1 et n (ou entre 0 et n − 1). Il faut ensuite créer une matrice M , n × n sous la forme d'un tableau dans la mémoire de la machine. Les arêtes sont codées par des 1 dans cette matrice avec la règle suivante : si G contient l'arête i, j, alors M [i, j] = M [j, i] = 1, sinon M [i, j] = M [j, i] = 0. Avec cette règle, la matrice est symétrique, c'est-à-dire que M [i, j] = M [j, i]. On note aussi que la diagonale n'est composée que de 0 (M [i, i] = 0 pour tout i). Illustrons cela avec le graphe suivant dont tous les sommets ont déjà un numéro entre 1 et 4.
La matrice M qui correspond à ce graphe est :
1 2 3 4
1 0 1 1 1
2 1 0 0 1
3 1 0 0 0
4 1 1 0 0
Examinons quelques cases. M [2, 3] = M [3, 2] = 0 car le graphe ne contient pas l'arête entre les sommets 2 et 3. En revanche, M [3, 1] = M [1, 3] = 1 car il contient une arête entre 1 et 3.
Graphes et algorithmes
222
Pour savoir si les sommets i et j sont voisins, il sut de lire le contenu de la case M [i, j] (ou M [j, i]) et de la comparer à la valeur 1, ce qui est très rapide.
Codages d'autres graphes avec une matrice.
L'avantage de ce codage est qu'il permet de représenter un graphe orienté avec un principe similaire : si G a un arc (i, j), alors M [i, j] = 1. En revanche, si G n'a pas l'arc (j, i), alors M [j, i] = 0. La matrice d'un graphe orienté n'est donc pas forcément symétrique. Il est aussi facile et pratique de représenter un graphe pondéré (orienté ou non) : la case M [i, j] reçoit la valeur du poids de l'arc ou de l'arête entre i et j (et la valeur 0 s'il n'y a pas de lien entre les deux).
Représentation d'autres éléments en mémoire.
Lors du parcours en profondeur ou en largeur, les sommets sont coloriés au fur et à mesure qu'ils sont visités (pour éviter de les visiter plusieurs fois). Dans le problème de la coloration vu au chapitre 15, l'objectif nal est d'attribuer une couleur aux sommets. Colorier les sommets est une activité courante et très utile. Mais comment représenter ces couleurs ? Chaque couleur est représentée par un entier : 1, 2, . . . Il sut de prendre un tableau C à une seule ligne et n colonnes. La valeur de la case C[i] représente alors la couleur du sommet i. Il est facile de modier la couleur d'un sommet en allant modier le contenu de la case qui lui est associée. Plus généralement, il est important de pouvoir stocker des données complexes de manière à ce qu'elles soient facilement accessibles, aussi bien en lecture (lire une valeur) qu'en écriture (ajouter ou modier une valeur). Pour cela, il faut faire appel à des structures de données plus compliquées à manipuler que de simples matrices, dont certaines sont organisées sous forme d'arbres. On en revient encore une fois aux graphes, mais cette fois bien cachés dans les entrailles des logiciels.
Index Arbre, 4, 11, 49 Arbre couvrant, 33, 61, 164, 166, 170, 181, 188, 195 Arbre couvrant de poids minimal, 41, 46, 174, 183 Arc, 93, 110, 124 Arête, 3, 4, 7 Chemin, 3, 9, 17, 18 Connexe, 23, 25, 41, 163 Couplage, 4, 103105, 107, 123, 149, 158, 167, 175 Distance, 17 Graphe biparti, 23, 25, 103, 104, 140, 162 Graphe complet, 14, 74, 117, 118, 140, 144, 162, 169, 183, 217 Problème dicile, 134, 140, 151, 154, 163, 181, 199, 208, 221 Sommet, 7