Isomorphismes de graphes, ou la non-trivialité de « est-ce que ce graphe est identique à celui-ci ? »

Tiens, ça fait longtemps que j’ai pas causé de maths ici. Il y a eu des actualités intéressantes très récemment, alors je vais en parler un peu, en espérant pas dire trop de bêtises parce que ça commence à toucher à des trucs que je maîtrise moins 🙂

Il y a à peu près un an, László Babai, mathématicien-avec-une-bonne-dose-d’informaticien a fait l’équivalent des (très) gros titres dans le monde de l’informatique théorique avec un article intitulé Graph Isomorphism in Quasipolynomial Time ou, en français, « Isomorphisme de graphes en temps quasipolynomial » (Je vais citer Langelot Pickpocket : « C’est très facile d’écrire l’anglais. Il n’y a qu’à renverser l’ordre des noms et des adjectifs. »). Il y a quelques jours, le même László Babai a publié sur son blog un « oops, en fait ça passe pas » (c’est-à-dire une rétractation partielle du papier, il y avait une erreur dans l’analyse qui ne permettait pas de conclure à un temps quasi-polynomial). Aujourd’hui, alors même que je retournais voir cette page pour ajouter un lien vers mon blog, je vois que le temps quasi-polynomial est de nouveau sur le tapis ! C’est plus palpitant que Game of Thrones, cette histoire 😉

Bon, là, normalement, j’ai perdu mes trois lecteurs. Snif. Revenez, les gens, jvais expliquer un peu de quoi on cause… D’abord, petite question : est-ce que ces deux graphes sont identiques ?

isomorphisme-1

Normalement, c’est le moment où vous me dites « ben, ça dépend… ça veut dire quoi, identique ? » – ce à quoi je réponds « ben, ça dépend de la définition qu’on choisit, en fait ».

J’utilise ici le terme « identique » pour dire « isomorphe », ce qui est un abus de langage mais un que je m’autorise. Isomorphe, c’est un mot blindé de grec, comme l’indique le « ph » (ouais, je fais de l’approximation liguistique aussi) : préfixe « iso » – même, et « morphe » – forme. Donc, « graphes isomorphes » : graphes qui ont la même tête. Là, j’en ai deux qui se disent « elle se paye ma fiole », parce que les deux graphes ci-dessus, ils ont pas vraiment la même tête, à froid comme ça.

Mais si je les affiche comme ça, est-ce que c’est mieux ?

isomorphisme-2

Avec des petits numéros, je peux dire que :

  • 1 est relié à 3, 4 et 5
  • 2 est relié à 3,4 et 5
  • 3 est relié à 1, 2, 4 et 5
  • 4 est relié à 1, 2, 3 et 5
  • 5 est relié à 1, 2, 3 et 4

et ce sur les deux graphes. C’est ce qu’on entend par « isomorphisme de graphe » : on peut associer chaque sommet du premier graphe à un sommet du deuxième graphe de sorte que si les sommets u et v sont reliés par un arc dans le premier graphe, alors ils sont reliés par un arc dans le deuxième graphe, et vice-versa. Et la question à laquelle Babai s’intéresse, c’est « quelle est la complexité algorithmique de décider si deux graphes sont isomorphes ? »

Il existe des cas où la réponse est « les deux graphes ne sont pas isomorphes » de manière immédiate. Parmi ces cas :

  • les graphes n’ont pas le même nombre de sommets
  • les graphes n’ont pas le même nombre d’arcs
  • il existe un sommet de degré k dans le premier graphe mais pas dans le second (le degré d’un sommet, c’est le nombre d’arcs qui y sont connectés)
  • le premier graphe a une structure, par exemple quatre sommets tous reliés les uns aux autres, qui n’existe pas dans le deuxième graphe

Il existe aussi un algorithme simple pour conclure à la question : numéroter tous les sommets du premier graphe, et essayer toutes les numérotations des sommets du deuxième graphe. Si on en trouve une qui marche, c’est gagné, et si on n’en trouve pas, c’est que les graphes ne sont pas isomorphes. Le problème de cet algorithme là, c’est qu’il réclame un temps d’exécution de n! (factorielle n), où n est le nombre de sommets. De manière générale, les algorithmes avec une factorielle au milieu, on n’aime pas trop.

Le problème est intéressant à – au moins – deux titres. Le premier, c’est qu’il arrive de résoudre des instances du problème dans la vraie vie, en chimie (identifier des composés dans une base de données) ou en électronique (vérifier qu’un circuit intégré est équivalent à son schéma). Comme on fait ça de manière quotidienne, c’est pas forcément le point critique. Le second titre, c’est que le problème « Graph Isomorphism » (GI, pour son petit nom) fait partie de cet ensemble de problèmes qu’on sait pas trop où mettre, d’un point de vue complexité. Il est dans NP – il suffit de fournir un étiquetage des sommets pour vérifier que deux graphes sont effectivement isomorphes – mais personne ne sait s’il est NP-complet (« aussi difficile que tous les autres problèmes de la classe NP »). Une hypothèse usuelle est que, si P est différent de NP, GI se trouve quelque part entre P et les problèmes NP-complets.

Jusqu’à l’an passé, la borne la plus basse qu’on avait pour la complexité de GI était e^{O(\sqrt{n \log n})}. La borne annoncée, puis rétractée, puis rétablie, est e^{\log^cn} pour une constante c. Et là, pour le coup, je l’avoue sans honte – je « sais » que le deuxième est très petit devant le premier (pour n suffisamment grand, s’entend), mais il faut que j’y réfléchisse à deux fois. Quant au fait que  e^{\log^cn} corresponde à « quasi-polynomial »… bon, e^{C \log n} est polynomial, e^{n^\varepsilon} est exponentiel, l’autre est quelque part entre les deux… Ça reste une gymnastique qui ne m’est pas évidente. Il reste que du point de vue théorique, passer d’un algo exponentiel (même modérément exponentiel) à un algo qui est non seulement sub-exponentiel mais quasi-polynomial, c’est classe.

J’ai un peu pipoté quand je parlais des problèmes de la vraie vie. Parce qu’il faut également l’admettre, d’un point de vue pratique, suivant la tronche de la constante c, il faut avoir un très, très (très) gros graphe avant que le « nouveau » résultat ne « vaille le coup » (sans même parler de la complexité implémentatoire dudit algo !). Les instances de la vraie vie, elles, restent probablement beaucoup plus simples et rapides à résoudre à grands coups d’heuristiques et de bourrinage.

J’aimerais être capable de dire « mais bon, j’ai lu la preuve et c’est très joli ». Parce que le papier, je l’ai ouvert, j’ai fait « AAAAAH », et je l’ai refermé. La vidéo de la conf de Babai, je l’ai regardée dans une espèce d’état second « alors c’est probablement très intéressant, mais je comprends quedalle ». Une version mise à jour du papier original (avec les corrections nécessaires) est apparemment en cours de rédaction ; j’ai comme vague projet d’essayer d’attaquer la compréhension de ce truc à un moment ou à un autre (avec assez peu d’optimisme sur le résultat final 😉 ).

TPA : Théorème de Ramsey

Je viens de me rendre compte que j’avais pas encore parlé de théorie de Ramsey dans ce blog, ce qui est profondément scandaleux, parce que c’est un sujet très rigolo. Donc, commençons – aujourd’hui, c’est TPA.

Supposons que nous ayons un graphe dit « complet », c’est-à-dire qu’on se donne un ensemble de sommets, et qu’on trace tous les arcs possibles entre tous les sommets. On dénote par K_n le graphe complet sur n sommets - par exemple, je vous présente K_5 :

 

k5-mono

