Université de Mons Faculté des Sciences Institut d’Informatique Service d’informatique théorique Algorithme de recherche dans des espaces variables appliqué au problème de coloration de graphes Directeur : Mr Hadrien M ÉLOT Rapporteurs : Mme Véronique B RUYÈRE Mr Olivier G AUWIN Année académique 2010-2011 Projet réalisé par Xavier D UBUC i Remerciements Je remercie mon directeur, Hadrien Mélot, pour sa disponibilité, son aide précieuse et la relecture du présent document. Je remercie aussi mes rapporteurs, Véronique Bruyère et Olivier Gauwin pour leurs remarques et conseils judicieux après lecture du pré-rapport. Je remercie également mon frère, Jérôme Dubuc, pour la correction orthographique et syntaxique ainsi que mon ami, Jérôme Descamps, pour l’aide apportée pour rendre le texte plus accessible. Finalement, un remerciement spécial à ma fiancée Morgane Capelleman pour ses talents d’infographiste bien utiles pour certaines images. ii Table des matières Introduction 1 2 3 1 La coloration de graphes 1.1 Rappel théorique . . . . . . . . . . . . . . . 1.2 Définition du problème . . . . . . . . . . . . 1.3 Historique . . . . . . . . . . . . . . . . . . . 1.4 Applications . . . . . . . . . . . . . . . . . . 1.5 Bornes sur le nombre chromatique . . . . . . 1.6 Algorithmes de calcul du nombre chromatique La recherche par espaces variables 2.1 Idée générale . . . . . . . . . . . . . 2.2 Intuition de fonctionnement . . . . . . 2.3 3 ensembles pour la k-coloration . . . 2.4 Traducteurs pour la k-coloration . . . 2.5 Algorithmes sous-jacents . . . . . . . 2.6 Algorithme VSS pour la k-coloration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 3 6 7 11 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 16 17 18 21 24 29 Une application du VSS 3.1 Modélisation . . . . . . . . . . . . . . . . . . 3.1.1 Structures . . . . . . . . . . . . . . . . 3.1.2 Heuristique . . . . . . . . . . . . . . . 3.2 Implémentation . . . . . . . . . . . . . . . . . 3.2.1 Structures de données & représentation 3.2.2 Algorithmique . . . . . . . . . . . . . 3.3 Résultats . . . . . . . . . . . . . . . . . . . . . 3.4 Difficultés . . . . . . . . . . . . . . . . . . . . 3.4.1 Compréhension . . . . . . . . . . . . . 3.4.2 Implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 34 34 36 38 38 42 45 49 49 50 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . 51 TABLE DES MATIÈRES iii Annexes 54 A Exemple de la Belgique 54 B Améliorations de TabuCol 56 1 Introduction Le problème de coloration de graphes (Graph Coloring Problem) (GCP) est un problème NP-difficile bien connu. Il consiste à colorier les sommets d’un graphe de telle manière que deux sommets adjacents ne soient pas coloriés avec la même couleur. Le but ultime est d’utiliser le moins de couleurs possible. Le nombre minimal de couleurs nécessaires pour colorier un graphe est appelé nombre chromatique. Les aboutissants de la résolution de ce problème sont souvent méconnus. Et pourtant, ce dernier est employé dans de nombreuses applications telles que l’affectation de fréquences dans les réseaux radio-mobiles et les problèmes d’optimisation avec contraintes (ordonnancement, établissement de planning, ...). Il peut également servir de base pour résoudre un sudoku [1, 2]. Ce problème paradigmatique (c’est-à-dire qu’il s’agit d’un problème simple et exemplaire) étant NP-difficile, il n’existe pas à ce jour d’algorithme performant (polynômial) pour le résoudre, il faut donc faire appel aux métaheuristiques. Le but de ce projet est d’appliquer une métaheuristique (récente) à la coloration d’un graphe et à la recherche du nombre chromatique de celui-ci. Cette métaheuristique s’appelle Variable Space Search (VSS), littéralement “Recherche dans des espaces variables” et a été proposée par Hertz, Plumettaz et Zufferey en 2008 [3]. Ce rapport consiste dans un premier temps à définir le problème de coloration de graphes (Graph Coloring Problem) (GCP), à relater son origine, son importance et l’état de l’art. Ces trois sujets seront traités dans le Chapitre 1. La métaheuristique employée dans le cadre de ce projet est, quant à elle, développée dans le Chapitre 2 et une application de celle-ci sera explicitée dans le Chapitre 3. 2 Chapitre 1 La coloration de graphes La coloration de graphes est un problème simple consistant à employer le moins de couleurs possible pour colorier un graphe de manière à ce que 2 sommets adjacents ne soient pas de la même couleur. Ce problème n’est défini que sur des graphes sans boucle (c’est évident, sinon un sommet, adjacent à lui même via la boucle ; devrait être colorié par deux couleurs différentes) et habituellement sur des graphes non-orientés. Le présent chapitre pose les bases théoriques et formelles du problème ainsi que le vocabulaire utilisé. Il présente également un court historique, l’importance de ce problème et l’état de l’art. 1.1 Rappel théorique Par souci de clarté, une définition formelle d’un graphe est requise afin de fixer les notations et le vocabulaire utilisés. Cette définition est celle proposée par Diestel [4]. Définition 1.1.1. Un graphe G = (V, E) est une structure de données caractérisées par les ensembles V et E. Ces ensembles sont définis comme suit : – V est l’ensemble des sommets (aussi appelés noeuds) (V comme Vertex), – E ⊆ V × V est l’ensemble des arêtes qui représentent les liens entre les sommets (E comme Edge). Il existe deux grandes familles de graphes : – les graphes non-orientés, dans lesquels un sommet x est relié à un sommet y si et seulement si y est relié à x ou, autrement dit, la présence de l’arête (x, y) implique l’existence de l’arête (y, x) ; – les graphes orientés, dans lesquels l’existence de l’arête orientée (x, y) n’implique pas spécialement l’existence de (y, x). Les arêtes orientées portent aussi le nom d’arc. Les figures 1.1 et 1.2 montrent une représentation graphique d’un exemple pour chacun de ces types de graphes. CHAPITRE 1. La coloration de graphes 3 1 2 3 4 F IGURE 1.1 – Graphe non-orienté 1 2 4 3 F IGURE 1.2 – Graphe orienté 1.2 Définition du problème A présent, les graphes étant formellement définis, le problème de coloration d’un graphe peut être posé de manière claire et formelle, lui aussi. Définition 1.2.1. La k-coloration d’un graphe G = (V, E) peut être vue comme une fonction : c : V → {1, 2, ..., k} qui assigne à chaque sommet v de G un nombre c(v) appartenant à {1, 2, ..., k} appelé couleur. La coloration d’un graphe n’est pas un problème en soi au vu de la définition, le problème est de rendre cette coloration légale. Une coloration légale est une coloration possédant certaines propriétés qu’il est nécessaire d’énoncer avant de la définir. Définition 1.2.2. Une coloration définit des classes de couleur Vi (i ∈ {1, ..., k}) qui forment une partition de l’ensemble V , la classe de couleur Vi contenant tous les sommets de couleur i. CHAPITRE 1. La coloration de graphes 4 Définition 1.2.3. Une arête (x, y) est dite conflictuelle si c(x) = c(y) ou, en terme de classes de couleur si Vc(x) = Vc(y) . De manière simple, il s’agit d’une arête reliant deux sommets de même couleur. Par exemple, dans la figure 1.3, l’arête (1, 2) est conflictuelle. conflit 1 2 3 4 F IGURE 1.3 – Arête conflictuelle Définition 1.2.4. Une coloration est dite partielle si tous les sommets ne sont pas coloriés, autrement dit, si ∃v ∈ V tel que c(v) n’est pas définie. Dans ce cas, la fonction c n’est pas totale. La figure 1.4 donne un exemple de coloration partielle, c(4) n’existant pas. 1 2 3 4 F IGURE 1.4 – Coloration partielle Définition 1.2.5. La k-coloration d’un graphe G = (V, E) est dite légale si et seulement si : – cette coloration n’est pas partielle, – aucune arête de E n’est conflictuelle. G est dit k-colorable. CHAPITRE 1. La coloration de graphes 5 1 2 3 4 F IGURE 1.5 – Coloration légale La figure 1.5 montre une 4-coloration légale d’un graphe. Le problème de décision consistant à savoir si un graphe G donné est k-colorable pour un k donné est un des aboutissants du projet. Le principal problème, qui est le problème d’optimisation associé, consiste quant à lui à trouver le nombre chromatique d’un graphe : Définition 1.2.6. Le nombre chromatique d’un graphe G est le plus petit nombre entier l tel que G est l-colorable. En d’autres termes, c’est l’entier l tel que G est l-colorable et tel qu’il n’existe pas k < l tel que G est k- colorable. Ce nombre est noté χ(G). Pour terminer cette section, le vocabulaire spécifique ayant été défini, voici un énoncé concis et précis des deux problèmes qui sont traités dans ce projet : – le problème de décision de k-coloration d’un graphe (k-GCP) consiste, pour un k et un graphe G donnés à déterminer si G est k-colorable. Ce problème est NP-complet. – le problème d’optimisation de coloration d’un graphe (GCP, Graph Coloring Problem) consiste à déterminer le nombre chromatique d’un graphe G donné. Ce problème est NP-difficile. Il est à remarquer que les deux problèmes sont liés et l’un peut toujours servir de base pour résoudre l’autre. En effet, le GCP permet de résoudre le k-GCP tout simplement en s’arrêtant dès que l’algorithme a fourni une k- coloration légale (ou une l-coloration avec l < k, si une telle coloration existe). Inversement, le k-GCP, démarré avec une borne supérieure de χ(G) (typiquement, le nombre de noeuds) et où k est décrémenté tant que G est k-colorable ; donne une solution pour le GCP. Il existe de meilleures valeurs pour la borne supérieure de χ(G), données par des expérimentations diverses menées par des chercheurs (voir section 1.5). CHAPITRE 1. La coloration de graphes 1.3 6 Historique Cette section présente un court historique de la branche de la théorie des graphes (l’étude des graphes) consacrée à la coloration des graphes. Cet historique débute en 1852 lorsqu’un mathématicien sud-africain, Francis Gutrie, postule la "four color conjecture" à partir d’observations faites sur la coloration d’une carte géographique des comtés d’Angleterre. La coloration des régions de cette carte devait respecter le fait que deux régions partageant une frontière ne pouvaient pas être coloriées avec la même couleur. Comme le nom de la conjecture l’indique, il s’est rendu compte que pour effectuer un tel travail, seulement quatre couleurs étaient nécessaires [5]. Il venait de poser les bases de la coloration des graphes. Effectivement, une carte géographique simplifiée (dans le sens où tous les pays sont considérés comme contigus) peut être facilement représentée par un graphe particulier, un graphe planaire ; le lien entre la coloration de carte et la coloration de graphe est donc vite établi. La simplification de la carte n’est pas toujours possible dans la réalité (par exemple, les États-Unis avec l’Alaska). (Pour rappel, un graphe planaire est un graphe qui peut être représenté dans le plan de manière à ce que ses arêtes ne se croisent qu’en ses sommets) Un exemple de la marche à suivre pour effectuer cette coloration est présenté dans l’annexe A. L’intuition pour trouver qu’il existe des cartes nécessitant quatre couleurs est d’avoir un pays entouré par trois autres qui se touchent tous, comme par exemple le Luxembourg (voir Figure 1.6). La difficulté du problème est de prouver que quatre couleurs sont toujours suffisantes. F IGURE 1.6 – Carte d’Europe partielle 4-coloriée CHAPITRE 1. La coloration de graphes 7 En effet, beaucoup de mathématiciens ont essayé de prouver cette "four color conjecture", en vain. Celle-ci, devenue un théorème, est d’ailleurs connue pour avoir donné lieu à la première preuve majeure assistée par ordinateur (dirigée par Appel & Haken en 1989 [6]). Beaucoup de théories sur les graphes ont été développées suite à cette introduction de la coloration, comme, par exemple, le polynôme chromatique de Birkhoff [7], le polynôme de Tutte [8] ou encore le "Strong perfect graph theorem" prouvé en 2002 par Chudnovsky, Robertson, Seymour & Thomas [9]. Ce problème figure également dans les célèbres 21 problèmes NP-complets de Karp [10]. 1.4 Applications En quoi la coloration de graphes est-elle importante, ou tout du moins, utile ? Cette question est le fil rouge de cette section dans laquelle sont présentées quelques applications pratiques où la coloration de graphes joue un rôle. Ces applications sont assez variées, allant du domaine du jeu avec la résolution de sudoku à l’ordonnancement de tâches (tant au niveau d’un processeur qu’au niveau de la gestion d’emploi du temps de personnes) en passant par l’allocation de fréquences dans les réseaux radio-mobiles ou encore des résolutions de problèmes plus mathématiques comme le Constraint Satisfaction Problem (CSP). Allocation de fréquences De manière simple et intuitive, certains réseaux de télécommunication étant composés de plusieurs émetteurs, ceux-ci sont confrontés à un problème majeur : les interférences. En effet, si deux émetteurs émettent sur la même fréquence, dès lors qu’ils sont trop proches l’un de l’autre et qu’ils émettent en même temps, de manière irrémédiable des interférences ont lieu (à moins qu’ils soient séparés par une montagne ou quelque chose empêchant la propagation des ondes). La coloration de graphes peut permettre de résoudre ce problème. Pour ce faire, il convient d’associer un graphe au réseau, ce graphe aura comme sommets les différents émetteurs et une arête est placée entre ces émetteurs lorsqu’ils sont trop proches (ou, tout du moins lorsqu’il est désirable qu’ils n’émettent pas à la même fréquence). Dès lors, si ce graphe est de nombre chromatique k, alors le réseau doit être capable d’utiliser au moins k fréquences différentes et il convient d’affecter des fréquences différentes à des noeuds n’ayant pas la même couleur. CHAPITRE 1. La coloration de graphes 8 Ordonnancement L’ordonnancement de tâches consiste à ordonner ces dernières de manière cohérente et sans conflit (une tâche peut être prioritaire sur une autre, la dernière utilisant peut-être les résultats de la première). Dans les systèmes actuels, plusieurs processeurs sont utilisés pour une seule machine ce qui permet à deux tâches d’être exécutées de manière concurrente. Il se peut dès lors que ces deux tâches entrent en conflit si elles utilisent les mêmes ressources. En considérant un graphe construit de telle sorte que chaque noeud est une de ces tâches et que deux tâches sont reliées si elles utilisent les mêmes ressources, il est possible, avec la coloration de graphes, de trouver une manière d’ordonnancer ces tâches sans qu’elles n’entrent en conflit réel (sous l’hypothèse qu’un tel ordonnancement soit possible). Planification d’horaires De manière assez semblable à l’ordonnancement, la génération du planning d’une ou plusieurs personnes peut se faire à partir de la coloration de graphes. Par exemple, un professeur d’université donnant plusieurs cours sur toute une année ne peut en aucun cas donner deux cours différents au même moment. Sous forme d’un graphe, chaque cours est représenté par un sommet et deux cours sont reliés s’ils sont donnés par le même professeur. Il est alors aisé de transposer le résultat de la coloration du graphe ainsi construit à l’allocation de laps de temps pendant lesquels le professeur donne effectivement ses cours. Algo1 IA SDD SDD2 AlgoApprox Algo2 CompNet1 CompNet2 Compil CalcEtCompl F IGURE 1.7 – Exemple de graphe de planning CHAPITRE 1. La coloration de graphes 9 Ainsi, par exemple, tous les noeuds coloriés en rouge représentent des cours qui doivent être donnés le lundi de 8h à 10h. (le graphe renseigne uniquement le fait que deux cours de la même couleur peuvent être donnés en même temps sans donner lieu à des conflits, la génération du planning ne dépend donc pas que du graphe) Aspect ludique : résolution de sudoku Voici le problème qui est posé : résoudre un sudoku semblable à celui représenté par la table 1.1. Pour rappel, résoudre un sudoku signifie remplir chaque case de ce tableau avec un chiffre de l’ensemble {1, 2, 3, 4, 5, 6, 7, 8, 9} de telle sorte qu’il n’y ait qu’une seule fois chaque chiffre dans une colonne, dans une ligne et dans un carré. 4 2 7 8 5 9 5 3 7 4 1 5 2 2 3 6 1 3 9 4 6 5 7 8 6 3 6 7 2 8 9 TABLE 1.1 – Sudoku à résoudre Il est possible de considérer ce sudoku comme un graphe de telle manière que la coloration de ce graphe donne une résolution de celui-ci. En effet, un sudoku n’est rien d’autre qu’un graphe particulier comportant 81 noeuds et qui, pour être résolvable, doit être 9-colorable. Dans ce graphe, les sommets correspondent aux cases, et chaque arête représente le fait que les deux cases ainsi reliées ne peuvent contenir le même chiffre. La numérotation suivante est utilisée : 1 10 19 28 37 46 55 64 73 2 11 20 29 38 47 56 65 74 3 12 21 30 39 48 57 66 75 4 13 22 31 40 49 58 67 76 5 14 23 32 41 50 59 68 77 6 15 24 33 42 51 60 69 78 7 16 25 34 43 52 61 70 79 8 17 26 35 44 53 62 71 80 9 18 27 36 45 54 63 72 81 CHAPITRE 1. La coloration de graphes 10 De cette manière, en ne considérant que les contraintes du carré (en abandonnant donc les contraintes de la ligne et de la colonne), le premier carré (donc les cases 1, 2, 3, 10, 11, 12, 19, 20 et 21) peut être représenté par le graphe dessiné en Figure 1.8. 10 11 1 2 12 21 19 20 3 F IGURE 1.8 – Graphe partiel d’un sudoku Il s’agit d’un graphe complet, c’est-à-dire qu’il possède le nombre maximal d’arêtes, ou autrement dit, que chaque sommet est relié à tous les autres. Le sudoku en entier est donc représentable par un graphe contenant neuf sousgraphes semblables à celui ci-dessus reliés entre eux de manière à tenir compte des contraintes de lignes et de colonnes. Le graphe contient alors au total 816 arêtes. Si une 9-coloration légale de ce graphe est trouvée, le sudoku est résolu. Selon les mathématiciens Russell et Jarvis, il y a 5.472.730.538 sudoku différents [11] qui sont résolvables et qui ont une solution unique, de quoi ravir les aficionados de ce sport cérébral. Aspect mathématique : CSP Le principe de ce problème est assez simple [12]. A partir d’un ensemble X de variables entières avec un domaine de valeurs permises pour chacune d’entre elles (tous ces domaines sont contenus dans un ensemble nommé D) ainsi que d’un ensemble de contraintes C, le CSP consiste à trouver une assignation des variables X avec des valeurs de leur domaine respectif qui satisfait à toutes les contraintes de C. Il est dès lors très facile d’exprimer le CSP comme un problème CHAPITRE 1. La coloration de graphes 11 de k-coloration et vice-versa. En effet, on peut mettre en relation : – les sommets du graphe avec les variables à assigner, – les contraintes avec des arêtes (et dans l’autre sens chaque arête (i, j) devient une contrainte disant que la variable xi doit être différente de la variable xj ), – les domaines en restreignant les couleurs autorisées pour certains noeuds (et dans l’autre sens toutes les variables doivent appartenir au même domaine : {1, ..., k}) 1.5 Bornes sur le nombre chromatique Beaucoup de résultats ont été trouvés dans cette branche. Ces résultats concernent principalement le fait de donner des bornes pour le nombre chromatique afin de limiter les zones de recherches pour certains types de graphes. Cette section énumére ces différentes bornes. Propriété 1.5.1. Le nombre chromatique d’un graphe G = (V, E), χ(G), est compris entre 1 et le nombre de noeuds de G, |V |. En effet, si G ne possède aucune arête, il est 1-colorable et dans le pire des cas, G est un graphe complet (graphe contenant toutes les arêtes possibles) et il est donc |V |-colorable. On a donc : 1 ≤ χ(G) ≤ |V |. 1 2 3 4 F IGURE 1.9 – Graphe complet 1 2 3 4 F IGURE 1.10 – Graphe sans arête Propriété 1.5.2. Si un graphe G contient une clique de taille k, c’est-à-dire un sous-graphe complet contenant k sommets, le nombre chromatique de G est supérieur ou égal à k. On pose w(G) comme étant la taille de la clique de taille maximale de G, on a donc : χ(G) ≥ w(G). (Exemple : voir Figure 1.11) CHAPITRE 1. La coloration de graphes 12 1 2 3 4 F IGURE 1.11 – Graphe avec une clique de taille maximale 3 Propriété 1.5.3. Soit ∆(G) le degré maximal d’un graphe G, ce nombre représente le nombre maximal d’arêtes incidentes à un noeud de G. Dès lors, il a été prouvé par un algorithme glouton [13] que G est colorable avec, au plus, ∆(G) + 1 couleurs. On a donc : χ(G) ≤ ∆(G) + 1. (Exemple : voir Figure 1.12) 1 2 3 Noeud 1 2 3 4 Degré 1 3 = ∆(G) 1 1 4 → χ(G) = 2 ≤ ∆(G) + 1 = 4 F IGURE 1.12 – Graphe 2-colorable G où ∆(G) = 3 Pour certains types de graphes, le nombre chromatique est connu. Il en va ainsi pour les graphes planaires (4-colorables, comme dit précédemment) et les graphes bipartis (2-colorables). Ces graphes bipartis sont des graphes dont l’ensemble de noeuds V peut être partitionné en deux sous-ensembles disjoints A et B de telle manière que chaque arête du graphe relie un sommet de A à un sommet de B. Une 2-coloration est donc simple à trouver, il suffit de colorier tous les sommets de A d’une couleur et tous les sommets de B d’une autre. (Les arbres sont des cas particuliers de ces graphes, par exemple) Prouver qu’un graphe est biparti peut être fait via un parcours en largeur adapté et donc en O(m + n) (m étant le nombre d’arêtes et n le nombre de noeuds). CHAPITRE 1. La coloration de graphes 1.6 13 Algorithmes de calcul du nombre chromatique De nombreux algorithmes permettant de calculer le nombre chromatique d’un graphe ont été développés au cours du temps, cette section effectue un court historique de ces algorithmes. Étant un problème N P -complet, les algorithmes exacts connus résolvant ce problème sont de complexité exponentielle. Il existe cependant des algorithmes polynômiaux pour des graphes particuliers (tester si un graphe est biparti par exemple permettant ainsi d’exhiber le fait que son nombre chromatique est 2). L’algorithme le plus basique est d’énumérer toutes les k-colorations possibles pour tous les 1 ≤ k ≤ |V | et de chercher une coloration légale dont le k est minimum. Le problème avec cette énumération c’est qu’elle est exponentielle. En effet, il y a 2n 2-colorations possibles, 3n 3-colorations possibles, etc les générer toutes depuis une certaine borne supérieure peut alors être très fastidieux. Pour exemple, si on prend un graphe de n = 10 noeuds et que l’on énumère toutes les k-colorations avec 1 < k ≤ 5, on obtient environ 1.09 107 colorations différentes. Pour résoudre ce problème, il est donc nécessaire de se fier à des heuristiques. Il en existe plusieurs donnant de bons résultats, la plus intuitive étant celle de Welsh-Powell [13] qui repose sur un tri topologique des sommets par ordre décroissant de degrés. Cet algorithme glouton propose de parcourir ces sommets triés (v1 , v2 , ... vi , ... v|V | ) l’un après l’autre en assignant à vi la plus petite couleur non utilisée par ses voisins dans v1 , v2 , ... vi−1 et en ajoutant une nouvelle couleur si aucune n’est disponible. En procédant de cette façon l’algorithme fournira une k-coloration où k sera au plus égal au degré maximum de G (cf. résultats plus haut). Par exemple, pour le graphe représenté en Figure 1.13, le tri topologique peut donner plusieurs ordres dont : 10, 3, 2, 8, 5, 11, 4, 1, 6, 7, 9. L’algorithme procède alors comme suit : – assigner la couleur 1 au sommet 10, – assigner la couleur 1 au sommet 3, – assigner la couleur 2 au sommet 2, – assigner la couleur 2 au sommet 8, – assigner la couleur 1 au sommet 5, – assigner la couleur 3 au sommet 11, – ... pour donner au final le résultat obtenu en Figure 1.14. CHAPITRE 1. La coloration de graphes 1 2 3 5 6 7 9 14 4 8 10 11 F IGURE 1.13 – Graphe pour l’algorithme glouton de Welsh-Powell 1 2 3 5 6 7 9 4 8 10 11 F IGURE 1.14 – Graphe colorié par l’algorithme glouton de Welsh-Powell Cette heuristique, bien qu’elle semble fonctionner correctement, ne garantit pas que le k trouvé soit le nombre chromatique de G. Elle ne rencontre donc pas les objectifs fixés. Une autre approche est également possible, celle des métaheuristiques. Une métaheuristique est une heuristique qui peut être employée dans plusieurs problèmes différents. Il en existe de deux types : – les métaheuristiques de population, qui utilisent une population de solutions qui évoluent au fil de l’exécution, – les métaheuristiques à solution unique, qui utilisent une seule solution qui s’améliore au fil de l’exécution. Dans le cadre de la coloration de graphes, les algorithmes les plus efficaces sont ceux utilisant ces métaheuristiques, il en existe des différents et variés. En effet, dans ce domaine, il existe un grand nombre d’articles traitant d’applications de métaheuristiques de population, comme par exemple les algorithmes génétiques et hybrides de Fleurent et Ferland [14] ou encore les algorithmes hybrides évolutifs de Galinier et Hao [15]. CHAPITRE 1. La coloration de graphes 15 D’autres algorithmes, basés sur une solution unique, également efficaces, ont été développés dans ce domaine. Parmi eux, il y a par exemple l’application de la recherche tabou à la coloration par Hertz et De Werra [16] ou, plus récemment, la métaheuristique utilisant des solutions partielles et un schéma tabou réactif de Bloechliger et Zufferey [17] ou encore la recherche par voisinage variable de Avanthay, Hertz et Zufferey [18]. Cette dernière métaheuristique est très importante car elle sert de base à la métaheuristique développée dans ce projet. En effet, en 2005, elle a été généralisée par la "Formulation Space Search" de Mladenovic [19] qui elle-même a été généralisée par Hertz, Plumettaz et Zufferey avec la recherche par espace variable (Variable Space Search) [3], métaheuristique dont fait l’objet ce projet et qui est présentée dans le chapitre suivant. 16 Chapitre 2 La recherche par espaces variables Ce chapitre développe la métaheuristique de Hertz, Plumettaz et Zufferey appelée Variable Space Search (Recherche par espaces variables, VSS). Le but est de présenter l’idée générale et de montrer comment ces personnes proposent de l’appliquer à la coloration de graphes. 2.1 Idée générale Avant de rentrer dans le vif du sujet, un petit rappel de la définition d’une métaheuristique à solution unique s’impose afin de fixer le vocabulaire utilisé. Une métaheuristique à solution unique est un algorithme d’optimisation qui est défini par trois éléments : – l’espace de recherche S, qui représente l’ensemble des solutions possibles au problème, – la fonction objectif f (s), qui permet d’évaluer la qualité de la solution s, – le voisinage N (s) (N (s) ⊆ S) qui représente un ensemble de solutions atteignables à partir de la solution s via une opération de voisinage (appelée aussi "move" ou mouvement). Afin d’appliquer une métaheuristique, il faut donc définir le problème au travers de ces trois éléments. La métaheuristique utilisée ici est un peu particulière. En effet, elle ne considère pas seulement un, mais un nombre arbitraire n d’espaces, avec chacun leur fonction objectif, voisinage et opération de voisinage. Elle définit alors des outils supplémentaires Tij appelés traducteurs qui vont permettre de transformer n’importe quelle solution de Si en solution de Sj et ce pour tout i, j ≤ n, i 6= j. L’algorithme en lui-même est alors très simple, il est présenté dans l’algorithme 1. CHAPITRE 2. La recherche par espaces variables 17 Algorithme 1 Variable Space Search Entrée : – une série d’espaces de recherche Si (1 ≤ i ≤ n) avec leur fonction objectif fi et leur voisinage Ni respectifs, – les traducteurs Tij , Sortie : la solution du problème 1: i ← 1 2: s ← Générer une solution initiale ∈ S1 3: Tant que le critère d’arrêt n’est pas rencontré faire 4: s0 ← Faire une recherche locale dans Si à partir de s en utilisant la fonction objectif fi et le voisinage Ni 5: j ← (i mod n) + 1 6: s ← Tij (s0 ) 7: i←j 8: fin Tant que 9: Retourner s Il peut cependant être modifié dans le sens où il n’y a aucune obligation de commencer à l’espace S1 ni de passer de l’espace Si à l’espace Si+1 . Ainsi, l’algorithme pourrait, par exemple, commencer par l’espace S42 et choisir l’espace suivant en fonction de la qualité des solutions que cet espace a fourni par le passé. Il est à noter qu’une solution admissible pour un certain ensemble Si ne l’est pas forcément pour un espace Sj (i 6= j) ; autrement dit, il se peut que les espaces considérés soient totalement différents (et il en va donc de même pour leur fonction objectif et leurs voisinages). 2.2 Intuition de fonctionnement F IGURE 2.1 – Fonctionnement du VSS CHAPITRE 2. La recherche par espaces variables 18 Le problème principal auquel sont confrontées les métaheuristiques est de s’échapper d’un optimum local (plus d’informations à ce sujet sont apportées dans la section 2.5). Ceci la principale motivation de cette métaheuristique, en effet, un optimum local dans un espace ne l’est pas spécialement dans un autre. Ainsi changer d’espace permet de s’échapper de l’optimum local dans lequel la recherche locale est restée coincée. L’algorithme, bien que complexe, suit une structure assez simple détaillée dans la figure 2.1. Dans le cadre de la coloration de graphe, les auteurs de cette métaheuristique recommande 3 espaces de solutions qui sont définis dans la section suivante. L’algorithme de recherche locale dans l’espace S1 , TabuCol, permet de réduire significativement la valeur objectif de la solution courante mais peut parfois avoir du mal à explorer toutes les régions de l’espace de recherche, il se bloque assez facilement dans un optimum local. L’algorithme de recherche locale dans S3 , S3Search, tient son rôle de là, il permet de diversifier de manière plus ou moins forte la solution courante afin de “sauter” hors d’un optimum local et ce, sans trop détériorer la solution. Par la suite, l’algorithme de recherche locale dans S2 , PartialCol, permet de réduire très rapidement la valeur de la solution courante en recolorant les noeuds non colorés jusqu’à atteindre un optimum local. L’algorithme repasse ensuite à TabuCol qui va commencer sa recherche dans une région qui, avec espoir, il n’a pas encore visité. 2.3 3 ensembles pour la k-coloration Cette section attaque le sujet de ce projet en profondeur. En effet, il consiste à détailler les ensembles de solutions, leur fonction objectif et leurs voisinages choisis pour la métaheuristique précédemment introduite afin de résoudre le problème de coloration de graphe. Pour le problème de k-coloration de graphes, il convient d’utiliser trois ensembles. Afin de trouver ces trois ensembles, il faut se rappeler la définition d’une coloration légale. Pour qu’une coloration soit légale, il faut qu’elle respecte deux contraintes : tous les noeuds sont coloriés et il n’y a aucune arête conflictuelle. Les deux premiers ensembles seront construits à partir d’une relaxation de l’une de ces deux contraintes tandis que le dernier contiendra des solutions d’une toute autre nature. Premier espace Description de l’ensemble S1 : cet espace contient toutes les k-colorations de G, qu’elles soient légales ou pas. CHAPITRE 2. La recherche par espaces variables 19 Fonction objectif f1 (s) : elle est égale au nombre d’arêtes conflictuelles que contient s. Voisinage N1 (s) : il contient toutes les k-colorations de G obtenues en modifiant la couleur d’un et un seul sommet dans s. Algorithme de recherche locale : TabuCol[16], cet algorithme permet d’effectuer la recherche locale dans l’ensemble S1 en se basant sur une recherche tabou. (Plus d’informations sur cet algorithme dans la section 2.5) Second espacee Description de l’ensemble S2 : cet espace, proposé par Morgenstern [20], contient toutes les k-colorations légales partielles de G (donc les colorations dont tous les noeuds ne sont pas coloriés). Ces colorations sont représentées comme une partition de l’ensemble des noeuds en classes de couleur Vi avec la classe Vk+1 qui contient tous les noeuds non-coloriés. Fonction objectif f2 (s) : égale au nombre de noeuds appartenant à Vk+1 . Voisinage N2 (s) : toutes les colorations obtenues à partir de s en effectuant un iswap. Un i-swap consiste à déplacer un noeud v de la classe Vk+1 à la classe Vi en déplaçant tous les noeuds adjaçents de v de Vi dans Vk+1 . Algorithme de recherche locale : PartialCol[17], cet algorithme, également basé sur une recherche tabou a donné de bons résultats, il est donc utilisé ici. (Plus d’informations sur cet algorithme dans la section 2.5) Troisième espace Cet espace est différent des deux autres en ce sens qu’il ne contient pas de graphes coloriés mais des graphes orientés acycliques non coloriés. Il est nécessaire d’introduire un vocabulaire spécial pour cet espace. Ainsi, un graphe est dit acyclique s’il n’admet aucun chemin de longueur arbitraire reliant un noeud à lui→ − même. Ensuite, une orientation G d’un graphe non-orienté G est un graphe dont chaque arête (u, v) est remplacée par une arête orientée de u vers v (u → v) ou de v vers u (v → u) selon le choix fait. Un exemple d’orientation est donné dans la Figure 2.2. 1 5 2 3 1 4 5 2 3 4 F IGURE 2.2 – Un graphe et une orientation de celui-ci Gallai, Roy et Vitaver [21, 22, 23] ont prouvé séparément que la longueur CHAPITRE 2. La recherche par espaces variables 20 → − → − maximale λ( G ) d’un chemin dans G est au moins égale au nombre chromatique → − de G. Comme corollaire, le problème, qui consiste à orienter les arêtes de G → − de telle manière qu’il soit sans cycle et que λ( G ) soit minimal, est équivalent à trouver le nombre chromatique de G. (La conviction vient rapidement en essayant de construire l’un à partir de l’autre et inversement) 1 5 2 3 4 F IGURE 2.3 – Orientation et un plus long chemin Description de l’espace S3 : Gendron, Hertz et St-Louis [24] proposent de définir → − cet espace comme l’espace des orientations acycliques G de G. De manière semblable aux classes de couleurs Vi pour S1 et S2 , l’espace S3 contiendra des classes de longueur nommées Ui . La classe Ui est définie comme la classe contenant tous les sommets dont le plus long chemin se terminant en ce sommet contient exactement i sommets. Ces classes Ui peuvent être plus nombreuses que les classes Vi de couleurs, il est important de ne pas les confondre. Un exemple est donné dans la figure 2.4. → − → − Fonction objectif f3 ( G ) : égale à λ( G ). → − → − → − Voisinage N3 ( G ) : on pose G λ le graphe construit à partir de G où toutes les arêtes orientées qui n’appartiennent pas à un chemin de longueur maximale dans → − → − G ont été enlevées (voir exemple figure 2.5). Dès lors, un voisin de G peut être obtenu en choisissant un sommet x aléatoirement et en changeant l’orientation de → − → − toutes les arêtes orientées de G dont la tête dans G λ est x (la tête d’une arête orientée est le point d’arrivée de l’arête). Il est prouvé dans [24] qu’une telle opération de voisinage ne crée pas de cycle et augmente la longueur d’un plus long chemin d’au plus une unité (voir exemple figure 2.6). CHAPITRE 2. La recherche par espaces variables 1 5 2 3 Classe U1 U2 U3 U4 21 4 Éléments {1} {2, 5} {3} {4} F IGURE 2.4 – Classes Ui 1 5 2 3 4 1 2 5 3 4 F IGURE 2.5 – Orientation et Gλ 1 5 2 3 1 4 5 2 3 4 F IGURE 2.6 – G et son voisin en choisissant x = 3 → − (les arêtes en bleu sont celles de G λ ) Algorithme de recherche locale : de manière simplifiée, il s’agira d’un algorithme aléatoire en ce sens où le but principal sera de modifier “suffisamment” la solution, en aucun cas de l’améliorer. (Plus d’informations sur cet algorithme dans la section 2.5) 2.4 Traducteurs pour la k-coloration Les différents ensembles sont maintenant bien définis et posés. Il reste à présent à spécifier comment l’algorithme transformera une solution d’un ensemble en une solution d’un autre ensemble. Vu qu’il y a trois ensembles, il y a six traduc- CHAPITRE 2. La recherche par espaces variables 22 teurs. Ces derniers sont présentés ci-dessous, accompagnés d’un exemple pour chacun d’entre eux. Ces exemples ont été inspirés (ou tout simplement repris) de ceux fournis par Hertz, Plumettaz et Zufferey [3]. Sur ces exemples les arêtes conflictuelles sont coloriées en rouge et les couleurs sont considérées telles que rouge = 1, bleu = 2 et vert = 3. 1. T12 construit une coloration partielle sans conflit à partir d’une coloration totale non légale en choisissant de manière aléatoire un des deux sommets de chaque arête conflictuelle et en insérant ce sommet dans la classe Vk+1 (donc retirer la couleur de ce sommet) 1 2 3 4 1 2 3 4 5 6 7 8 T12 5 6 7 8 F IGURE 2.7 – Application de T12 2. T21 construit une k-coloration de G à partir d’une k-coloration partielle sans conflit de G en considérant les noeuds de Vk+1 un à un dans un ordre aléatoire et en leur donnant la couleur dans {1, ..., k} qui crée le moins d’arêtes conflictuelles. 1 3 7 2 8 1 9 3 7 2 8 9 T21 4 5 4 6 5 6 F IGURE 2.8 – Application de T21 3. T13 construit une orientation de G en dirigeant les arêtes (u, v) telles que u ∈ Vi et v ∈ Vj : – de u vers v si i < j ou que i = j et que l’indice du noeud (son "numéro" dans la structure de données) u est plus petit que celui de v, – de v vers u sinon. CHAPITRE 2. La recherche par espaces variables 1 1 T13 6 2 23 3 4 6 2 5 3 4 5 F IGURE 2.9 – Application de T13 4. T23 procède exactement de la même façon que T13 . 1 2 6 3 1 5 T23 7 2 6 3 5 7 4 4 F IGURE 2.10 – Application de T23 5. T31 construit une k-coloration en donnant la couleur i (1 ≤ i ≤ k) à chaque sommet de la classe Ui et colorie les sommets restants dans un ordre aléatoire en leur donnant une couleur dans {1, ..., k} qui crée le moins d’arêtes conflictuelles. 1 2 3 4 1 2 3 4 8 7 6 5 T31 8 7 Classe U1 U2 U3 U4 6 Éléments {1, 8} {2, 7} {3, 6} {4, 5} 5 ⇒ Classe V1 (rouge) V2 (bleu) V3 (vert) Éléments {1, 4, 5, 8} {2, 7} {3, 6} F IGURE 2.11 – Application de T31 CHAPITRE 2. La recherche par espaces variables 24 6. T32 ordonne dans un premier temps les classes Ui (1 ≤ i ≤ k) par ordre décroissant de taille. Ensuite, les k premières classes sont respectivement coloriées avec les couleurs {1, ..., k}. Finalement les classes excédentaires sont fusionnées en une seule pour former la classe Vk+1 de l’ensemble S2 (les sommets non-coloriés). 6 4 2 9 11 6 1 4 2 9 11 1 T32 10 3 7 8 Classe U1 U2 U3 U4 10 5 Éléments {4, 6, 9} {1, 2, 11} {3, 7, 10} {5, 8} 3 8 ⇒ Classe V1 (rouge) V2 (bleu) V3 (vert) V4 (non-colorié) 7 5 Éléments {4, 6, 9} {1, 2, 11} {3, 7, 10} {5, 8} F IGURE 2.12 – Application de T32 2.5 Algorithmes sous-jacents Chaque ensemble est défini et l’algorithme est capable de passer de l’un à l’autre avec les six traducteurs définis plus haut. Il ne reste donc plus qu’à définir plus en détails les algorithmes utilisés pour la recherche locale dans chacun de ces ensembles. Cette section concerne ces algorithmes, elle explique le fonctionnement global de chacun d’eux en commençant par l’algorithme de recherche dans S1 . Viennent ensuite les algorithmes de recherche dans S2 et S3 . Avant de s’attaquer à ces algorithmes, elle décrit le fonctionnement de la recherche tabou qui est utilisée dans les algorithmes TabuCol et PartialCol. Recherche Tabou Pour rappel, le but des métaheuristiques développées est de trouver l’optimal global d’une fonction objectif. Il est dès lors très important de trouver un moyen de s’échapper d’optima locaux afin de poursuivre la recherche. En effet, la technique consistant à prendre le "meilleur" voisin (au vu de sa valeur par la CHAPITRE 2. La recherche par espaces variables 25 fonction objectif) ne permet pas d’atteindre l’optimal global. Par exemple, sur la figure ci-dessous, une fois arrivé sur le pic indiqué par "local maximum", on ne bougera plus car tous ses voisins sont "moins bien" que lui ; or ce n’est pas le maximum global. F IGURE 2.13 – Graphe d’une fonction objectif arbitraire (image prise de [25]) Le principe général de la recherche tabou est simple, il consiste à autoriser les mouvements d’une solution à un de ses voisins qui est "moins bien" que cette solution à condition que le mouvement ne soit pas tabou. Un mouvement tabou est un mouvement qui n’est pas autorisé, typiquement un mouvement qui ferait revenir l’algorithme sur ses pas. La recherche tabou tient donc en mémoire une liste circulaire appelée liste tabou qui contiendra à toute itération i les mouvements interdits pour cette itération. Cette liste est de taille fixe, ainsi un mouvement est interdit pendant un certain nombre d’itérations. Lorsque cette liste est pleine et qu’un mouvement doit être ajouté à la liste, le plus ancien est enlevé. Typiquement lors de l’itération consistant à passer de s à s0 , le mouvement s0 → s est ajouté à la liste et le plus vieux (si la liste est pleine) est enlevé. Il faut également spécifier un critère d’arrêt à cette méthode. Usuellement, le critère d’arrêt est un nombre maximal d’itérations mais ce n’est pas toujours le cas. TabuCol, algorithme de recherche dans S1 Le but de cet algorithme (proposé par de Werra et Hertz [16]) est de minimiser la fonction f (s) qui représente le nombre d’arêtes conflictuelles, notre objectif étant que cette valeur soit nulle afin que la coloration soit légale. Le fait que f doit être proche de (voire égale à) 0 peut alors être utilisé dans le critère d’arrêt. La liste tabou, quant à elle, contient des couples (x, i) et signifie qu’un sommet x ne peut plus être colorié par la couleur i. CHAPITRE 2. La recherche par espaces variables 26 L’algorithme génère aléatoirement un certain nombre (paramètre à préciser) de voisins de la solution courante s (si ce voisin provient d’un mouvement de la liste tabou, il en génère un autre). Pour rappel, un voisin de s est obtenu en modifiant la couleur d’un et un seul sommet de s. Il choisit ensuite le meilleur de ces voisins, s∗ c’est-à-dire celui avec la valeur de f la plus basse. Il effectue alors le mouvement s → s∗ et ajoute le mouvement s∗ → s dans la liste tabou (en supprimant le plus ancien si la liste est pleine). En effet, pour rappel, l’algorithme accepte les mouvements vers des solutions “moins bonnes” pourvu que ces mouvements ne soient pas tabou. Le but de cette manoeuvre étant de se donner les moyens de sortir d’un minimum local. L’algorithme continue ensuite selon le même raisonnement avec s = s∗ et ce jusqu’à ce que f (s) = 0 ou qu’un nombre maximal d’itérations (fixé au début) soit atteint. Dans ce dernier cas, si f (s) > 0 quand le nombre d’itérations est atteint, alors l’algorithme n’a pas trouvé de coloration légale. D’autres améliorations sont apportées à cet algorithme mais il n’est pas important de les citer pour la compréhension de l’algorithme. Ces améliorations permettent de réduire un peu plus le temps de calcul, elles sont détaillées dans l’annexe B. L’algorithme 2 résume cet algorithme en pseudo-code. Algorithme 2 TabuCol Entrée : – un graphe G = (V, E), – un entier k représentant le nombre de couleurs, – tabsize la taille de la liste tabou, – neighbcount le nombre de voisins à générer, – itermax le nombre maximal d’itérations. Sortie : une k-coloration de G tentant de minimiser le nombre d’arêtes conflictuelles. 1: Génération d’une solution aléatoire initiale s 2: iter ← 0 3: T ← nouvelle liste de taille tabsize 4: Tant que f (s) > 0 et que iter < itermax faire 5: Génération de neighbcount voisins si de s (tels que le mouvement s → si 6∈ T ) ∗ 6: s ← choisir le meilleur des si (au sens de la valeur de la fonction objectif en s∗ ) 7: Ajouter s∗ → s à T (en enlevant la plus vieille valeur de T si T est pleine) 8: s ← s∗ 9: iter ← iter + 1 10: fin Tant que 11: Retourner s CHAPITRE 2. La recherche par espaces variables 27 PartialCol, algorithme de recherche dans S2 Cet algorithme, proposé par Bloechliger et Zufferey [17], a pour but de minimiser la fonction objectif qui représente la taille de l’ensemble Vk+1 des noeuds non-coloriés. A chaque itération, il génère tous les voisins v de la solution courante s et évalue la taille de Vk+1 (notée L) pour chaque v et il choisit d’effectuer le mouvement qui mène à la taille la plus petite si ce mouvement n’est pas tabou ou que cette valeur est plus petite que la valeur optimale L∗ (obtenue avec la solution s∗ ) rencontrée jusque là. Si plusieurs mouvements mènent à la même valeur, un choix aléatoire est effectué. Pour rappel les voisins d’une solution s sont toutes les colorations que l’on peut obtenir en appliquant un i-swap. Il s’agit de déplacer un noeud v de la classe Vk+1 à une classe Vi (1 ≤ i ≤ k) mais également déplacer tous les sommets adjacents à v de la classe Vi à la classe Vk+1 pour ne pas créer de conflit. Un tel déplacement sera noté v ,→ Vi . L’algorithme 3 relate le fonctionnement de cet algorithme de recherche en pseudo-code. CHAPITRE 2. La recherche par espaces variables 28 Algorithme 3 PartialCol Entrée : – un graphe G = (V, E), – un entier k représentant le nombre de couleurs, – tabsize la taille de la liste tabou, – itermax le nombre maximal d’itérations. Sortie : une k-coloration de G tentant minimiser le nombre de noeuds noncoloriés. 1: Génération d’une solution initiale s. 2: iter ← 0 3: L∗ ← +∞ 4: T ← nouvelle liste de taille tabsize 5: Tant que Vk+1 6= ∅ et que iter < itermax faire 6: /* (Génération de tous les mouvements possibles) */ 7: M ← {(v ,→ Vl ) | v ∈ Vk+1 , 1 ≤ l ≤ k} 8: Enlever de M les mouvements tabous qui ne mènent pas à une nouvelle meilleure solution. 9: Choisir le meilleur mouvement (u ,→ Vl ) ∈ M . 10: Appliquer le mouvement (u ,→ Vl ) à s. 11: /* (Tous les noeuds de la classe où on place u et qui lui sont adjacents sont déplacés dans la classe Vk+1 , on ajoute alors tous les mouvements visant à les renvoyer dans cette classe dans la liste tabou.) */ 12: Pour tout sommet w ∈ Vl adjacent à u faire 13: Ajouter le mouvement (w ,→ Vl ) à T . 14: fin Pour 15: iter ← iter + 1 16: Si |Vk+1 | < L∗ alors 17: s∗ ← s 18: L∗ ← |Vk+1 | 19: iter ← 0 20: fin Si 21: fin Tant que 22: Retourner s∗ Algorithme de recherche dans S3 Cet algorithme, appelé S3Search, a été proposé par Hertz, Plumettaz et Zufferey [3]. Assez simple, l’algorithme consiste à générer des voisins de la solution initiale jusqu’à ce qu’un certain nombre d’arêtes MA aient été inversées. Le but de cet algorithme n’est pas d’améliorer la solution mais surtout de la modifier un maximum, de la diversifier afin de s’extirper plus facilement des optimaux locaux dans les autres espaces de solution. CHAPITRE 2. La recherche par espaces variables 29 Algorithme 4 S3Search Entrée : – s3init , la solution initiale – un entier λ représentant la longueur d’un plus long chemin, – MA le nombre d’arêtes minimum à inverser. Sortie : un graphe orienté essayant de minimiser la longueur d’un plus long chemin 1: inversed ← 0 2: Tant que inversed < MA faire 3: s3init ← générer aléatoirement un voisin de s3init 4: mettre à jour la variable inversed 5: fin Tant que 2.6 Algorithme VSS pour la k-coloration Maintenant que toutes les étapes sont clairement posées et définies, il est temps d’énoncer l’algorithme principal qui est utilisé pour ce problème. Cela sert en quelque sorte de résumé pour la section et permet de bien fixer le fonctionnement de l’heuristique. Les auteurs proposent l’ordre S1 → S3 → S2 → S1 → ... sur base de quelques expérimentations faits par leur soin. Ils proposent également un pré-traitement avant d’appliquer le traducteur T13 ainsi qu’un post-traitement après l’application de l’algorithme de recherche locale dans S3 , S3Search. Cette section développe donc d’abord ces deux étapes supplémentaires et finit par énoncer l’algorithme au complet. Pré-traitement Soit sT C la solution donnée par l’algorithme TabuCol pour le graphe G. Soit G0 le graphe obtenu à partir de G en enlevant toutes les arêtes conflictuelles de la coloration spécifiée par sT C . La solution sresult , construite en appliquant la coloration spécifiée par sT C au graphe G0 , est donc une coloration partielle légale. Soit la solution s3init = T13 (sresult ), une dernière étape visant à modifier cette solution est à appliquer, l’algorithme S3Search sera ensuite appliqué à cette solution. Cette modification consiste à, pour chaque arête conflictuelle Aconf de sT C : – choisir aléatoirement une extrémité v de Aconf , – inverser l’orientation de toutes les arêtes orientées dont la tête est v ou dont la queue est v dans G0λ , le choix étant aléatoire et identique pour chaque arête considérée. s3init est alors passé à l’algorithme S3Search. L’exemple ci-dessous permet de bien comprendre le pré-traitement. CHAPITRE 2. La recherche par espaces variables 30 Exemple 2.6.1. Dans cet exemple, l’hypothèse faite est que l’algorithme sélectionne à chaque fois l’extrémité d’indice inférieur (lors du choix aléatoire) sauf dans le cas de 4 et 8 où il sélectionne 8. Il considére également toutes les queues (lors du choix aléatoire entre têtes et queues). Voici G, 1 2 3 4 5 6 7 8 F IGURE 2.14 – Graphe G initial ST C a été calculée comme suit : 1 2 3 4 5 6 7 8 F IGURE 2.15 – Solution sT C Dès lors, G0 est construit en enlevant de G les arêtes conflictuelles présentes dans sT C : 1 2 3 4 5 6 7 8 F IGURE 2.16 – Graphe G0 sresult , la solution construite en appliquant la coloration définie par sT C à G0 est donc comme suit : 1 2 3 4 5 6 7 F IGURE 2.17 – La solution sresult 8 CHAPITRE 2. La recherche par espaces variables 31 → − Après application de T13 , on obtient s3init ou autrement dit G 0 : 1 2 3 4 5 6 7 8 F IGURE 2.18 – La solution s3init En supprimant toutes les arêtes n’étant pas sur un plus long chemin, dans le → − but de construire G 0λ , on obtient exactement le même graphe, λ = 2. Ensuite, pour chaque arête conflictuelle le pré-traitement sélectionne une extrêmité v au hasard et inverse l’orientation dans G0 de toutes les arêtes orientées u → v dans G0λ . On obtient alors le graphe suivant : 1 2 3 4 5 6 7 8 F IGURE 2.19 – La solution s3init modifiée On obtient donc finalement s3init telle qu’elle sera passée à l’algorithme S3Search. Post-traitement L’application de S3Search sur s3init donne la solution sOR . Pour rappel, cette solution concerne un sous-graphe du graphe initial, il faut donc transformer ce sous-graphe en graphe compatible avec le graphe initial afin de ne pas biaiser l’algorithme général (VSS). Le post-traitement permet justement d’appliquer cette transformation. Il consiste à réinjecter séquentiellement les arêtes enlevées lors du pré-traitement en leur donnant l’orientation qui minimise f3 , c’est-à-dire la longueur du plus long chemin en terme de noeuds (aucun ordre n’est spécifié pour traiter les arêtes). Exemple 2.6.2. Même exemple que précédemment, en supposant que S3Search n’a pas modifié la solution initiale. Dans cet exemple, les arêtes sont traitées dans l’ordre (1, 5), (2, 6), (3, 7), (4, 8) ; dans ce cas-ci l’ordre n’est pas important mais il pourrait l’être. En effet, il suffit par exemple, qu’une arête ajoutée à l’étape d’avant ajoute un nouveau chemin de longueur égale à λ et que ce nouveau chemin puisse être étendu par l’arête que l’algorithme doit considérer à présent. CHAPITRE 2. La recherche par espaces variables 32 Ajout en premier lieu de 5 → 1 car elle n’augmente pas λ alors que 1 → 5 augmente λ de 2. 1 2 3 4 5 6 7 8 F IGURE 2.20 – sOR après l’ajout de 5 → 1 Ajout ensuite de 2 → 6, 1 2 3 4 5 6 7 8 F IGURE 2.21 – sOR après l’ajout de 2 → 6 puis de 7 → 3, 1 2 3 4 5 6 7 8 F IGURE 2.22 – sOR après l’ajout de 7 → 3 et finalement de 4 → 8. 1 2 3 4 5 6 7 8 F IGURE 2.23 – sOR après l’ajout de 4 → 8 CHAPITRE 2. La recherche par espaces variables 33 Formulation de l’algorithme A présent, voici une formulation en pseudo-code de l’algorithme au complet, permettant de clore cette section et ce chapitre en résumant briévement chaque étape de l’algorithme et son fonctionnement global. Algorithme 5 VSSColoration Entrée : – un graphe G = (V, E), – un entier k représentant le nombre de couleurs, – TM AX le temps maximal d’exécution, – MA un paramètre pour l’algorithme de recherche dans S3 , – itermaxtabucol le nombre maximal d’itérations consécutives sans amélioration de TabuCol, – itermaxpartcol le nombre maximal d’itérations consécutives sans amélioration de PartialCol. Sortie : une k-coloration légale de G (dans la plupart des cas) 1: Génération d’une solution aléatoire initiale s1init ∈ S1 2: Tant que l’on n’a pas trouvé de k-coloration légale et que TM AX n’est pas atteint faire 3: sT C ← TabuCol(s1init ,itermaxtabucol ) 4: s3init ← Pré-traitement avec sT C 5: sOR ← S3Search(s3init , MA ) 6: Post-traitement de sOR 7: s2init ← T32 (sOR ) 8: sP C ← PartialCol(s2init ,itermaxpartcol ) 9: s1init ← T21 (sP C ) 10: fin Tant que 11: Retourner s Pour trouver le nombre chromatique du graphe, il convient d’appeler cet algorithme en commençant avec une borne supérieure de χ(G) et de continuer à l’appeler tant qu’une k-coloration légale est trouvée. Pour rappel, le choix de l’ordre des espaces a été défini par les auteurs de l’article, après expérimentations ils se sont rendu compte que cet ordre était un bon choix. 34 Chapitre 3 Une application du VSS Toute la théorie a été posée, il ne reste plus qu’à vérifier tout ce qui a été expliqué en appliquant le tout pour en faire un programme. Le langage choisi est le JAVA afin de pouvoir bénéficier de l’orienté objet, pour faciliter la prise en charge et la compréhension du projet (et du code) ainsi que de la portabilité de ce langage. Le but ici n’est pas de concurrencer en terme de temps de calcul avec les auteurs mais bien de reproduire des résultats similaires et vérifier la qualité des solutions. Ce chapitre s’articule en quatre points principaux représentés par les différentes sections : la modélisation, l’implémentation, les résultats obtenus et les difficultés éprouvées. 3.1 Modélisation Il s’agit ici de présenter l’organisation des classes JAVA utilisées afin de donner une vue globale du fonctionnement du programme. 3.1.1 Structures La première partie de l’implémentation, qui conditionne toute la suite du projet, concerne les structures de données employées. En effet, les algorithmes développés ci-haut manipulent et modifient des graphes et ces derniers peuvent être implémentés de plusieurs manières. Le choix fait ici est d’abstraire le tout par une interface Graph qui reprend toutes les méthodes que l’on pourrait appeler sur un graphe telles que connaître les successeurs d’un sommet ou encore le nombre de sommets du graphe. Il est cependant nécessaire de pouvoir faire la différence entre un graphe orienté et un graphe non-orienté, c’est pour cela que les classes abstraites DirectedGraph et UndirectedGraph qui implémentent l’interface Graph sont employées. Pour présenter simplement l’architecture de ces classes dédiées aux structures de données, la Figure 3.1 contient un diagramme de classe UML schématisant les relations entre ces classes. Il est à savoir, pour CHAPITRE 3. Une application du VSS 35 comprendre le diagramme, qu’un sommet est représenté par un nombre entier et qu’il en va de même pour une couleur. Visual Paradigm for UML Standard Edition(University of Mons) F IGURE 3.1 – Diagramme de classes des structures de données Cette manière de modéliser permet d’abstraire totalement la manière dont les sommets sont stockés par le graphe, le fait de comparer lesquelles des listes ou des matrices d’adjacence sont les plus appropriées au problème pourrait être un résultat intéressant. Il est également envisageable d’ajouter un autre type de structure, en créant une classe de graphes l’utilisant et en la plaçant simplement comme sous-classe des classes UndirectedGraph ou DirectedGraph selon le type du graphe. De manière similaire, il est possible d’ajouter un type de graphes différent (par exemple des graphes pondérés) en plaçant la classe créée comme sous-classe de AbstractGraph. Mis à part les graphes, il y a également des structures de données utilisées pour les listes tabous et les élements que ces listes contiennent. De manière claire et simplifiée il s’agit d’une liste capable de contenir à la fois des Configuration ainsi que des “groupes” de Configuration symbolisés par la classe Configurations. CHAPITRE 3. Une application du VSS 36 Visual Paradigm for UML Standard Edition(University of Mons) F IGURE 3.2 – Diagramme de classes des structures tabous 3.1.2 Heuristique Il convient de modéliser les trois espaces de solution et une solution de la meilleure manière possible. Il y a essentiellement deux façons : – soit une solution est une classe générale indépendamment de l’espace auquel elle appartient et chaque espace de solutions est représenté par une classe qui permet, entre autres de tester qu’une solution appartient bien à l’espace, – soit une solution d’un espace est représenté par une classe, chaque espace de solutions aura donc son propre type de solutions représenté par une classe (une classe pour l’espace S1 , une autre pour S2 et idem pour S3 ). Vu que l’algorithme est amené à passer d’une solution d’un espace à une solution d’un autre et que ces solutions ne sont pas toujours de la même forme (par exemple une solution dans S1 est une coloration d’un graphe non-orienté, dans S3 c’est une orientation de ce même graphe), le choix se porte sur la seconde solution. L’interface Solution régit les solutions. En effet, une solution doit absolument permettre de récupérer sa valeur pour la fonction objectif. Il y a donc les classes S1Solution, S2Solution et S3Solution héritant de la classe Solution. La figure 3.3 contient un diagramme de classe UML schématisant les relations entre ces classes. Il convient ensuite d’implémenter les traducteurs et les algorithmes de recherche (locale et le VSS). De manière assez simple, une classe abstraite regroupe les différents traducteurs sous forme de méthodes statiques tandis que chaque algorithme de recherche est représenté par sa propre classe (avec un héritage tout de même au travers une classe mère LocalSearch ainsi qu’une classe TabuAlgorithm pour généraliser les deux algorithmes tabous). Plus de détails sont fournis dans le diagramme UML correspondant dans la figure 3.4. CHAPITRE 3. Une application du VSS Visual Paradigm for UML Standard Edition(University of Mons) F IGURE 3.3 – Diagramme de classes des solutions Visual Paradigm for UML Standard Edition(University of Mons) F IGURE 3.4 – Diagramme de classes des algorithmes 37 CHAPITRE 3. Une application du VSS 3.2 38 Implémentation Cette section décrit quelques aspects techniques ayant fait l’objet d’un choix lors de l’accomplissement du projet. Elle se divise en plusieurs sous-sections concernant chacune un aspect différent du projet. 3.2.1 Structures de données & représentation Représentation Au niveau de la représentation, comme il est précisé plus haut, le premier choix (bien que simple et évident) effectué est de représenter un sommet par un nombre entier ; aucune information spéciale, à part la couleur, n’étant nécessaire. Cette couleur est préférablement stockée dans un tableau d’entiers dédié à cet effet. Structures de données pour les graphes Concernant les structures de données utilisées, il est à remarquer que, pour gagner de l’espace mémoire, les graphes non-orientés (que ce soit avec listes ou matrices) ne tiennent trace que des arêtes (i, j) avec i < j (vu que cela implique l’existence de (j, i) et que les graphes non-orientés simples ne contiennent pas de boucles). Dès lors, la matrice d’adjacence M , telle que stockée par le programme, est de la forme (pour n sommets) : M1,2 M1,3 ... M1,n−1 M1,n M2,3 ... M2,n−1 M2,n .. .. .. M = . . . Mn−2,n−1 Mn−2,n Mn−1,n Ceci peut se traduire globalement en JAVA de deux manières différentes : un tableau 2-dimensionnel de taille n − 1 (pas de "ligne" pour le n-ème noeud) comprenant des lignes de plus en plus petites (la seconde taille du tableau étant donc variable) ou un tableau unidimensionnel contenant toutes les lignes de la matrice l’une après l’autre. (En ce qui concerne les listes, intuitivement, la case i du tableau contenant les listes d’adjacence abrite une liste qui ne contient que des éléments j > i.) Que ce soit un tableau à 2 dimensions ou à une 1 seule dimension (appelé vecteur), il a fallu prendre en compte le fait que ces structures ne sont pas complètes dans les méthodes parcourant les sommets pour ne pas dépasser les tailles des tableaux/vecteurs stockés. Ainsi, par exemple, la méthode vérifiant si i est successeur de j va en fait rechercher si l’arête (min (i, j), max (i, j)) existe. CHAPITRE 3. Une application du VSS 39 Ensuite, selon le stockage (les 2 manières ont été implémentées), il y a d’autres modifications d’indices à appliquer : stockage par tableaux {M , M , ..., M , M }, 1,2 1,3 1,n−1 1,n {M2,3 , ..., M2,n−1 , M2,n }, int[][] M = ; .. . {M n−1,n } F IGURE 3.5 – Stokage par tableau 2-dimensionnel Lors de la recherche de l’existence de l’arête (min (i, j), max (i, j)), il faut savoir que la case [min (i, j) − 1][max (i, j) − 1] de la matrice peut ne pas exister (pour rappel, un tableau en JAVA est numéroté à partir de 0). Prenons par exemple la recherche de l’existence de l’arête (n, n − 2), il faut donc accéder à Mn−2,n , mais la matrice en JAVA ne contient que deux éléments en M [n − 3], il faut donc, en fait, accéder à M [n − 3][1]. Plus généralement, pour connaître l’existence d’une arête (i, j) il faut consulter la case M [min (i, j) − 1, (max (i, j) − min (i, j)) − 1]. stockage par vecteur P Le vecteur a besoin de (n − 1) + (n − 2) + (n − 3) + ... + 1 + 0 = n−1 i=0 i, n(n − 1) composantes. Il ressemble donc à ceci : soit 2 (M1,2 , M1,3 , . . . M1,n , M2,3 , . . . M2,n , . . . Mn−1,n ) Pour une arête (i, j) donnée, comme dit précédemment, il faut en fait chercher l’arête (i∗ , j ∗ ) où i∗ = min (i, j) et j ∗ = max (i, j). Ensuite, pour trouver cette arête, il faut chercher dans le vecteur l’endroit correspondant à la bonne ligne de la matrice et ensuite atteindre la bonne colonne. Pour ce faire, l’indice à atteindre est donné par : ∗ ∗ i (i + 1) ∗ i n− + j ∗ − (i∗ + 1) 2 (la première partie (entre [ ]) spécifie l’indice du premier élément de la ligne i∗ de la matrice tandis que la seconde partie spécifie l’indice de la colonne j ∗ pour la ligne i∗ ) Remarque 3.2.1. Les 2 stockages sont identiques en terme de complexité, tant au niveau espace qu’au niveau temps. Le choix entre ces 2 stockages est donc totalement arbitraire et est laissé aléatoire dans l’implémentation du projet. Le stockage en vecteur est peut-être un peu préféré pour des raisons d’habitude de programmation en C. CHAPITRE 3. Une application du VSS 40 Structures de données pour les listes Les listes étant utilisées un peu partout dans ce projet (liste de successeurs, liste de prédécesseurs, liste de sommets appartenant à une même classe de couleur, ...), il est important que ces listes fournissent des opérations rapides pour la recherche, l’ajout et la suppression d’éléments. Les listes sont donc triées par ordre croissant permettant ainsi une recherche plus rapide (et donc une suppression plus rapide) et sont doublement chaînées. Elles conservent en outre trois références de manière permanente : – la queue de la liste, – la tête de la liste, – le centre de la liste. Cette dernière, assez inhabituelle peut permettre dans le meilleur des cas de diviser la complexité par 2. En effet, lors de la recherche d’un élément, l’algorithme va l’utiliser pour commencer sa recherche au milieu de la liste dans certains cas plutôt qu’au début ou à la fin. Voici comment l’algorithme fonctionne : Algorithme 6 SearchSortedList Entrée : – L une liste triée, – un entier k ≥ 0 à rechercher dans L. Sortie : vrai si k ∈ L, faux sinon. 1: begin ← arg mini∈{center,f irst,last} |i.data − k| 2: current ← begin 3: Si current.data = k alors 4: Retourner Vrai 5: Sinon Si current.data < k alors 6: Tant que current.data < k faire 7: current ← current.next 8: fin Tant que 9: Sinon 10: Tant que current.data > k faire 11: current ← current.prev 12: fin Tant que 13: fin Si 14: Retourner current.data = k La référence vers le centre n’est pas toujours utile, en effet, dans certains cas l’élément se trouve à côté d’une des références stockées mais l’algorithme débute tout de même sa recherche à partir d’une autre référence (c’est en fait le pire des cas). Pour s’en convaincre, il suffit de considérer la liste suivante dans laquelle l’algorithme recherche 42 : 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 42 → 500 CHAPITRE 3. Une application du VSS 41 Dans cet exemple, f irst = 1, last = 500 et center = 7. Lors du calcul de l’arg min, les trois valeurs calulées sont : – pour first : |1 − 42| = 41 – pour center : |7 − 42| = 35 – pour last : |500 − 42| = 458 La recherche va donc débuter à partir de center alors qu’elle devrait commencer à partir de last. De manière assez rapide et intuitive, l’intérêt de la référence center existe lorsque les données “ne sont pas trop éloignées les unes des autres”. Lorsque les données ne respectent pas cette condition (comme l’exemple précédent), il s’agit du pire des cas de l’algorithme : la recherche, l’ajout et la suppression se font en O( n2 ) (avec n la taille de la liste). En effet, l’algorithme ne parcourera que la partie de la liste située entre center et last. Dans le cas où la condition est respectée, et donc le meilleur des cas, la complexité est de O( n4 ) (vu que la donnée est proche du départ choisi, elle ne peut être plus loin de ce départ que la moitié du chemin entre lui et l’autre borne délimitant l’intervalle). Au final, l’algorithme est donc en O( n2 ). Ce n’est cependant pas la meilleure approche, une table de hashage ou un tableau unidimensionnel de la taille du nombre de noeuds contenant des booléens (vrai si l’élément indicé est contenu, faux sinon) mais une telle représentation pour les listes d’adjacences revient au final à utiliser une matrice d’adjacence. Structures de données pour la liste tabou Cette structure est un peu particulière car il s’agit d’une liste circulaire. Pour implémenter cela, il n’y a nul besoin que la liste soit réellement circulaire. Il suffit en fait d’une liste chainée (doublement dans le cas de ce projet, via la classe LinkedList de JAVA) dans laquelle on ajoute tout élément à la fin de la liste et, lorsque la capacité fixée est atteinte, la tête de la liste est supprimée. Cette structure, dans le cadre de ce projet, possède une autre particularité. En effet, elle contient des “TabuElement” qui sont simplement des éléments implémentant une interface permettant de définir n’importe quel type d’élément tabou pourvu que cet élément soit doté d’un nombre d’itérations (tenure) pendant lesquelles il est considéré tabou. Dans le cadre de ce projet, deux éléments forts semblables ont été implémentés ; il s’agit de “Configuration” qui est simplement une configuration à éviter (typiquement un couple sommet-couleur) et “Configurations” qui représente un ensemble de configurations de même tenure à éviter. La particularité des “Configurations” est qu’il est possible d’ajouter plus d’éléments que la capacité de la liste vu qu’un ensemble de configuration ne prend qu’une seule place dans la liste tabou. La présence de ces deux éléments différents se justifient par les deux algorithmes utilisant ces listes tabous, TabuCol n’ajoute qu’un seul élément à la fois tandis que PartialCol ajoute en général un groupe d’éléments à la liste. CHAPITRE 3. Une application du VSS 3.2.2 42 Algorithmique Cette section est destinée à expliquer l’implémentation de certains algorithmes évolués permettant de calculer certaines valeurs (ou autres) utilisées par l’heuristique. Calcul de longueur de chemins dans S3 Le premier algorithme envisagé était une fermeture transitive adaptée, algorithme donc en O(n3 ). Après réflexion, un algorithme récursif moins gourmand a été trouvé (O(m) avec m le nombre d’arêtes, et donc O(n2 ) dans le pire des cas). Il a été implémenté dans la classe S3Solution via les méthodes computeLongestPaths et longestPathTo. Le comportement de ces dernières est donné par les algorithmes 7 et 8. Le fonctionnement de cet algorithme est assez simple en soi, il se base sur la récursion suivante : 1 si i n’a aucun prédecesseur, longestP aths[i] = (maxv prédecesseur de i longestP aths[v]) + 1 L’algorithme commence par n’importe quel sommet (le sommet 0 ici) et remonte les arêtes orientées jusqu’à un noeud sans prédécesseurs (il y en a forcément au moins un pour chaque chemin exploré sinon il y a un cycle dans le graphe) en explorant toutes les possibilités afin de remplir le tableau longestP aths. Une amélioration serait possible en démarrant l’algorithme du sommet de plus grand degré incident (et donc le plus grand nombre de prédécesseurs) ou même d’un sommet n’ayant pas de successeurs. L’algorithme marque les noeuds par lesquels il est déjà passé auparavant, ceci permet de ne pas calculer plusieurs fois les mêmes valeurs mais également de détecter un éventuel cycle dans le graphe. Lorsqu’il n’y a pas de cycle, un noeud déjà marqué doit avoir une longueur de plus long chemin supérieure ou égale à 1 (vu qu’il y a au moins le chemin ne contenant que le noeud lui-même), si l’algorithme tombe sur un noeud déjà marqué (et donc déjà visité) dont la longueur du plus long chemin est 0 cela signifie qu’il reconsidère un noeud pour lequel la récursion n’a pas été terminée et donc qu’il y a un cycle dans le graphe. Cet algorithme est lancé pour chaque noeud non-marqué du graphe (et donc lors d’une éxécution, si aucun des chemins générés ne passent par un des noeuds du graphe, l’algorithme relance une exploration à partir de ce noeud). Il parcourt donc toutes les arêtes orientées du graphe une seule fois, ce qui lui vaut sa complexité en O(n2 ) où n est le nombre de noeuds du graphe (pour rappel le nombre d’arêtes d’un graphe m = O(n2 )). CHAPITRE 3. Une application du VSS Algorithme 7 LongestPathTo Entrée : – node, le noeud vers lequel le chemin doit aller, – mark, le tableau de marquage des noeuds déjà visités, – longestP aths, le tableau des plus longs chemins connus jusque là. Sortie : la longueur du plus long chemin allant jusque node 1: Si mark[node] alors 2: Si longestP aths[node] = 0 alors 3: longestP aths[node] = +∞ 4: fin Si 5: Retourner longestP aths[node] 6: Sinon 7: mark[node] ← Vrai 8: max ← 1 // le chemin ne contenant que le noeud lui-même 9: Pour tous les prédécesseurs p de node faire 10: longestP aths[p] ← LongestPathTo(p, mark, longestP ath) 11: Si longestP aths[p] = +∞ alors 12: Retourner +∞ 13: fin Si 14: Si max < longestP aths[elem] + 1 alors 15: max ← longestP aths[elem] + 1 16: fin Si 17: fin Pour 18: longestP aths[node] ← max 19: Retourner max 20: fin Si 43 CHAPITRE 3. Une application du VSS 44 Algorithme 8 ComputeLongestPaths Entrée : G, un graphe orienté de n sommets. 1: lambda ← −1 2: mark ← nouveau tableau de n booléens initialisés à Faux 3: longestP aths ← nouveau tableau de n entiers initialisés à 0 4: Pour i allant de 0 à n − 1 faire 5: Si Non mark[i] alors 6: LongestPathTo(i, mark, longestP aths) 7: fin Si 8: Si longestP aths[i] > lambda alors 9: lambda ← longestP aths[i] 10: fin Si 11: Si lambda = +∞ alors 12: Le graphe est cyclique ! 13: fin Si 14: fin Pour Les algorithmes tabous Ces deux algorithmes (PartialCol et TabuCol) sont caractérisés par le fait qu’il y a beaucoup d’itérations et que chacune d’entre elles consiste en la génération de voisins. Il est donc primordial que ces algorithmes soient optimaux et donc que cette génération le soit également. De manière claire, en lisant les algorithmes, il est facile de constater que, pour un certain noeud v, le nombre de voisins de v de la même couleur que lui est une valeur souvent calculée. Pour ne pas parcourir les voisins de chaque noeud dont cette valeur est désirée ou tous les noeuds d’une classe de couleur, les algorithmes utilisent la programmation dynamique comme suggéré dans [17]. La matrice γ(k × n) (avec k le nombre de couleurs utilisées actuellement et n le nombre de noeuds) est donc définie comme suit : γkn = nombre de voisins de n de couleur k l’accès à ces valeurs n’est donc plus en O(nombre de voisins de n) mais en O(1). Pour que cela reste en O(1), il convient de conserver cette matrice à jour lors des différentes opérations de voisinages. Lors des opérations de voisinages, deux modifications impliquent la matrice γ : – colorer un noeud non coloré j par la couleur k, et d’incrémenter les cases γkw , ∀w adjacent à j, – enlever la couleur d’un noeud j qui était coloré par k, il convient donc de décrémenter les cases γkw , ∀w adjacent à j. CHAPITRE 3. Une application du VSS 45 De cette façon, ces algorithmes peuvent avoir une complexité beaucoup plus attrayante que précédemment bien qu’une opération de mise à jour soit nécessaire lorsqu’un voisin est choisi et que l’on modifie la solution courante vers ce voisin. Cela permet tout de même de générer (plutôt “simuler la génération”) des voisins de manière assez rapide. 3.3 Résultats Le but de ce projet était de lire, de comprendre et de reproduire l’expérience menée par les auteurs de l’article sur la recherche par espaces variables appliquée à la coloration de graphes. Jusque maintenant, le présent rapport s’est efforcé de montrer que la lecture a bel et bien été faite et que la compréhension est totale. Il a également montré comment l’expérience allait être reproduite, c’est-à-dire comment l’heuristique allait être implémentée en JAVA. A présent, la présente section confronte les résultats obtenus par les auteurs à ceux obtenus par ce projet. Il est évident que la différence de temps d’exécution et de résultats pourra être conséquente car elle dépend du langage utilisé, de la machine utilisée, du nombre de personnes ayant collaborés pour le projet et de beaucoup d’autres facteurs. Les résultats qu’ils ont reportés pour leur application en C++ ont été obtenus sur un Pentium 4 de 2Ghz avec 512 MB de RAM. Pour l’application développée dans le cadre de ce projet en JAVA, les résultats ont été obtenus sur un Intel core i3 2, 27Ghz avec 4 GB de RAM (pour information, l’application n’est pas multithreadée). La première constatation est que l’algorithme développé semble fonctionner en ce sens que sur n’importe quelle instance l’algorithme se rapproche de l’optimum (lentement parfois, mais tout de même) ce qui signifie que l’algorithme fonctionne réellement. Il s’agissait de l’un des aboutissants des projets qui est pleinement rempli : reproduire l’expérience. Cependant, force est de constater que les performances divergent fortement. Il faut cependant bien se rendre compte que l’application qu’ils ont développée doit avoir subi énormément d’améliorations jusqu’au plus petit détail afin d’être la plus rapide possible alors que dans le cas de l’application développée dans ce projet, la programmation objet était mise en avant, en utilisant au maximum les mécanismes de polymorphisme. Les tests sont effectués sur des instances choisies par les auteurs dans les graphes de DIMACS challenge [26] avec les mêmes paramètres pour l’algorithme que ceux qu’ils ont utilisés. Ces graphes sont réputés pour avoir un nombre chromatique difficile (voir les plus difficiles) à calculer. Pour l’expérience, vu la lenteur relative de l’application en JAVA, les graphes sélectionnés seront des graphes dont le nombre de noeuds n ne dépasse pas 500. Voici une première table reprenant les résultats obtenus sans considérer les temps d’exécution. Elle se présente comme suit : CHAPITRE 3. Une application du VSS 46 – la première colonne reprend le nom du fichier considéré, – la seconde colonne reprend la densité du graphe, cette valeur représente la quantité d’arêtes existantes par rapport aux noeuds, il s’agit en fait du quotient du nombre d’arêtes par le nombre de noeuds au carré |E| density = |V |2 , – la troisième colonne contient la meilleure borne connue suivi du nombre chromatique (“ ?” s’il n’est pas connu), – la quatrième colonne contient la meilleure borne trouvée à chaque run (c’est-à-dire qu’à chaque lancement avec cette valeur de k, l’algorithme trouve une k-coloration) par les auteurs, – la cinquième colonne contient la meilleure borne trouvée à chaque run pour l’application JAVA développée dans ce projet, il est à savoir que pour les résultats de la quatrième ainsi que de la cinquième colonne, le temps limite accordé à l’application est d’1 heure ; – finalement, la dernière colonne montre l’écart entre les valeurs obtenues. Graphe G DSJC500.1 DSJC500.5 DSJC500.9 DSJR500.1c DSJR500.5 flat300_28_0 le450_15c le450_15d le450_25c le450_25d ∗ ∗ density(G) k ∗ , χ kauthors kJAV A 0.1 12, ? 12 13 0.5 48, ? 49 53 0.9 126, ? 127 140 0.97 85, ? 86 99 0.47 126, ? 127 138 0.48 28, 28 31 33 0.165 15, 15 15 17 0.165 15, 15 15 17 0.17 25, 25 26 28 0.17 25, 25 26 28 ∗ ∗ |kJAV A − kauthors | 1 4 13 13 11 2 2 2 2 2 La première constatation est que, dans aucun cas, l’application développée, ici, n’est capable d’obtenir les mêmes résultats que l’application initiale (tout du moins, en moins d’une heure). La seconde est que l’efficacité de l’algorithme est directement liée à la densité du graphe. La raison à cela est que l’algorithme considère à plusieurs reprises les arêtes du graphe et donc, plus le graphe en possède moins l’algorithme est rapide et efficace. Les graphiques en figure 3.6 portent ces données par famille de graphe afin de vérifier que les deux applications suivent les mêmes courbes d’évolution. Suivant ces graphes, les auteurs sont très proches de l’optimum trouvé jusqu’à présent tandis que l’application développée dans le cadre de ce projet bien que un peu plus loin en général suit le même genre de courbe. CHAPITRE 3. Une application du VSS 47 F IGURE 3.6 – Graphiques des résultats Voici à présent une seconde table comparant les temps d’exécution, le nombre de cycles et d’itérations nécessaires à l’obtention des solutions. Ces temps ne sont pas directement comparables car ils ne concernent pas la même valeur de k mais peuvent être utiles pour dégager un facteur permettant de qualifier la différence de vitesse d’exécution entre les deux applications et vérifier qu’il est avéré partout. Il ne faut pas oublier que l’algorithme met en oeuvre quelques décisions aléatoires, les données reprises ci-dessous sont donc des moyennes obtenues sur 10 runs. Cette table se présente comme suit : – la première colonne reprend le nom du fichier considéré, – les colonnes 2 à 4 sont les données obtenues par les auteurs, – les colonnes 5 à 7 sont les données obtenues par l’application JAVA, – finalement, la dernière colonne montre le facteur de différence de temps. (tous les temps sont exprimés en secondes) Graphe G DSJC500.1 DSJC500.5 DSJC500.9 DSJR500.1c DSJR500.5 flat300_28_0 le450_15c le450_15d le450_25c le450_25d taut cycleaut iteraut 97 17 19.799.000 162 82 10.524.000 169 62 7.754.000 291 165 20.020.000 183 60 9.066.000 39 32 4.173.000 6 4 497.000 4 39 4.761.000 1 1 183.000 1 1 117.000 tJ cycleJ iterJ 146 1 107.625 279 1 134.741 230 1 102.769 195 1 100.824 241 1 152.427 78 1 125.433 133 1 103.332 126 1 101.753 127 1 106.746 118 1 100.630 Facteur 1, 5 1, 7 1, 3 0.67 1, 3 2 22 31 127 118 CHAPITRE 3. Une application du VSS 48 Comme le révèle la table, aucun facteur ne semble se dégager. Ce qui est à remarquer, c’est que l’application en JAVA n’effectue jamais plus d’un cycle pour trouver la k-coloration. Ceci est explicable par le fait qu’il faut beaucoup de cycles pour ne fut-ce que décrémenter la valeur du k par 1 et qu’un cycle est énormément couteux dans le cas de cette application (ce temps dépend de la densité du graphe et du nombre de noeuds). De plus, de manière non-explicable lors du premier cycle, les itérations de l’algorithme TabuCol sont plus rapides que par la suite. En poussant l’analyse, il semblerait que ce soit l’action du garbage collector de JAVA qui est plus actif par la suite et qui entrave la vitesse du programme. Lors des différentes exécutions, on remarque également que l’algorithme arrive très souvent à une valeur de 1, 2 ou 3 pour la meilleure solution obtenue jusque maintenant mais ne parvient pas à descendre plus bas de manière assez rapide. On pourrait alors imaginer une manière de sortir de cet optimum local en appliquant un très grand changement à la solution, encore plus grand que l’algorithme S3Search, ou une manière simple et rapide d’arriver à la solution optimale afin d’améliorer l’algorithme tout entier. Les graphiques en figure 3.7 portent ces données par famille de graphe afin de vérifier que les deux applications suivent les mêmes courbes d’évolution au niveau du temps d’exécution. Il faut cependant, comme pour les valeurs de la table bien tenir compte du fait que ces temps sont obtenus pour des valeurs de k différentes. F IGURE 3.7 – Graphiques des temps d’exécution Au niveau des temps d’exécution, les applications sont assez fortement espacées mais elles suivent à nouveau approximativement les mêmes courbes d’évolution. Les graphes en figure 3.8 permettent de faire la même analyse pour le nombre CHAPITRE 3. Une application du VSS 49 d’itérations bien qu’un pique pour le450_15d pour les auteurs perturbent leur courbe d’évolution. F IGURE 3.8 – Graphiques des nombres d’itérations Au final, de ces résultats, nous pouvons conclure que cette heuristique fonctionne et, si elle est très bien programmée et optimisée, peut être assez rapide. Il faut cependant déplorer que, dans certains cas, même les auteurs ne sont pas parvenus à obtenir le nombre chromatique ni la borne la plus basse connue jusque maintenant mais cela ne remet pas en cause le fonctionnement global de l’heuristique qui semble être au point. 3.4 Difficultés Cette section regroupe les différentes difficultés éprouvées lors de la compréhension de la matière ainsi que de l’implémentation de l’heuristique. Elle s’articule, dès lors, autour de ces deux points en explicitant en détails chacune de ces difficultés. 3.4.1 Compréhension Les premières difficultés éprouvées peuvent être regroupées en un point, il s’agit de l’espace de solutions 3. En effet, bien que simple une fois bien compris, celui-ci peut être difficile à intégrer de prime abord. Cette difficulté vient probablement du fait que cet ensemble nécessite d’introduire beaucoup de théories ne concernant pas la coloration de graphes et n’étant pas spécialement fortement utilisées en dehors de ce contexte. Certaines définitions peuvent alors être un peu déroutantes, comme par exemple la définition d’orientation ou encore de chemin le plus court, cette dernière notion étant particulièrement floue dans l’article. Par → − exemple, lorsque les auteurs décrivent la construction du graphe G λ , ils écrivent ceci : CHAPITRE 3. Une application du VSS “Given a solution G ∈ S 3 50 → − , let G λ denote the digraph obtained by removing all arcs that do not belong to a longest path in G. ” Il a été, dès lors, assez difficile de savoir s’il fallait choisir un chemin de longueur maximale ou s’il fallait considérer tous les chemins de ce type. La logique et la compréhension appliquée dans ce rapport est de les considérer tous. 3.4.2 Implémentation L’implémentation a donné lieu, elle aussi, à quelques difficultés. Ces dernières sont explicitées dans cette sous-section. Liste tabou L’idée des configurations à éviter et des groupes de configurations à éviter fut assez déroutante à mettre en oeuvre de par les différentes méthodes à implémenter correctement pour que les deux coïncident parfaitement avec l’interface TabuElement. Pré et post-traitement Le pré-traitement et le post-traitement à effectuer entre les transitions de l’espace 1 à 3 et 3 à 2 furent particulièrement éprouvants. En effet, le pré-traitement doit inverser certaines arêtes du graphe dans certaines conditions ce qui, lorsqu’une seule des conditions est oubliée, peut créer des graphes cycliques (ce qui est très problèmatique pour l’espace de recherche S3 ). De manière identique, le post-traitement ajoute des arêtes au graphe (qui avaient été supprimées avant) dans le sens minimisant le plus long chemin dans le graphe. Le problème de ceci est que l’ajout dans un sens peut impliquer un cycle et donc une longueur de plus long chemin égale à +∞. Ce dernier problème a d’ailleurs motivé la modification de l’algorithme de calcul des longueurs des plus longs chemins (développé dans la section 3.2.2) afin d’y incorporer une détection de cycle de manière à obtenir une valeur infinie plutôt qu’une récursion infinie. Algorithmes tabous Les algorithmes tabous que sont TabuCol et PartialCol sont particulièrement difficiles à implémenter. En effet, la moindre petite erreur peut mettre en cause l’ensemble du programme en ce sens où l’approche de la solution optimale est totalement chamboulée. Ils furent l’objet de beaucoup d’attention pendant ce projet afin d’éviter ce cas de figure et ceci ne fut pas de tout repos. 51 Conclusion La coloration de graphes est un problème N P -complet bien connu et ses applications sont multiples. En effet, la résolution de ce problème permet, entre autres, de résoudre un sudoku, de gérer un réseau d’émetteurs/récepteurs, d’effectuer un ordonnancement ou encore de générer des emplois du temps. Ce problème étant N P -complet, il ne peut être résolu par un algorithme exact en un temps raisonnable, il faut donc faire appel aux métaheuristiques. Celles-ci sont nombreuses et ont déjà été appliquées avec succès à ce problème, elles sont en majorité basées sur une population. Dans ce rapport, une métaheuristique à solution unique récente qui utilise différents espaces de recherches, voisinages et fonctions objectifs a été exposée. Celle-ci, appelée Variable Space Search et développée par Hertz, Plumettaz et Zufferey [3], a ensuite été appliquée à la coloration de graphes. Cette métaheuristique un peu atypique met en jeu plusieurs espaces de recherche (trois pour la coloration) complémentaires afin d’approcher de l’optimum local. Cette métaheuristique a alors été programmée en JAVA afin d’être testée et permettre d’en tirer quelques résultats. Ces résultats, bien qu’assez décevants car assez éloignés de ceux récoltés par les auteurs de l’article, ont permis de confirmer que la métaheuristique développée par les trois auteurs fonctionne. De plus, si l’application mettant en oeuvre cette métaheuristique est optimisée au maximum, elle fait partie des meilleurs algorithmes connus jusque maintenant pour résoudre ce problème N P -complet. Cette métaheuristique fonctionne particulièrement bien sur les graphes peu denses, elle pourrait donc être associée à une autre manière de procéder pour les graphes denses (ou avec d’autres espaces de recherche ou encore d’autres transitions) pour en tirer son plein potentiel. 52 Bibliographie [1] Wikipedia. Graph coloring applications, December 2010. http://en. wikipedia.org/wiki/Graph_coloring#Applications. [2] Wikipedia. Un exemple d’application de coloration de graphes, December 2010. http://fr.wikipedia.org/wiki/ Coloration_de_graphe#Un_exemple_d.27application_: _l.27allocation_de_fr.C3.A9quences. [3] N. Zufferey, M. Plumettaz, A. Hertz. Variable space search for graph coloring. Discrete Applied Mathematics, 156 :2551–2560, 2008. doi :10.1016/j.dam.2008.03.022. [4] R. Diestel. Graph Theory (4th edition). Springer-Verlag, Heidelberg : Graduate Texts in Mathematics, July 2010. ISBN 978-3-642-14278-9. [5] Wikipedia. Graph coloring : History, December 2010. http://en. wikipedia.org/wiki/Graph_coloring#History. [6] Wolfgang Haken Kenneth Appel. Every Planar Map is Four-Colorable. Providence, RI : American Mathematical Society, 1989. ISBN 0821851039. [7] G.D. Birkhoff. A determinant formula for the number of ways of coloring a map. Ann. Math, 14 :42–46, 1912. [8] W.T. Tutte. Graph Theory. Addison-Wesley, 1984. [9] M. Chudnovsky, N. Robertson, P.Seymour, R.Thomas. The strong perfect graph theorem. Annals of Mathematics, 164 :51–229, 2006. doi :10.4007/annals.2006.164.51. [10] Richard M. Karp. Reducibility among combinatorial problems. Complexity of Computer Computations, pages 85–103, 1972. http://www. cs.berkeley.edu/~luca/cs172/karp.pdf. [11] Russel, Jarvis. Sudoku enumeration problems, December 2010. http ://www.afjarvis.staff.shef.ac.uk/sudoku/. [12] V. Kumar. Algorithms for constraint-satisfaction problems : A survey. AI Magazine, 13 :32–44, 1992. [13] D.J.A. Welsh, M.B. Powell. An upper bound for the chromatic number of a graph and its application to timetabling problems. The Computer Journal, 10(1) :85–86, 1967. doi :10.1093/comjnl/10.1.85. BIBLIOGRAPHIE 53 [14] C. Fleurent, J.A. Ferland. Genetic and hybrid algorithms for graph coloring. Annals of Operations Research, 63 (3) :437–461, 1996. [15] P. Galinier, J.K. Hao. Hybrid evolutionary algorithms for graph coloring. Journal of Combinatorial Optimization, 3 (4) :379–397, 1999. [16] A. Hertz, D. de Werra. Using tabu search techniques for graph coloring. Computing, 39 :345–351, 1987. [17] I. Bloechliger, N. Zufferey. A graph coloring heuristic using partial solutions and a reactive tabu scheme. Computers & Operations Research, 35 :960– 975, 2008. [18] N. Zufferey, C. Avanthay, A. Hertz. A variable neighborhood search for graph coloring. European Journal of Operational Research, 151 :379–388, 2003. [19] M.Mladenović. Formulation space search — a new approach to optimization (plenary talk). Vrnjacka Banja, Serbia, 2005. [20] C. Morgenstern. Distributed coloration neighborhood search. DIMACS Series in Discrete Mathematics and Theoretical Computer Science, 26 :335– 357, 1996. [21] T. Gallai. On directed paths and circuits. Theory of Graphs, Academic Press, New York, pages 115–118, 1968. [22] B. Roy. Nombre chromatique et plus longs chemins d’un graphe. Revue AFIRO 1, pages 127–132, 1967. [23] L.M. Vitaver. Determination of minimal coloring of vertices of a graph means of boolean powers of the incidence matrix. Doklady Akademii Nauk SSSR147, pages 758–759, 1963. [24] B. Gendron, A. Hertz, P. St-Louis. On edge orienting methods for graph coloring. Journal of Combinatioral Optimization, 13 :163–178, 2007. [25] P. Norvig S. Russel. Artificial Intelligence : A Modern Approach (3rd edition). Pearson, 2010. [26] Dimacs website. ftp://dimacs.rutgers.edu/pub/challenge/ graph/. 54 Annexe A Exemple de la Belgique La Belgique présente une géographie telle que ses provinces peuvent être coloriées légalement avec seulement trois couleurs, voici les différentes étapes utilisées dans le raisonnement pour appliquer la coloration de graphes à la coloration de carte. A partir de la carte vierge, le graphe planaire est créé puis colorié et les résultats trouvés sont transposés à la carte. F IGURE A.1 – Carte de Belgique non coloriée. ANNEXE A. Exemple de la Belgique FlandreOccidentale FlandreOrientale 55 Anvers Limbourg BrabantFlamand Hainaut BrabantWallon Liege Namur Luxembourg F IGURE A.2 – Graphe planaire non colorié. FlandreOccidentale FlandreOrientale Anvers Limbourg BrabantFlamand Hainaut BrabantWallon Namur Luxembourg F IGURE A.3 – Graphe planaire 3-colorié F IGURE A.4 – Carte de Belgique 3-coloriée Liege 56 Annexe B Améliorations de TabuCol Deux mécanismes, proposés par les auteurs de l’algorithme, sont utilisés pour améliorer les performances de cet algorithme : la fonction d’aspiration ainsi qu’un critère supplémentaire pour stopper la génération de voisins. Fonction d’aspiration Cette fonction représente le niveau d’aspiration A(z) de la valeur de la fonction objectif à atteindre lorsque la valeur courante est z = f (s). Plus concrètement, si un mouvement tabou s → s0 est généré mais qu’il est tel que f (s0 ) ≤ A(z), alors le mouvement s → s0 n’est pas considéré tabou pour cette itération. Il s’agit donc simplement d’un critère permettant d’effectuer un mouvement tabou car il est peut-être bénéfique. De plus ce critère est mis à jour et est de plus en plus restrictif, ainsi un mouvement rencontrant le critère d’aspiration à une certaine itération ne le rencontrera probablement plus par la suite. La fonction d’aspiration est définie pour toutes les valeurs de z possibles (donc de 0 à une certaine borne supérieure, le nombre de sommets par exemple). On l’initialise comme A(z) = z−1 pour toutes les valeurs de z. Ensuite, lors de la génération d’un mouvement s → s0 tel que f (s0 ) ≤ A(f (s)), on place A(f (s)) = f (s0 ) − 1. Critère d’arrêt supplémentaire Ce critère est assez simple et est appliqué dans la boucle permettant de générer un nombre fixé neighcount de voisins aléatoirement. Dans cette boucle, dès qu’un mouvement s → s0 tel que f (s0 ) < f (s∗ ) (où s∗ est la meilleure solution trouvée jusque maintenant) est généré, l’algorithme va directement effectuer le mouvement s → s0 au lieu d’attendre d’avoir généré les neighcount voisins. Cette amélioration va permettre de diminuer les temps de calcul (moins de voisins générés) à chaque itération et donc améliorer les performances globales de l’algorithme. ANNEXE B. Améliorations de TabuCol 57 L’algorithme devient donc : Algorithme 9 TabuCol Entrée : – un graphe G = (V, E), – un entier k représentant le nombre de couleurs, – tabsize la taille de la liste tabou, – neighbcount le nombre de voisins à générer, – itermax le nombre maximal d’itérations. Sortie : une k-coloration de G tentant de minimiser le nombre d’arêtes conflictuelles. 1: Génération d’une solution aléatoire initiale s 2: iter ← 0 3: T ← nouvelle liste de taille tabsize 4: Tant que f (s) > 0 et que iter < itermax faire 5: neighbours ← {} 6: count ← 0 7: Tant que count < neighbcount ou que f (si ) < f (s) faire 8: si ← nouveau voisin généré aléatoirement 9: aspirationcriteria ← f (si ) ≤ A(f (s)) 10: Si aspirationcriteria ou s → si 6∈ T alors 11: count ← count + 1 12: ajouter si à neighbours 13: Si aspirationcriteria alors 14: A(f (s)) ← f (si ) − 1 15: fin Si 16: fin Si 17: fin Tant que 18: s∗ ← choisir le meilleur des si (au sens de la valeur de la fonction objectif en s∗ ) 19: Ajouter s∗ → s à T (en enlevant la plus vieille valeur de T si T est pleine) 20: s ← s∗ 21: iter ← iter + 1 22: fin Tant que 23: Retourner s