Étant donnés G et k, déterminer si G est k-coloriable est un problème NP-complet. Comme il ne serait pas raisonnable d'allouer un temps non-polynômial au problème de l'allocation de registres, on procède de la manière suivante : on tente de colorier le graphe avec k couleurs mais, en cas d'échec, on s'autorise à retirer (spill) un ou plusieurs sommets du graphe. Dans un second temps, on considérera ces sommets éliminés et on leur appliquera un second algorithme de coloration, avec cette fois une infinité de couleurs possibles.
module S = Set.Make(struct type t = int let compare = Pervasives.compare end) module M = Map.Make(struct type t = int let compare = Pervasives.compare end)Un graphe est alors représenté par un dictionnaire associant à chaque sommet l'ensemble de ses voisins :
type graph = S.t M.tLes graphes sont non orientés : dès lors que x est dans l'ensemble des voisins de y alors y est de même dans l'ensemble des voisins de x.
Les couleurs sont également représentées par des entiers (consécutifs). Un coloriage est un dictionnaire associant à chaque sommet d'un graphe sa couleur :
type coloring = int M.t
On considère ici la variante de l'algorithme de Chaitin appelée « coloriage optimiste », dont le pseudo-code est le suivant :
colorier(g) si g est vide renvoyer le coloriage vide s'il existe un sommet s de g de degré < k colorier (g \ s) attribuer une couleur disponible à s sinon choisir un sommet s de g arbitrairement colorier (g \ s) si une couleur reste disponible pour s, la lui attribuer sinon spiller sÉcrire une fonction
val color : int -> graph -> coloringréalisant cet algorithme. Les k couleurs seront représentées par les entiers 0,1,...k-1 et les sommets spillés porteront la couleur -1.
Indications. Il pourra être utile de commencer par définir les fonctions suivantes :
Tests. Du code vous est fourni pour tester. Pour l'utiliser il faut récupérer d'une part le fichier bench.ml et d'autre part l'archive tests.tar.gz qu'il convient de décompresser à l'endroit où vous travaillez (avec tar zxf tests.tar.gz). Les fichiers fournis s'utilisent ainsi :
open Bench module B = Make(S)(M)et on compile avec une commande de la forme ocamlopt bench.ml td12.ml -o td12. Le module B fournit une fonction
B.bench1 : int -> (int -> graph -> coloring) -> unitqui prend en argument k et votre fonction color et la teste sur tous les fichiers contenus dans le répertoire "tests". Le résultat doit ressembler à :
coloriage avec k = 3 tests/full-3.dot: coloriage ok / 0 spilled sur 3 / rapport = 0.00 tests/lin-3.dot: coloriage ok / 0 spilled sur 3 / rapport = 0.00 tests/full-4.dot: coloriage ok / 1 spilled sur 4 / rapport = 0.25 tests/circle-4.dot: coloriage ok / 0 spilled sur 4 / rapport = 0.00 tests/lin-4.dot: coloriage ok / 0 spilled sur 4 / rapport = 0.00 ... moyenne = 0.05Pour vous permettre de débugger, le module B fournit également les fonctions suivantes :
L'algorithme proposé est le suivant :
spill(g) si g est vide renvoyer le coloriage vide sinon choisir arbitrairement un sommet s de g colorier (g \ s) attribuer à s la plus petite couleur non utilisée par ses voisinsÉcrire une fonction
val spill : graph -> coloringréalisant cet algorithme. Les couleurs utilisées seront représentées par des entiers consécutifs 0,1,...k-1. On pourra avantageusement réutiliser la fonction de suppression d'un sommet dans un graphe utilisée dans la question précédente.
Du code est également fourni pour tester :
coloriage avec une infinité de couleurs tests/full-3.dot: coloriage ok / 3 couleurs pour 3 sommets / rapport = 1.00 tests/full-10.dot: coloriage ok / 10 couleurs pour 10 sommets / rapport = 1.00 tests/lin-3.dot: coloriage ok / 2 couleurs pour 3 sommets / rapport = 0.67 tests/full-4.dot: coloriage ok / 4 couleurs pour 4 sommets / rapport = 1.00 tests/circle-4.dot: coloriage ok / 2 couleurs pour 4 sommets / rapport = 0.50 tests/lin-4.dot: coloriage ok / 2 couleurs pour 4 sommets / rapport = 0.50 ... moyenne = 0.66