Maintenant, on va colorier les arcs du graphe en rouge et en bleu, de façon arbitraire. On peut par exemple colorier tous les arcs en bleus, tous les arcs en rouge, ou choisir d’autres coloriages :

Et avec ce coloriage, on se pose une première question : est-ce qu’il est possible d’avoir un coloriage tel qu’on ne puisse pas trouver dans la figure un triangle bleu ou un triangle rouge, c’est-à-dire trois sommets reliés uniquement par des arcs bleus ou rouges ? Les deux du haut ne fonctionnent clairement pas (puisque tous les sommets sont reliés par des arcs bleus et rouges respectivement). Le coloriage en bas à gauche ne fonctionne pas non plus : il a un triangle bleu et un triangle rouge (vous les voyez ?). Le coloriage en bas à droite n’a pas de triangle rouge, mais il a un triangle bleu.

Pour 5 points, ce n’est pas très difficile de finir par trouver un coloriage qui ne contient ni triangle bleu ni triangle rouge, et c’est celui-ci :

k5-notriangle

L’étape suivante est de se poser la même question non pas avec un graphe complet sur 5 sommets (K_5) mais avec un graphe complet sur 6 sommets (K_6), comme celui-ci :

k6

Et la réponse est non, ce n’est pas possible : on ne peut pas colorier une K_6 avec deux couleurs en évitant d’avoir un triangle monochromatique (tout bleu ou tout rouge, donc). La première remarque qu’on peut faire, c’est que pour vérifier par force brute que c’est effectivement le cas, ça va prendre un petit peu de temps. Le graphe ci-dessus a 15 arcs, ce qui correspond à 2^{15} coloriages différents. Pour voir ça, on peut choisir un ordre pour les arcs (n’importe quel ordre) et commencer à colorier. Pour le premier arc, on peut choisir rouge ou bleu, ça fait deux choix. Pour le deuxième arc, idem – et on a déjà quatre choix avec deux arcs, puisqu’ils peuvent être tous les deux rouges, tous les deux bleus, rouge-bleu ou bleu-rouge. Le troisième choix multiplie encore ces choix par deux (puisque chaque choix peut correspondre à un troisième arc rouge ou bleu), et ainsi de suite pour les 15 arcs, soit 2×2×2×…×2 = 2^{15}. 2^{15}, ça fait 32768. À la main, ça va faire beaucoup de coloriages à vérifier, mais sur un ordinateur, ça reste encore nettement dans le domaine du faisable.

Avant de programmer le coloriage et de vérifier, on peut se demander s’il y aurait moyen de prouver qu’il y a toujours un triangle monochromatique dans une K_6 coloriée avec 2 couleurs. Donc, on prend un coloriage, n’importe lequel. On prend un sommet, n’importe lequel aussi. Ce sommet, il a 5 arcs qui y sont connectés, et parmi ces cinq arcs, il y en a au moins trois qui sont de la même couleur (parce que si j’ai au plus deux arcs bleus ET au plus deux arcs rouges, ben j’ai au plus 4 arcs). Pour faire simple, je vais supposer que les trois arcs qui sont de la même couleur sont bleus (je peux toujours faire ça, si ce n’est pas le cas il suffit de remplacer rouge par bleu et bleu par rouge dans ce qui suit). Maintenant on fait une petite figure avec des notations.

k6-firstcolors

J’ai au moins trois arcs bleus qui partent de mon sommet initial, j’en choisis exactement trois (les deux autres peuvent être rouges ou bleus). Ces arcs définissent trois points sur le graphe, qui sont les autres extrémités des trois arcs ; je nomme ces points u, v et w. Et là, il peut se passer plusieurs choses. Si l’arc entre u et v est bleu, alors j’ai un triangle bleu. Si l’arc entre v et w est bleu, alors j’ai un triangle bleu. Si l’arc entre u et w est bleu, alors j’ai un triangle bleu. Et si rien de tout ça ne se passe…

k6-plusred

ben l’arc entre u et v est rouge, l’arc entre v et w est rouge, et l’arc entre w et u est rouge aussi, donc le triangle défini par u, v, w est tout rouge. Tout ce que j’ai raconté là s’applique à n’importe quel coloriage (même s’il faut éventuellement changer rouge en bleu et bleu en rouge), et donc on ne peut pas trouver de coloriage qui n’ait ni triangle bleu, ni triangle rouge.

La remarque suivante, c’est que n’importe quelle K_n (c’est-à-dire un graphe complet sur n sommets), pour n \geq 6, contient une K_6. Donc si on ne peut pas colorier une K_6 sans avoir de triangle monochromatique, on ne peut pas non plus colorier une K_n, n\geq 6 sans triangle monochromatique.

Donc, pour les triangles, on sait ce qu’il se passe : pour n\leq 5, on peut trouver un coloriage tel qu’on ne trouve pas de triangle monochromatique, et pour n \geq 6, on a prouvé qu’il n’existait pas.

La question suivante est : maintenant qu’on a des choses pour les triangles, est-ce qu’on peut avoir le même genre de résultat pour plus de points ? Plus spécifiquement, si je considère une K_n, est-ce que je peux la colorier avec deux couleurs de manière à éviter d’y trouver une K_4, K_5, K_6, K_{42} de la même couleur (remarquons qu’un triangle est une K_3 😉 )? Et ben, c’est une question compliquée. Pour être sûr de trouver une K_4, il a été prouvé qu’on a besoin de 18 points. Ce qui veut dire qu’on peut colorier ce bidule-là avec 2 couleurs et ne pas y trouver de K_4 monochromatique:

K17

Si on rajoute un sommet et qu’on le connecte à tous les autres, par contre, on est sûr de trouver une K_4 monochromatique si on colorie avec deux couleurs. Là, pour le coup, si on veut réellement vérifier tous les coloriages possibles, il va vraiment falloir faire des progrès sur la puissance informatique. Une K_{18} (un graphe complet sur 18 sommets, donc) a 153 arcs, donc si on se mettait en tête de vérifier tous les coloriages, il faudrait en vérifier 2^{153}. Si on est capable de vérifier un coloriage par nanoseconde (ce qui n’est pas franchement réaliste), il faudrait plus de 3 000 000 000 000 000 000 000 000 000 (3 suivi de 27 zéros) SIÈCLES pour tout vérifier… on est pas rendu 🙂

Là où ça devient VRAIMENT amusant, c’est quand on se pose la question de trouver des K_5 et supérieur (qui soient monochromatiques, donc). Pour la K_5, on sait que pour plus de 49 sommets, on la trouvera nécessairement. On sait aussi qu’on peut construire un coloriage sur 42 sommets sans K_5 monochromatique. Le lecteur attentif aura remarqué qu’on ne sait pas ce qu’il se passe entre 43 et 49 sommets… La situation est de moins en moins claire au fur et à mesure : pour la K_{10} monochromatique, on sait que le seuil se situe quelque part entre 798 (pour 797 sommets on sait qu’on peut colorier sans avoir de K_{10} monochromatique) et… 23556 (pour un graphe complet sur 23556 sommets colorié avec deux couleurs, on sait qu’on aura toujours une K_{10} monochromatique).

Le théorème de Ramsey qui donne son nom à ce billet de blog dit que quelque soit r, on peut trouver un n tel que tout coloriage avec deux couleurs d’une K_n contienne nécessairement une K_r monochromatique. Ledit théorème est plus général que ça : il dit que pour tout r_1, r_2, r_3... r_\ell on peut trouver un n tel que tout coloriage de K_n avec \ell couleurs c_1, c_2,..., c_\ell contient ou bien une K_{r_1} de couleur c_1, ou bien une K_{r_2} de couleur c_2, …, ou bien une K_{r_\ell} de couleur c_\ell. Et on dénote n par R(r_1, r_2,..., r_\ell). (Pour rattacher aux trucs précédents, j’ai démontré que R(3,3) = 6, et j’ai indiqué que R(4,4) = 18, 43 \leq R(5,5) \leq 49 et que 798 \leq R(10,10) \leq 23556.

On peut exprimer ça de façon plus « philosophique » en disant qu’une structure suffisamment grande aura toujours un peu d’ordre local quelque part. Ce que je trouve personnellement assez fascinant.

Le mot de la fin revient à Joel Spencer: « Erdős nous a demandé d’imaginer une force d’extraterrestres, beaucoup plus puissante que nous, qui atterrit sur Terre et nous demande la valeur de R(5,5), ou bien ils détruisent la planète. Dans ce cas, dit-il, nous devrions combiner la puissance de tous nos ordinateurs et de tous nos mathématiciens pour tenter de trouver la valeur. Mais supposons qu’ils demandent R(6,6). Dans ce cas, il pense qu’il faudrait détruire les extraterrestres. » (traduction personnelle).

TPA : Mincut, maxflow et réseaux de distribution d’eau

Je viens de regarder une vidéo intitulée Efficient Maximum Flow Algorithms (en anglais), et comme je me demandais depuis quelques jours ce dont je pourrais bien parler dans un TPA, c’est arrivé à point nommé. Donc, aujourd’hui, on va parler de « max flow » ou, en français, « flot maximum ». Commençons par donner un petit exemple. Supposons qu’on veuille trimbaler de l’eau depuis un point A à un point B. Pour cela, on dispose d’un réseau de tuyaux qui ont tous une certaine capacité, en terme de nombre de litres par seconde qui peuvent y circuler, et on se pose la question de savoir combien de litres par seconde peuvent arriver au point A. Faisons un petit dessin :

On a le point A, le point B, quelques points intermédiaires reliés par des tuyaux, et la capacité de chaque tuyau est indiquée sur chaque tuyau. Sur un « petit » réseau comme celui-ci, on peut s’en sortir en regardant très fort le schéma et en tentant de répartir l’eau au maximum dans les tuyaux de façon à ce qu’aucun tuyau ne soit en surcapacité, sinon ça explose et ça fait désordre (spoiler : sauf erreur grossière de ma part, on arrive avec ce réseau à 5600 litres par seconde).

Évidemment, plus le réseau est compliqué, plus le calcul l’est, et au-delà d’une certaine taille ça devient franchement pénible à faire à la main. Beaucoup de gens se sont intéressés au problème, qui est à la fois considéré comme un problème « classique » voire « résolu » (par exemple la bibliothèque C++ Boost a quelques implémentations pour le résoudre), et comme un domaine actif de recherche (il est, par exemple, en couverture du numéro d’août des Communications of the ACM). Au lieu de parler de litres d’eau et de tuyaux, on utilise notre modèle préféré, qui est celui d’un graphe (comme le laisse vaguement sous-entendre mon petit schéma au-dessus) ; le type de graphe qu’on considère a cependant quelques éléments supplémentaires par rapport aux graphes que nous avons manipulés jusqu’ici, par exemple lorsque nous avons parlé du théorème des cinq couleurs. On a, comme d’habitude, des sommets (les points A, B et les points intermédiaires) et des arcs (les tuyaux). Dans les sommets, on a deux sommets « spéciaux », les points A et B, que l’on appelle la source et le puits. Sur les arcs, on a deux informations supplémentaires : la direction, représentée sur le schéma par une flèche (on dit que le graphe est dirigé) et la capacité. Et on veut trimbaler une certaine quantité de trucs, c’est-à-dire un « flot », de A à B. Le problème peut donc être formulé comme « étant donné un graphe dirigé, dont les arcs ont des capacités, et étant donnés une source et un puits, quelle est la quantité maximum de flot qui peut être transportée de A à B ? ». Les algorithmes permettant de répondre à cette question ne sont pas forcément très compliqués, mais ils sont plus longs que ce que j’ai envie de détailler ici. Il en existe plusieurs, et il peut être intéressant de ne pas utiliser les mêmes en fonction du graphe considéré (on n’utilisera par exemple pas forcément le même algorithme pour un graphe ayant peu ou beaucoup d’arcs).

Considérons maintenant un autre problème. Supposons que j’aie une fuite massive d’eau en B, et que je veuille couper l’eau qui arrive en B en coupant la circulation dans des tuyaux, et ce de la manière la plus « efficace » possible. L’efficacité, dans mon exemple imaginaire, se mesure en débit total des tuyaux que je coupe : je veux le minimiser, pour que la reprise du service soit la plus facile possible. Quels tuyaux dois-je couper ? Il faut impérativement que je coupe le tuyau direct à 4000 entre A et B (sinon j’ai forcément de l’eau qui continue à arriver en B). Je peux couper tout ce qui arrive en B : j’ai deux tuyaux à 1000 et un à 900, ça fait un « coût » total de 2900. Mais en regardant d’un peu plus près mon graphe, plutôt que de couper l’arrivée en B, si je coupe les départs en A (hors la ligne à 4000, qui est coupée quoi qu’il arrive), je peux m’en sortir pour 2300, c’est mieux. Et en regardant d’encore plus près, si au lieu de couper le tuyau à 2000 je coupe celui à 500 et celui à 800, je descends à 1600 (300 + 500 + 800). Coïncidence amusante, si je fais la somme de tous les tuyaux que je coupe (4000+300+500+800), j’obtiens le même nombre que le nombre maximum de litres que je peux faire circuler dans mon réseau entre A et B, c’est à dire 5600.

Il se trouve que ce n’est pas une coïncidence. Ce second problème est connu sous le nom de « min cut » ou, en français, « coupe minimum » : étant donné un graphe avec des coûts ou poids sur les arcs, comment déconnecter deux points donnés A et B en supprimant des arcs de manière à ce que la somme de leurs poids soit minimum ? Et étant donné un graphe dirigé avec des capacités/poids, le théorème dit « min cut/max flow » dit que, pour toutes les paires de sommets A et B du graphe, la quantité maximum de flot qui peut aller de A à B est égale au poids minimum des arcs à supprimer pour déconnecter A et B. Pour donner une idée de la preuve, on peut voir qu’une coupe (un ensemble d’arcs qui, lorsqu’on les supprime, déconnecte A et B) est un goulot d’étranglement : il est impossible de faire passer plus de flot entre A et B que la capacité des arcs qui forment cette coupe. Ça permet de déduire que le flot maximum est forcément inférieur (ou égal) au poids de la coupe minimum. L’autre direction (montrer que la valeur de la coupe minimum est effectivement atteinte) est un peu plus délicate. L’idée est que si on a un flot maximum, alors on a des arcs qui sont saturés, c’est-à-dire que pour que le flot puisse passer de A à B, il faut faire passer une valeur de flot égale à la capacité de l’arc par l’arc en question. Si ce n’était pas le cas, on pourrait augmenter le flot, ce qui ne peut pas arriver (puisqu’on considère un flot maximum). On peut aussi voir que tous les chemins de A à B contiennent au moins un arc saturé – sinon, on pourrait augmenter le flot sur le chemin en question. Maintenant, on explore le graphe en partant de A, en avançant sur tous les chemins possibles, et on marque tous les sommets que l’on peut atteindre sans passer par un arc saturé. Si on retire tous les arcs qui partent de cet ensemble de sommets marqués vers l’ensemble des sommets non-marqués, on obtient une coupe (sinon il existerait un chemin de A à B contenant un arc non saturé). De plus, ces arcs sont saturés, et la somme de leur poids est exactement la valeur du flot maximum (puisqu’il s’agit de l’ensemble des arcs qui sortent de l’ensemble des sommets marqués). Donc la valeur de flot maximum est égale à la valeur d’une coupe, elle-même supérieure ou égale à la valeur de la coupe minimum. Tout ceci nous permet de déduire que la valeur de la coupe minimum est égale à la valeur du flot maximum (puisqu’elle lui est à la fois inférieure ou égale et supérieure ou égale), et donc on est contents. Pour une certaine définition de « contents » – l’idée de la preuve que j’ai donnée ici n’est pas des plus rigoureuse (en espérant qu’elle soit correcte, il a fallu que je m’y reprenne à trois ou quatre fois…), mais j’espère en avoir donné tout de même une idée raisonnable !

Deux mémoires

Hier soir, j’ai fait un truc vachement bien : j’ai vaguement mis à jour ma page personnelle. J’en ai profité pour la déplacer sur un autre serveur, pour faire un peu de nettoyage, et surtout pour y publier les deux mémoires sur lesquels j’ai travaillé depuis un peu plus d’un an.

Le premier est un « mémoire de semestre » : on a la possibilité, à l’ETH, d’obtenir des crédits pour un module « Research in Computer Science » (recherche en informatique, donc), qui est en théorie un mini-projet de recherche. Comme je sais pas faire les choses à moitié, le mémoire en question fait une petite centaine de pages et il est possible qu’il explose le record du nombre de pages sur ce genre d’exercice. À ma décharge, ledit projet consistait à reprendre deux mémoires précédents par deux étudiants et à les réintégrer dans un cadre commun avec une notation développée entre temps. Le mémoire s’intitule Understanding the PPSZ algorithm for ClSP.

Le deuxième est mon mémoire de master – i.e. le projet de 6 mois à la fin des études. Celui-ci s’intitule Towards the derandomization of the PPSZ algorithm for the multiple satisfying assignments case, et il a même un zouli DOI (et ça, c’est classe, quand même.) Les deux projets sont liés (d’ailleurs on est en train de faire rentrer des morceaux du deuxième mémoire dans le premier mémoire pour tenter de publier tout ça), au sens où ils tournent tous les deux autour d’un même thème assez spécifique, mais les deux mémoires sont assez différents. Le premier était plus un boulot de lecture / compréhension / vérification / réécriture, avec assez peu de contenu original. Le deuxième mémoire était un boulot nettement plus exploratoire, et contient par ailleurs BEAUCOUP de résultats de type « on a essayé ça, ben ça marche pas. Ou alors pas trivialement. ».

Je ne vais pas rentrer tout de suite dans les détails et les explications de ce que racontent ces deux bidules – je ferai un billet plus tard sur le sujet pour essayer d’expliquer un peu ; pour ceux que ça intéresse, ça a essentiellement à voir avec SAT et avec un algorithme qui s’appelle PPSZ (et il faudrait sans doute que j’écrive la page Wikipedia de PPSZ, ne serait-ce que parce que ça me donnerait un truc à lier ici). Mais c’est une histoire pour une autre fois 🙂

Algorithmes aléatoires et conflits sur l’échiquier

J’avais dit que je ferais un billet sur les algorithmes aléatoires, et je ne l’ai toujours pas fait. Une petite part de flemme, et une grosse part d’infusion – mais je viens enfin de trouver l’exemple raisonnablement rigolo et abordable que je cherchais depuis longtemps…

Le principe de l’algorithme aléatoire, c’est essentiellement « boah, on va faire n’importe quoi, et puis si on fait n’importe quoi suffisamment de fois, on finira bien par tomber sur une solution qui marche ». Le « n’importe quoi » consiste à faire un choix au hasard quand on ne sait pas quoi faire, et le « suffisamment de fois » dépend du « n’importe quoi ». L’idée, c’est que quand on fait les choses au hasard, on a normalement au moins une chance, même infime, de tomber sur une solution. Mais si j’ai, par exemple, une chance sur 1000 de tomber sur la solution à mon problème, si j’exécute la même procédure aléatoire indépendamment 1000 fois, j’ai de bonnes chance de tomber sur ce que je veux. Je ne peux pas garantir à 100% que je vais trouver la solution, mais… Tiens, d’ailleurs, quelle est la probabilité que ça marche en faisant 1000 essais ? C’est un petit exercice assez facile, peut-être qu’un petit rappel de probabilités peut être utile (voir la partie 1 et la partie 2)… Je vais tout de même donner la solution. La probabilité que l’algorithme retourne le bon résultat est 0.001. La probabilité qu’après 1000 fois ça ait réussi au moins une fois, c’est 1 – la probabilité que ça ait échoué toutes les fois. La probabilité que ça échoue, c’est 0.999. Comme mes exécutions sont indépendantes, je peux multiplier les probabilités de tous ces essais entre elles : ça fait 0.999^1000, c’est à dire environ 0.368. Donc, la probabilité que ça ait réussi après 1000 essais est de 0.623. Ce qui peut paraître assez faible, ça fait moins de trois chances sur quatre. Mais si je répète, non pas 1000 fois, mais 3000 fois, j’obtiens une probabilité de succès de 1 – 0.999^3000, soit environ 0.95… Et on peut continuer à répéter pour avoir la probabilité de succès que l’on souhaite obtenir, même avec des probabilités de succès initiales très faibles. Si l’algorithme lui-même n’est pas trop violent en terme de calculs, n’importe quel ordinateur est tout à fait capable d’exécuter ça en un temps correct. Et les algorithmes aléatoires peuvent souvent se permettre d’être très simples – plutôt que de calculer un machin compliqué à calculer, pouf, on met une valeur au hasard et on espère que ça marche.

Ça peut paraître un peu fou comme approche, mais comme j’ai essayé de l’expliquer dans le paragraphe qui précède, on peut en fait faire des choses tout à fait rigoureuses dans le domaine. On prend un algorithme, on calcule la probabilité qu’il renvoie une réponse correcte, de là on déduit combien de fois il faudra le répéter pour avoir une probabilité de succès qui convient, et on est content. Et de là, il y a deux grosses difficultés. La première, c’est d’arriver à un algorithme qui ne soit pas complètement débile. Si l’espace de solutions est immense et qu’on se contente de prendre un élément de l’espace de solutions au pif, ça va rarement donner des choses intéressantes. Par contre, si on arrive à avoir une approche semi-intelligente, ça permet souvent d’avoir des algorithmes qui ont une espérance de temps d’exécution meilleure que les algorithmes déterministes pour un problème donné. La deuxième grosse difficulté, c’est parfois de calculer/prouver la probabilité de succès d’un algorithme (ce qui permet d’avoir une idée du nombre de répétitions à prévoir et donc du temps d’exécution). Il y a une troisième difficulté, qui n’est pas vraiment liée directement à l’algorithme aléatoire lui-même. On ne sait pas, à l’heure qu’il est, si l’ajout d’une composante aléatoire permet toujours d’avoir des algorithmes plus performants. Il existe des gens (j’en fais partie en ce moment) qui s’intéressent à la question de savoir si un algorithme aléatoire donné peut être transformé de façon magique en un algorithme déterministe (sans hasard, donc) en gardant des performances équivalentes. Il y a aussi des gens qui s’intéressent à la question générale de savoir si l’aléatoire peut apporter quelque chose, ou si la classe des problèmes qui se résolvent « facilement » avec des éléments aléatoires est la même que celle des problèmes qui se résolvent « facilement » de façon déterministe – pour une certaine définition de facilement. (Notons d’ailleurs que les deux questions n’ont pas le même sens, en dehors de la distinction « un algorithme donné / une classe de problèmes » – dans le premier cas on cherche à avoir les mêmes performances, dans le second cas, on est plus souples et on pourrait par exemple se contenter d’un « si je peux résoudre un problème en temps t avec de l’aléatoire, je peux le résoudre en temps t³ sans aléatoire »).

Après ces longs paragraphes introductifs et vaguement philosophiques, je vais donner un exemple à partir d’un problème connu, celui des 8 reines. Ça rappellera sans doute des choses à ceux qui ont joué à The 7th Guest il y a 20 ans. On suppose qu’on dispose d’un échiquier – donc une grille 8×8 – et de 8 pions qui peuvent se déplacer comme une reine aux échecs, c’est-à-dire en ligne horizontale ou verticale, et en diagonale, d’un nombre arbitraire de cases. On dit que deux reines sont en conflit lorsqu’elles peuvent s’attaquer - c’est-à-dire qu’elles sont sur la même ligne horizontale, verticale ou diagonale. On veut placer huit reines sur l’échiquier de façon à ce qu’aucune d’elles ne soit en conflit avec aucune autre. C’est pas complètement trivial comme problème, comme on peut s’en rendre compte en essayant de le résoudre à la main (ça se fait, mais c’est pas immédiat). Là, le problème reste relativement simple, au sens où la grille reste assez petite. Faire le même genre de choses avec 50 reines sur un échiquier 50×50, il y a de bonnes chances de ne pas s’en sortir à la main. Et puis construire un algorithme déterministe pour ce machin, c’est pénible, ça backtracke dans tous les sens, et moi le backtracking ça me fout mal au crâne (c’est pas complètement vrai, il y a un algorithme récursif qui existe aussi, mais bon). Donc, voyons ce qu’on peut faire de manière aléatoire. Je vais me limiter dans certains cas aux 8 reines, parce que ça fait des nombres « gérables ». Et je vais tricher un peu, je vais dégainer la Wikipedia sur le sujet Problème des huit dames, et je vais affirmer qu’il existe 92 solutions au problème – ça me sera utile pour donner certains nombres.

On peut prendre une approche vraiment bourrine – on numérote les cases de l’échiquier de 1 à 64, on tire huit numéros de 1 à 64, et on place une reine sur chaque case. Si ça marche, ben on a fini ; si ça marche pas, on reprend les reines de l’échiquier et on recommence. La probabilité de succès de cet algorithme, elle est pas bien élevée – il y a 4 426 165 368 manières de choisir 8 chiffres parmi 64, ce qui nous donne une probabilité de succès d’environ 2.07 × 10^-8. C’est peu.

Donc, on regarde d’un peu plus près le problème, et on fait une observation facile : si je veux avoir la moindre chance qu’une solution marche, une chose est sûre, c’est que je n’aurai qu’une seule reine par colonne de mon échiquier. Donc, plutôt que de prendre huit numéros de 1 à 64, je prends, pour chaque reine, un nombre au hasard de 1 à 8, et je place la reine numéro k sur la colonne k et sur la ligne correspondant au nombre qu’on a pris au hasard. C’est sensiblement meilleur – ça correspond à 8^8 = 16 777 216 possibilités, soit une probabilité de succès qui tourne autour de 5.48 × 10^-6. C’est mieux, mais c’est toujours pas top.

On fait alors l’observation facile suivante : je n’aurai aussi qu’une seule reine par ligne de mon échiquier. Bon, d’accord, j’aurais probablement pu faire l’observation en même temps que la précédente, mais faut pas me brusquer, on est dimanche matin et j’ai mangé de la raclette hier soir. Donc, plutôt que de prendre un nombre au hasard de 1 à 8 pour chaque reine, je prends une permutation au hasard (c’est à dire que je prends un nombre de huit chiffres dans lequel tous les chiffres de 1 à 8 apparaissent), et je pose mes reines en fonction de cette permutation (la première reine sur la ligne du premier chiffre, la deuxième sur la ligne du deuxième, et ainsi de suite). Ça améliore encore un peu les choses – il y a 8! = 40 320 permutations possibles, soit une probabilité de succès de 2.28×10^-3, soit 0.02%. Ça commence à rentrer dans la catégorie des machins « faisables »… mais uniquement pour des petites valeurs de la taille de l’échiquier. Si on monte, mettons, ne serait-ce qu’à 16 reines sur un échiquier de 16×16 cases, la séquence OEIS kivabien nous permet d’évaluer la probabilité de succès à 14 772 512/16!, soit environ 7.06×10^-7. Le problème, c’est que la factorielle, là, ben elle est quand même encore très moche.

On peut encore améliorer les choses. Il y a trois sources de conflits pour les reines sur l’échiquier : les lignes horizontales, les lignes verticales, et les diagonales. L’approche par permutations permet de gérer les deux premières, mais y ajouter la gestion des diagonales commence à filer VRAIMENT mal au crâne (on se retrouve à faire des machins du genre « si j’ai deux chiffres qui sont dans des positions qui diffèrent de k, ils ne peuvent pas différer eux-mêmes de k », et c’est imbitable). Par contre, ce qu’on peut faire assez facilement, c’est générer la permutation petit à petit. L’idée, c’est qu’on place les reines une par une au hasard dans une colonne, en supprimant toutes les lignes qui sont en conflit avec une reine placée précédemment.  Donc, on place la première reine au hasard sur la première colonne, la deuxième reine au hasard, mais de façon à ce qu’elle ne soit pas sur la ligne de la première ni sur ses diagonales, et ainsi de suite. Et si on ne peut pas trouver une position sans conflit dans la colonne, c’est que la solution courante ne marche pas – on vire tout et on recommence du début. Je n’ai pas trouvé d’analyse rigoureuse de ce machin, et j’ai la flemme de la tenter moi-même (je soupçonne accessoirement ne pas être capable de m’en sortir, du moins pas dans un temps qui me permette de publier ce billet rapidement), mais il y a au moins une implémentation qui a été testée sur 1000 reines avec des temps d’exécution raisonnables (variant apparemment de 39 secondes à 25 minutes). L’idée, cela dit, c’est qu’on élimine beaucoup de permutations de l’espace de recherche : par exemple, on supprime toutes celles qui commencent par « 12 » – ce qui correspond, pour un problème de 8 reines, à 720 permutations, ce qui n’est pas énorme, mais si on multiplie ça par 14 (parce qu’on supprime aussi celles qui commencent par « 23 », « 34 »,…, « 78 », « 21 », « 32 »,…,« 87 »), à 10 080 permutations, soit déjà un quart de l’espace de recherche ! Plus l’échiquier est grand, moins ces deux premières étapes seront significatives (et plus le « un quart » va tendre vers 0, si vous me passez l’expression), mais l’idée reste la même : on réduit l’espace de recherche, et la probabilité de succès augmente.

Toutes ces approches ont la même base : on recherche une combinaison qui fonctionne dans tout l’ensemble des combinaisons, en essayant de supprimer les combinaisons qui ne marchent pas le plus tôt possible. Il existe un autre type d’approche, qui est celle de la « réparation ». L’idée, c’est que pour une permutation donnée, on n’est peut-être pas loin d’une solution qui marche. Donc, plutôt que d’explorer l’espace de combinaisons entier, on peut peut-être explorer autour d’une permutation en essayant de se rapprocher d’une solution. Une solution, par définition, c’est une combinaison qui a zéro conflit. Donc, on peut supposer qu’on se « rapproche » d’une solution quand on diminue le nombre de conflits. C’est l’idée du dernier algorithme que je vais présenter dans ce billet. On commence par prendre une permutation aléatoire qui « a une bonne tête » en utilisant l’algorithme précédent et, au lieu de s’arrêter lorsqu’on rencontre un conflit, en plaçant une reine au hasard sur l’une des cases présentant le nombre minimum de conflits de la colonne. Si elle marche, tant mieux, on a fini. Sinon, on choisit une colonne au hasard, et on compte, pour chaque case, le nombre de conflits auquel elle correspond, en ne comptant qu’un seul conflit si plusieurs reines génèrent ce conflit (parce de toute façon elles sont alors en conflit entre elles, et il faudra gérer ça). S’il existe une case pour laquelle la reine de la colonne courante aurait moins de conflits, on la déplace (en choisissant au hasard la case si plusieurs d’entre elles ont le même nombre de conflits). Et on recommence. Il n’y a en revanche pas de garantie que cet algorithme va toujours renvoyer une solution : si on n’a pas de chance, on peut se retrouver dans un minimum local, ne pouvoir améliorer le nombre de conflits sur aucune colonne, et boucler infiniment. Il faut prendre ce genre de cas en compte, et arrêter/recommencer l’algorithme si le nombre d’étapes commence à exploser vraiment trop violemment. Là, vraiment, je sais que l’analyse de cet algorithme va au-delà de ce que je veux présenter sur ce blog (j’avoue ne pas l’avoir lue, surtout qu’il manque des figures sur le papier). Le papier qui présente la méthode indique que le nombre de conflit réparés reste à peu près constant quel que soit la taille de la grille, et qu’ils arrivent à résoudre le problème sur un échiquier d’un million de cases de côté en 4 minutes. Sur une SPARCstation 1, en 1991.

Bref, les algorithmes aléatoires, c’est rigolo et utile. Je pense qu’un prochain billet discutera des algorithmes aléatoires pour SAT, parce que c’est quand même ce sur quoi je passe beaucoup de temps en ce moment. J’ai quand même passé un truc assez fondamental sous le tapis, c’est que générer des nombres aléatoires, c’est hautement non-trivial. En pratique, ce que j’ai présenté ici fonctionne aussi avec des nombres « à peu près aléatoires » tels qu’un ordinateur est capable de les générer. Mais ceci est une autre histoire, que nous vous raconterons une prochaine fois… ou pas, parce que là, j’ai pas le niveau 😛

TPA : Théorème des cinq couleurs

Aujourd’hui, c’est coloriage. Je vais déjà commencer par vous montrer un petit jeu : ça s’appelle Map, c’est tout con : le jeu vous donne une carte rectangulaire avec des zones à colorier, et quelques couleurs déjà placées ; il faut colorier les autres de façon à ce que deux voisins n’aient pas la même couleur. Allez-y, allez jouer un peu avec, c’est rigolo 😛 La question théorique derrière ce petit jeu est la suivante : quel est le nombre minimal de couleurs nécessaires pour pouvoir colorier n’importe quelle carte de ce type ? Peut-être que vous vous souvenez, mais j’ai déjà un peu parlé de problèmes de ce genre là : quand j’ai parlé de P vs NP, j’ai parlé de colorabilité de graphes, et de décider si un graphe donné pouvait être colorié avec un nombre donné de couleurs. La question du nombre minimum de couleurs peut revenir à poser la question précédente plusieurs fois : « et avec 2 couleurs, ça passe ? non ? et avec trois ? non plus ? et avec quatre ? ah, cool. » Là, normalement, j’ai deux objections dans la salle. La première, c’est : « minute papillon, tu parlais de cartes et là tu me parles de graphes, c’est pas pareil, si ? ». La deuxième, c’est « heu, dis voir, demander si un graphe peut être colorié avec trois couleurs, on a vu dans le P vs NP que c’était pas trivial comme question… donc on fait quoi, surtout qu’il y a peut-être beaucoup de nombres à considérer ? ». Il se trouve que la première objection permet de répondre à la deuxième. Pour répondre à la première objection, je vais faire un petit dessin. Voilà le petit dessin. map   Le petit dessin en question, il est fait comme suit :

  • j’ai commencé par dessiner la carte – la structure en noir ;
  • j’ai ajouté un point dans chaque zone de la carte (les points rouges) ;
  • j’ai relié deux points (avec les traits rouges) si les zones correspondantes étaient voisines.

NORMALEMENT j’ai vérifié ma construction, mais si elle est bancale (si j’ai des traits en trop ou en pas assez), merci de me le signaler 🙂 Et là, que voit-on apparaître en rouge sous nos yeux ébahis ? Un graphe. Pas n’importe quel graphe, d’ailleurs : il s’agit d’un graphe planaire, c’est à dire qu’on peut le dessiner sur une feuille de papier sans que deux arcs se croisent. J’ai parlé de graphes planaires dans un billet de blog précédent : Planarité, mineurs et donuts. Là, il va falloir que vous me fassiez confiance sur quelques points. Primo, la construction ci-dessus est toujours possible. Deuzio, ça donne toujours un graphe planaire. Tertio, ce graphe planaire est unique pour une carte donnée : il a toujours le même nombre de sommets, et les arcs sont toujours les mêmes. Et c’est là qu’on répond à la deuxième objection. Oui, dans le cas général, décider si un graphe peut être colorié avec 3, 4, 5 ou 12 couleurs est difficile (au stade actuel de nos connaissances). Par contre, dans le cas particulier des graphes planaires, on sait plus de choses. Plus précisément, on connaît le théorème suivant, appelé « théorème des 4 couleurs » :

Tout graphe planaire peut être colorié avec au maximum 4 couleurs.

Et, comme corollaire au point précédent, je peux colorier une carte avec quatre couleurs (il suffit de faire ma petite construction là-haut, de colorier le graphe résultant, et de colorier chaque zone avec la couleur du sommet en question – rappelons que dans le cas qui nous intéresse, un coloriage est valide si deux sommets reliés par un arc n’ont pas la même couleur.) Un petit aparté: je parle de « cartes » ici, mais c’est pas vraiment applicable directement « dans la vraie vie ». Le problème principal, c’est que certains pays ne sont pas « connectés ». Et si on colorie une carte du monde, on s’attend à ce que, par exemple, la Guyane soit de la même couleur que la France. Ce qui ajoute des arcs entre la zone « France » et les zones « Brésil » et « Suriname », et qui risque fort de nuire à la planarité du graphe correspondant. Fin de l’aparté, revenons à nos moutons. Donc, si j’ai un graphe planaire, je peux répondre directement à la question « est-ce que ce graphe peut être colorié avec k couleurs » par « oui », dès que k est supérieur ou égal à 4. La preuve du théorème des quatre couleurs est… compliquée. J’en parlerai dans un prochain billet, mais il faut que je me documente encore un peu avant sur le sujet 🙂

Un résultat intéressant est que pour trois couleurs, même dans le cas « graphe planaire », le problème reste NP-complet ! (C’est un résultat de Stockmeyer en 1973, dans un article intitulé « Planar 3-colorability is polynomial complete », qui 40 ans plus tard est toujours planqué en tant qu’article payant à l’ACM. Mais je digresse.) Donc, pour 3 couleurs, c’est dur à décider, pour 4 couleurs, on sait qu’on peut le faire, mais c’est dur à prouver. En revanche, le théorème suivant est relativement simple à prouver :

Tout graphe planaire peut être colorié avec au maximum 5 couleurs.

J’ai dit que c’était relativement simple à prouver, et donc je vais le faire. Ça va quand même être probablement un peu long, donc accrochez-vous, et je vais essayer de faire preuve de pédagogie 🙂

J’ai besoin d’un premier résultat pour ma preuve :

Tout graphe planaire contient au moins un sommet de degré au plus 5.

Le degré d’un sommet, c’est le nombre d’arcs qui y sont connectés. Comme un arc connecte exactement deux sommets, si je fais la somme des degrés de tous les sommets, j’obtiens deux fois le nombre d’arcs (parce qu’un arc donné est compté exactement deux fois, une fois à chacune de ses extrémités). Maintenant, je vais vous demander d’admettre encore un truc, c’est qu’on connaît le nombre maximal d’arcs d’un graphe planaire, en fonction de son nombre de sommets, n : il y a au plus 3n-6 arcs, dès qu’il y a plus de deux sommets (comme on me l’a fait remarquer en commentaire). Je pourrais aussi le démontrer, mais ça implique encore un résultat intermédiaire, et ce billet va finir par être vraiment beaucoup trop long (dit-elle au bout de deux pages). Comme j’ai au plus 3n-6 arcs, la somme des degrés de tous les arcs d’un graphe planaire est inférieure ou égale à 6n-12. Par conséquent, il y a au moins un sommet de degré au plus 5 : si ce n’est pas le cas, la somme des degrés de tous les arcs est supérieure ou égale à 6n (6 arcs, multipliés par n sommets), et ce n’est pas possible pour un graphe planaire.

Revenons à nos moutons initiaux : on veut maintenant démontrer que tout graphe planaire peut être colorié avec au maximum 5 couleurs. On va faire un raisonnement par récurrence sur le nombre de sommets du graphe. J’ai interrompu la rédaction de ce billet pour aller faire un billet sur le raisonnement par récurrence, allez le lire si le terme ne vous est pas ou plus clair 🙂 Donc, on commence par traiter l’hypothèse de base. Si mon graphe n’a qu’un seul sommet, je peux le colorier avec au maximum 5 couleurs : j’en choisis une pour mon sommet, et voilà.

Maintenant, je suppose que l’hypothèse de récurrence suivante est vraie : tout graphe planaire à (n-1) sommets peut être colorié avec au maximum 5 couleurs. Le but du jeu est maintenant de déduire, à partir de ça, que tout graphe planaire à n sommets peut être colorié avec au maximum 5 couleurs. Pour ça, on considère un graphe à n sommets, n’importe lequel (si je démontre que ça marche pour n’importe quel graphe, je montre que ça marche pour tous les graphes). Par le résultat intermédiaire que j’ai prouvé au-dessus, le graphe à n sommets étant planaire, il a un sommet de degré inférieur ou égal à 5, qu’on va appeler v dans la suite pour aller vite. On peut prendre v, et le retirer temporairement (on retire aussi tous les arcs qui y sont connectés). Le graphe sans v est un graphe planaire à (n-1) sommets. Donc, on peut le colorier avec au maximum 5 couleurs. Maintenant, on remet v (et les arcs qu’on a retirés aussi) : on obtient un graphe à n sommets dont (n-1) sommets sont coloriés avec au maximum 5 couleurs, et un sommet, v, n’a pas encore de couleur.

Là, on a deux possibilités. Si les voisins de v (les sommets auxquels v est connecté) utilisent moins de 5 couleurs, on peut utiliser une des couleurs non utilisées pour colorier v, et le graphe initial est colorié avec au maximum cinq couleurs.

Sinon, si on prend une représentation plane du graphe planaire, on est dans la situation suivante :

fiveneighbors Je n’ai dessiné ici que six sommets, v et ses cinq voisins. Le graphe peut contenir plus de sommets et plus d’arcs, mais il contient forcément cette structure.

Sur ma représentation plane, je choisis arbitrairement un voisin de v, je l’appelle v1, et je numérote les autres dans le sens des aiguilles d’une montre (v2 à v5). Les sommets v1 à v5 sont de cinq couleurs différentes. Je vais maintenant prouver que je peux soit colorier v1 et v3 de la même couleur, soit colorier v2 et v4 de la même couleur.

Je regarde d’abord v1 et v3, et plus précisément je m’intéresse au sous-graphe colorié avec les couleurs de v1 et de v3, c’est-à-dire que je prends tous les sommets de la couleur de v1 (gris, sur la figure) et tous les sommets de la couleur de v3 (bleu, sur la figure), et tous les arcs du graphe initial qui relient ces sommets entre eux. Un exemple sur un petit graphe :

subgraphÀ droite, j’ai représenté le sous-graphe du graphe de gauche en ne prenant que les sommets bleus et gris (j’ai supprimé les sommets rouges et violets, et les arcs qui y étaient attachés). Sur ma figure, j’ai quatre sommets entre lesquels j’ai des chemins (une suite d’arcs qui me permettent de passer de l’un à l’autre), et un sommet qui n’a de chemin vers aucun autre.

Si je ne regarde que les sous-graphe bleu/gris de mon graphe initial, j’ai deux possibilités : soit v1 et v3 ne sont plus connectés, soit ils le sont encore ; et par « connectés », j’entends qu’il y a un chemin entre v1 et v3. S’il n’y a pas de chemin, v1 et v3 sont dans des composants différents (un composant, c’est un ensemble de sommets qui sont reliés les uns aux autres par des chemins). Dans ce cas là, je regarde le composant qui contient v3 (il se peut que ce soit v3 tout seul, mais sinon c’est un ensemble de sommets bleus et gris qui ont tous un chemin connecté à v3). Dans ce composant-là, le fait que les sommets soient bleus ou gris est arbitraire : je peux inverser les deux couleurs sans que ça ait un impact sur le fait que le graphe est correctement colorié. Si je ne fais ça que dans ce composant là, v1 (qui n’est pas dans composant en question) reste gris, et v3 (qui était bleu) devient gris. Maintenant, je peux remettre le « reste » du graphe (les sommets rouges, violets, verts, et le sommet v qui n’est pas encore colorié) : comme je n’ajoute pas de sommets bleus ou gris, le coloriage reste valide. Et j’ai « libéré » le bleu pour mon sommet v : donc, je peux colorier v en bleu. La vie est belle.

Enfin, presque. Parce qu’il se peut aussi que v1 et v3 soient dans le même composant « bleu/gris ». Donc là, si j’inverse le bleu et le gris, ben je me retrouve avec v1 en bleu, et v3 en gris, et je peux toujours rien faire avec v. Donc, il faut trouver une autre astuce.

Je regarde maintenant le sous-graphe composé des sommets coloriés avec les couleurs de v2 (rouge) et v4 (vert) : je retire tout ce qui n’est pas rouge ou vert du graphe, et je regarde ce qui reste. Si v2 et v4 ne sont pas dans le même composant rouge/vert, je peux inverser le rouge et le vert dans le composant de v4. v2 reste rouge, v4 devient rouge, et je peux colorier v en vert. Là, j’ai peut-être quelqu’un qui me dit « oui, mais si v2 et v4 sont aussi dans le même composant, tu as le même raisonnement et donc le même problème que pour le composant bleu/gris ! ». Oui, mais. Regardons un peu ce qui se passe sur la figure. Comme v1 et v3 sont dans le même composant bleu/gris (sinon, on n’aurait pas besoin de regarder ce qui se passe dans le composant rouge/vert), il y a un chemin entre v1 et v3 composé de sommets bleus et gris.

bluegreypath

Si je veux faire un chemin composé de sommets verts et rouges entre v2 et v4 (ce qui est la condition pour que v2 et v4 soient dans le même composant rouge/vert), ça bloque : soit je dois croiser l’arc entre v et v3, soit je dois croiser un des arcs du chemin bleu/gris entre v1 et v3. Et comme mon graphe est plan… ben j’ai pas de croisement entre mes arcs. Donc, v2 et v4 ne peuvent pas être dans le même composant, donc je peux bien inverser les couleurs du composant de v4, et colorier v en vert.

J’ai donc regardé tous les cas possibles pour mon graphe, et dans tous les cas, je peux colorier le sommet v de façon valide. Donc, si un graphe avec (n-1) sommets peut être colorié avec au maximum 5 couleurs, alors un graphe avec n sommets peut être colorié avec au maximum 5 couleurs, ce qui termine mon étape de récurrence et donc la preuve du théorème des cinq couleurs. Ouf.

J’avais initialement prévu de parler du théorème des 4 couleurs plus en détail dans ce billet, mais je crois que je vais arrêter là pour ne pas fumer plus de neurones de mon lectorat (en espérant qu’il y en ait quand même une partie qui soit arrivée jusqu’ici…). Comme d’habitude, pour les questions, typos, remarques, etc., c’est dans les commentaires que ça se passe 🙂

Raisonnement par récurrence et raisonnement par l’absurde

Pouf pouf, un petit billet vite fait pour pouvoir y faire référence plus tard – je suis en train de faire un autre monstro-billet, et je me suis rendue compte que j’avais besoin des éléments suivants pour faire un truc qui tienne à peu près debout, donc hop, un deuxième billet ! Je vais causer ici de deux outils fondamentaux dans la « boîte à outils » nécessaire pour prouver des trucs : le raisonnement par récurrence et le raisonnement par l’absurde. Ce sont des outils qui sont présentés et utilisés au moins au lycée (du moins de mon temps 😉 ), et qu’on retrouve à longueur de temps, partout, donc autant expliquer comment ça marche.

Le raisonnement par récurrence

Le principe du raisonnement par récurrence, c’est les dominos. Pas ceux avec des points, ceux qu’on fait tomber. On sait que si on fait tomber un domino, le suivant tombe aussi. Et on fait tomber le premier domino. Du coup, tous les dominos tombent.

Pour le raisonnement par récurrence, c’est pareil. On a une propriété qui dépend d’un entier, et on veut prouver qu’elle est vraie pour n’importe quel entier. Donc ce qu’on fait, c’est qu’on commence par montrer qu’elle est vraie pour un petit entier, genre 0, 1, 2 (parfois les cas pour 0 ou 1 n’ont pas beaucoup de sens). Et on montre que si elle est vraie pour un entier k, alors elle est vraie aussi pour un entier k+1. Du coup, on a le « départ » des dominos (« c’est vrai pour 0 ») et le « chaînage » des dominos (« si c’est vrai pour n, c’est vrai pour n+1 ») ; et si ces deux conditions là sont vraies, alors tous les dominos se cassent la gueule (« la propriété est vraie pour tous les entiers à partir de celui qu’on utilise comme cas de base »).

Là, je suis un peu embêtée, parce que j’ai un exemple qui marche, mais la preuve est assez nulle par rapport à une autre, qui est plus jolie. Donc je vais montrer les deux 🙂 Je veux démontrer que la somme des entiers de 1 à n est égale à n(n+1)/2, pour n’importe quel n. Je commence par n = 1 : j’ai bien 1(1+1)/2 = 2/2 = 1, donc l’hypothèse de base est vraie.

Ensuite, je suppose que c’est vrai pour un entier k, c’est à dire que la somme des entiers de 1 à k est égale à k(k+1)/2. Maintenant, je calcule la somme des entiers de 1 à k+1 : 1 + 2 + 3 + … + (k-1) + k + (k+1), c’est égal à la somme des entiers de 1 à k, plus (k+1). Par hypothèse de récurrence, c’est égal à k(k+1)/2 + k+1 = (k² + k + 2k + 2)/2 = (k² + 3k + 2)/2. Pour que ma preuve fonctionne, je veux que la somme des entiers de 1 à k+1 soit égale à (k+1)(k+2)/2. Et il se trouve que (k+1)(k+2), ça fait précisément k² + 3k + 2.

J’ai donc mon hypothèse de base, mon étape de récurrence, et j’ai prouvé ce que je voulais prouver.

Pour le fun, la preuve que je préfère sur cette égalité, c’est celle-ci : on considère un tableau à deux lignes et n colonnes :

1  2   3   4 ... n-2 n-1 n
n n-1 n-2 n-3     3   2  1

et on additionne les deux lignes. Chaque colonne vaut n+1 : c’est le cas pour la première colonne, et à chaque étape, on ajoute 1 à la première ligne et on enlève 1 à la deuxième ligne, donc la somme reste identique. (Quelque part, ici, je fais du raisonnement par récurrence sans vraiment le dire !). J’ai n colonnes, donc si j’additionne tous ces résultats, ça fait n(n+1). Et si je fais la somme dans l’autre sens, la première ligne est égale à la somme des entiers de 1 à n, la deuxième ligne aussi… donc mon n(n+1), c’est aussi deux fois la somme des entiers de 1 à n.

Le raisonnement par l’absurde

Le raisonnement par l’absurde est parfois un peu dangereux/délicat, parce qu’il est parfois utilisé à tort et à travers. Admettons que je veuille prouver qu’une phrase A est vraie. L’idée du raisonnement par l’absurde, c’est de partir du principe que A est faux, de dérouler une suite d’arguments impliqués par le fait que A est faux, et d’arriver à quelque chose qu’on sait être impossible. Si le fait que A est faux implique que quelque chose d’impossible arrive, c’est que A est vrai, sinon l’univers se casse la gueule et ça fait désordre.

Mon exemple préféré, c’est de démontrer que \sqrt 2 est irrationnel, c’est à dire qu’on ne peut pas l’écrire sous la forme \frac p q avec p et q des nombres entiers.

J’ai besoin d’une toute petite preuve au passage : j’affirme que si un entier n est pair, alors son carré n² est pair, et que si n² est pair, alors n est pair. Si n est pair, je peux l’écrire comme n = 2k, et donc n² = 4k² = 2 × 2k², donc n² est pair. Si n est impair, je peux l’écrire comme n = 2k+1, donc n² = 4k² + 2k + 1 = 2(2k² + k) + 1, donc n² est impair. Donc si n² est pair, il ne peut pas arriver que n soit impair (parce que sinon n² serait impair aussi), donc n est pair.

Donc, supposons qu’on puisse écrire \sqrt 2 = \frac p q. On peut aussi supposer que la fraction est irréductible, parce que si elle ne l’est pas, on peut la réduire pour qu’elle le soit (rappel : une fraction \frac p q est irréductible s’il n’existe pas d’entier k tel que p et q soient tous les deux divisibles par k. Si k existe, on divise p et q par k, et on considère la fraction \frac{p/k}{q/k}). Donc, je peux écrire que \sqrt 2 \times q = p. Je mets tout au carré, et j’obtiens que 2q² = p². Comme q est entier, q² est entier aussi, et donc p² est pair (puisque c’est deux fois un nombre entier). Mais si p² est pair, alors p est pair aussi, d’après ma preuve auxiliaire. Donc je peux écrire p = 2k, et donc p² = 4k². Mais du coup, comme 2q² = p² = 4k², je peux écrire que q² = 2k², et donc q² est pair, et q est pair. Mais ça, c’est pas possible : la fraction \frac p q est irréductible, donc p et q ne peuvent pas être pairs en même temps ! Donc, j’ai un truc qui ne va pas dans mon raisonnement, et ce truc, c’est mon hypothèse initiale, c’est-à-dire que \sqrt 2 est rationnel. Donc, \sqrt 2 est irrationnel. Joli, non ?