Résolution d`un puzzle

publicité
Optimisation 2 : projet Eternity II
Dimitri Watel
Semestre 5, 2016
Déroulement du projet
Ce projet consiste en l’étude d’un problème via toutes les techniques de recherche opérationnelle que l’on vous
a enseigné jusqu’à présent ou que vous connaissez. Il propose des étapes d’étude de l’aspect théorique ou appliqué.
Il s’effectue en binôme. Vous devez rendre un code, un rapport et effectuer une soutenance. Le code et le
rapport doivent être rendus en temps et la soutenance doit être effectuée sans quoi la note que le binôme se verra
attribuée sera 0. En cas d’absence d’un des deux membres à la soutenance, la personne présente se verra le droit de
soutenir seule. Si l’absence est justifiée, votre chargé de projet peut s’arranger pour faire soutenir l’autre personne
un autre jour. Sinon elle se verra seule attribuée la note de 0.
Le rapport
Vous devez rendre le rapport dans une archive rapport.tar.gz. Le rapport en lui même doit être rédigé dans
un fichier rapport.pdf. Si vous le souhaitez, vous pouvez ajouter des documents annexes dans un dossier Annexe,
mais ces documents ne seront pas nécessairement relus par votre chargé de projet.
Le fichier rapport.pdf doit avoir une vingtaine de pages maximum (images comprises). Il n’y a pas de minimum
de page autre que celui que votre conscience vous impose. Bref, le rapport ne doit être ni insuffisant ni du remplissage
inutile. Petit rappel : un rapport comporte une introduction rappelant rapidement le sujet et présentant rapidement
la contribution, une conclusion, une page de garde, un sommaire, ...
Les annexes servent à mettre les preuves trop longues, les tableaux trop grands, ... Dans tous les cas, le rapport
doit se suffire à lui même. Par exemple, si vous décidez de placer une preuve en annexe, il peut être bon de laisser
une idée de la preuve dans le rapport.
Le rapport sera noté sur le fond et la forme. Le fond regroupe la justesse des preuves, la validité/pertinence des
modèles de programmation linéaire et des algorithmes (pourquoi pensez vous que ce sont de bons modèles/algorithmes)
et, enfin, la pertinence des évaluations numériques que vous effectuerez. La forme regroupe votre qualité à synthétiser,
la qualité des images et des exemples, la clarté des propos, les fautes d’orthographe, ...
Le code
Vous devez rendre le code dans une archive nommée code.tar.gz. Ce code doit être commenté et il doit
pouvoir se compiler sans erreur ni warning à l’aide d’un Makefile qui vous est donné. Il devra pouvoir fonctionner
uniquement avec les fichiers présent dans l’archive à l’exception de possibles fichiers d’entrée et de sortie. Si une de
ces conditions n’est pas respectée, la moyenne ne peut être attribuée à ce projet : il vaut mieux ne rien rendre que
rendre un code qui ne compile pas ! Testez votre compilation régulièrement, et faites des sauvegardes au fur et à
mesure. Vous utiliserez le langage C.
Vous ne devez pas créer l’archive code.tar.gz vous-même. Une commande vous est fournie pour ce faire.
Tout est expliqué dans la partie suivante.
Organisation du code ; l’archive opti2.tar.gz
Vous devez télécharger et décompresser l’archive opti2.tar.gz créant ainsi normalement un dossier opti2 qui
contient tous les fichiers nécessaires au démarrage du projet.
Cette archive contient de nombreux fichiers. Ceux qui vous intéressent sont :
— Le fichier algorithm 0.c contient un modèle de code d’algorithmes.
— Les fichiers algorithm exemple.c et algorithm lpexemple.c contiennent des exemples d’algorithmes.
— Un fichier Makefile, déjà rédigé, pour compiler votre code.
1
— Un dossier Benchmark contenant des fichiers .input, chacun représentant une instance du problème (voir la
section Fichier d’entrée et de sortie des codes, page 4 ).
— Une bibliothèque proglin helper(.c,.h) pour créer et exécuter facilement un programme linéaire.
— Un dossier customlibs où vous pourrez mettre des fichiers .c et .h indirectement liés au projet
— Un dossier others où vous pourrez mettre tout autre fichier (par exemple, des fichiers pour générer des
tests).
Deux exemples vous sont donnés dans les fichiers algorithm exemple.c et algorithm lpexemple.c. Le second
vous montre comment utiliser un programme linéaire. Vous pouvez tester l’archive avec les commandes suivantes
$ make −j 4
$ . / a l g o r i t h m e x e m p l e exemple . i n p u t exemple . output
$ . / a l g o r i t h m l p e x e m p l e exemple . i n p u t exemple . output
Ce premier make est long. C’est normal, il ne devrait plus l’être par la suite.
Tous les algorithmes que vous coderez seront placés dans le dossier racine opti2, sous le nom algorithm S.c
et le fichier compilé se nommera algorithm S où S est le nom de votre algorithme. Votre fichier algorithm S.c
doit respecter quelques contraintes afin de faciliter votre travail et la correction du chargé de projet. Pour créer un
nouvel algorithme, vous êtes invités à suivre la procédure indiquée dans la partie Coder un nouvel algorithme,
page 4. Votre code doit posséder au moins 2 algorithmes :
algorithm exact1.c et algorithm pl1.c correspondant à la partie 2 de ce sujet.
Pour compiler et exécuter votre algorithme, vous devez effectuer les commandes suivantes où S est le nom de
votre algorithme et fichier.input fichier.output sont deux noms de fichiers.
$ make
$ . / a l g o r i t h m S f i c h i e r . i n p u t f i c h i e r . output
Vous aurez peut-être besoin de fichiers .c et .h pour factoriser votre code, par exemple une bibliothèque décrivant
les dictionnaires ou les listes chainées. Vous devez placer tous ces fichiers dans le dossier customlibs. En utilisant
la commande make, ils seront automatiquement compris dans la chaı̂ne de compilation, quelque soit l’algorithme
qui en a besoin. Il est donc, normalement inutile de toucher au fichier Makefile. Vous pouvez le constater avec la
bibliothèque example déjà présente dans le dossier. Si toutefois vous êtes contraint de modifier le makefile, prévenez
votre chargé de projet.
Tout autre fichier que vous créez et que vous jugez utile de mettre dans l’archive code.tar.gz que vous rendrez
peut être placé dans le dossier others. Par exemple, si vous générez des tests, si vous créez un fichier contenant le
résultat des tests, ...
Certains de vos algorithmes utiliseront une bibliothèque de programmation linéaire : deux bibliothèques de
programmation linéaire vous sont proposées. Par défaut, en tapant la commande make pour compiler, vous utilisez
la bibliothèque SCIP. Vous pouvez, si vous le souhaitez utiliser la bibliothèqe LPSOLVE si SCIP ne fonctionne pas
sur votre machine avec la commande make LPS=TRUE. Une interface proglin helper.h est prévue pour que vous
n’ayez pas à changer votre code si vous souhaitez tester la première ou la seconde bibliothèque.
Vous ne devez pas créer l’archive code.tar.gz vous-même. Une commande vous est fournie pour ce faire.
$ make a r c h i v e
Cette commande ajoute l’archive code.tar.gz à la racine de opti2. Cette archive contient tous vos algorithmes
et le contenu de vos dossiers customlibs et others. Vérifiez là avant d’envoyer votre code.
Notation du code
Votre code sera noté en deux parties.
En premier lieu, chacun de vos fichiers algorithmes sera relu puis testé et évalué sur un ensemble d’instances. Ces
instances ne sont pas exactement celles dont vous disposez dans l’archive opti2.tar.gz, mais seront générées de
la même façon. Ces tests permettront uniquement de vérifier si les algorithmes répondent correctement aux entrées
par rapport à ce qui est indiqué dans le rapport.
Ensuite, vos deux algorithmes algorithm exact1.c et algorithm pl1.c seront comparés à ceux des autres
binômes sur 10 instances de tailles au plus 15x15. Pour chaque instance, chaque algorithme sera exécuté 100 fois,
puis le temps de calcul moyen est mesuré. Deux classements sont effectués pour chaque instance, un par algorithme.
Un algorithme qui ne renverrait pas une solution en 2 minutes sur une des instances est retiré du classement de
cette instance. Une première place rapporte 0.125 point, la seconde place 0.10, la troisième place 0.075, la quatrième
0.05, et la cinquième 0.025. Les autres places ne rapportent pas. Vous pouvez donc gagner jusqu’à 2.5 points en
étant premier partout. La somme des points est arrondie au demi-point supérieur. Ces points sont bonus.
2
La soutenance
Vous devez enfin effectuer une soutenance lors de la dernière séance. Sa durée est de 15 minutes + 5 minutes
de questions. La soutenance n’a pas vocation à répéter oralement la totalité du rapport mais à évaluer vos qualités
de présentation. Lors des 15 minutes de présentation, vous devez : présenter le sujet et vos résultats de la partie
Développement. Vous pouvez également, à vos risques et périls, faire une démonstration de votre code pendant ces
15 minutes. Sachez toutefois que, présentation terminée ou non, à l’issue des 15 minutes, vous serez interrompus.
Remarque : vous êtes deux sur le travail, cependant, un découpage artificiel de la présentation est inutile. Il vaut
mieux qu’une seule personne présente le travail si vous avez travaillé ensemble du début à la fin. Les deux pourront
répondre aux questions.
L’archive à renvoyer
Les archives rapport.tar.gz et code.tar.gz doivent être toutes les deux insérées dans une archive nommée
opti2 binome N.tar.gz où N est votre numéro de binôme attribué à la première séance. Cette archive sera rendue
seule sur exam.ensiie.fr.
Dates importantes
— Soutenance : vendredi 13 janvier, de 14h à 17h45. Toute absence à votre soutenance entraı̂nera la note de 0.
— Rapport et code à rendre au plus tard le dimanche 15 janvier à 23h59. Tout rapport/code non rendu entraı̂nera
la note de 0.
3
1
Problème étudié
On étudie dans ce projet le problème de résoudre un puzzle.
On dispose d’un ensemble de n × m pièces carrées de même taille. On souhaite les placer dans un rectangle
ayant n pièces par ligne et m par colonne. Les pièces ont des bords droits, si bien qu’il est physiquement possible
de placer chaque pièce à côté de n’importe quelle autre.
Sur les bords de chaque pièces sont peintes des couleurs, distinctes ou non. On ne peut placer une pièce à côté
d’une autre que si les couleurs des bords qui se touchent sont les mêmes.
Dans cette version, il n’est pas possible de tourner les pièces.
Un exemple de puzzle est donné en Figure 1.
1
3
3
2
2
3
2
3
1
1
3
2
2
1
1
1
3
1
1
2
2
1
2
3
1
3
1
2
3
2
3
1
Figure 1
1.1
Coder un nouvel algorithme
Pour créer un nouvel algorithme dont le nom est S, vous devez copier le fichier algorithm 0.c en algorithm S.c.
Vous pouvez ensuite remplir deux fonctions :
— void run(int width, int height, int** pieces, int** puzzle) : le coeur de votre algorithme, qui
remet les pièces du puzzle dans le bon sens. width et height indiquent la largeur et hauteur du puzzle.
pieces contient l’ensemble des pièces mélangées, il s’agit d’un tableau de height · width lignes et 5 colonnes.
Chaque ligne décrit l’identifiant et les 4 couleurs d’une pièce par un entier dans cet ordre : haut, gauche, bas
et droite. La première pièce a pour identifiant 0, la suivante 1, ... la dernière (height · width - 1). Enfin,
puzzle est un tableau de height lignes et width colonnes et est chargé de recueillir la sortie. Vous devez,
avant la fin de la fonction, mettre dans puzzle[i][j] l’identifiant de la pièce que vous placeriez sur la ligne
i et la colonne j du puzzle. Dans l’exemple de la Figure 1, width et height vaudraient 2. pieces serait un
tableau contenant dans cet ordre la pièce en haut à gauche, celle en haut à droite, celle en bas à gauche puis
la dernière en bas à droite. Il faudrait mettre dans puzzle[0][0] la valeur 1, dans puzzle[0][1] la valeur 2, dans
puzzle[1][0] la valeur 3 et dans puzzle[1][1] la valeur 0.
— int post process(double cpu time, int width, int height, int** pieces, int** puzzle) où vous
pouvez afficher des informations dans une fichier ou dans la console. Cette fonction est appelée après run.
Le paramètre cpu time est le temps de calcul de cette dernière fonction. La valeur des autres est celle des
mêmes paramètres à la sortie de la fonction run.
Attention ! Il ne faut pas libérer la mémoire des pointeurs pieces et puzzle dans ces fonctions.
1.2
Fichiers d’entrée et de sortie des codes
Votre algorithme prendra en entrée le nom d’un fichier d’entrée .input (variable input file) et le nom d’un
fichier de sortie .output (variable output file). Vous n’aurez pas à manipuler ces fichiers directement, le fichier
main.c s’en charge pour vous. Toutefois savoir comment sont organisés ces fichiers peut vous intéresser, entre autres
si vous voulez générer vos propres entrées.
Ces deux types de fichiers commencent pas une ligne ### Commentaire, suivie par une ligne vide. Le commentaire
permet d’expliquer par exemple comment le fichier est généré. Il n’est pas possible de le faire sur plus d’une ligne.
Dans les fichiers d’entrée (fichiers .input) vous sont données 3 informations : les tailles n et m du puzzle et
une liste de n × m pièces dans un ordre arbitraire, chacune décrite par ses 4 couleurs, séparées d’une virgule. Les
4
couleurs sont dans cet ordre : haut, gauche, bas, droite. Un identifiant est implicitement donné à chaque pièce : leur
numéro dans l’ordre dans lequel elles vous sont données. La première pièce a pour identifiant 0, la suivante 1, ... la
dernière le nombre de pièces moins 1.
Dans le fichier de sortie (fichiers .output) sont écrites sur n × m lignes les pièces du puzzle. Elles sont écrites
dans cet ordre : de gauche à droite les pièces de la ligne la plus haute du puzzle, puis la seconde ligne de gauche
à droite, puis la troisième . . . jusqu’à la ligne du bas. Une pièce est décrite par son identifiant et ses 4 couleurs,
séparés d’une virgule. Les couleurs sont dans cet ordre : haut, gauche, bas, droite.
Dans l’exemple de la Figure 1, le fichier example.input et le fichier example.output correspondant seraient
Listing 1 – .input
Listing 2 – .output
### Exemple d ’ e n t r e e
### Exemple de s o r t i e
2 ,2
1 ,3 ,3 ,2
3 ,1 ,1 ,2
3 ,2 ,1 ,2
1 ,2 ,1 ,3
2
1 ,3 ,1 ,1 ,2
2 ,3 ,2 ,1 ,2
3 ,1 ,2 ,1 ,3
0 ,1 ,3 ,3 ,2
Étude commune
2.1
Modélisation
Décrivez ce problème de manière formelle. On attend de vous que vous l’écriviez sous forme d’un problème de
décision en détaillant l’instance et la sortie. On rappelle qu’il n’est pas possible de tourner les pièces. On
nommera ce problème (PZL).
Votre modèle doit être validé par votre chargé de TD avant de continuer.
2.2
Étude de la complexité
On souhaite prouver que (PZL) est NP-Complet.
1. Expliquez pourquoi ce problème est dans la classe NP.
2. Pour prouver la NP-Complétude, on va effectuer une réduction de puis le problème de 3-partition. Vous
pouvez décider de l’effectuer depuis un autre problème si vous le souhaitez, mais vous ne serez pas guidés.
Le problème de 3 partitions consiste à savoir si un ensemble X de 3p entiers positifs peut être partitionné
en p triplets de mêmes sommes. Soit p · B la somme totale des éléments de X. On suppose tous les entiers
de X strictement compris entre B/2 et B/4. Ce problème est NP-Complet au sens fort.
Voici des questions qui peuvent vous guider vers la réduction
(a) Montrer que la somme de chaque triplet dans une solution de 3 partition est B. Montrer qu’il n’est pas
possible de trouver 2 éléments ou moins de X dont la somme fait B. De même pour 4 éléments ou plus.
(b) Comment forcer certaines pièces du puzzle à être posées sur un bord du puzzle ?
À l’aide de cette technique, on va fabriquer un puzzle avec p + 2 lignes et B + 2 colonnes tel que, quelque
soit la solution proposée, les 2p + 2B + 4 pièces de bords sont toujours les mêmes.
(c) Soit i un entier. Comment forcer i pièces du puzzle à être côte à côte en ligne ? Pour chaque entier i de X,
on va créer i pièces de la sorte. Soient pi ces pièces. On considère maintenant que pi est une super-pièce
de hauteur 1 et de largeur i.
(d) Faire en sorte qu’on puisse placer la super-pièce pi à côté d’une pièce de bord (gauche, droite, bas et
haut), pour tout i ∈ X. Faire en sorte qu’on puisse placer pi à côté de pj , pour tout i, j ∈ X.
(e) Montrer qu’on ne peut placer plus de 3 super-pièces côtes à côtes. Montrer ensuite que si, sur une ligne,
on ne place que 2 pièces pi et pj côtes à côtes, il sera impossible de remplir le puzzle.
(f) Montrer enfin que le puzzle ne peut être terminé si et seulement si on peut découper X en p triplets de
même somme.
Votre preuve n’est pas obligée de suivre ces indications. Dans tous les cas, votre preuve doit figurer dans le rapport
sous une forme agréable à lire (dessins, découpage en lemme, théorème, ...). Vous avez le droit de demander de
l’aide pour la comprendre et la terminer.
5
2.3
Algorithme exact
Proposez un algorithme exact pour résoudre une instance de (PZL). Votre algorithme doit être constructif. Ce
qui signifie que si le puzzle est réalisable, votre algorithme doit déterminer une manière d’agencer les pièces du
puzzle. Cet algorithme ne doit pas utiliser la programmation linéaire.
Vous décrirez l’algorithme dans le rapport, ainsi que sa complexité. Une preuve de l’exactitude de l’algorithme
est un plus.
Vous coderez ensuite cet algorithme dans un fichier algorithm exact1.c (voir la section Organisation du
code : l’archive opti2.tar.gz page 1). Cet algorithme sera comparé aux algorithmes exacts des autres binômes
(voir la section Notation du code page 2).
2.4
Programmation linéaire
Proposez un programme linéaire pour modéliser une instance de (PZL). Votre programme doit être constructif.
Ce qui signifie que si le puzzle est réalisable, votre programme doit déterminer une manière d’agencer les pièces du
puzzle. Il peut être intéressant d’écrire le problème sous forme d’un problème d’optimisation pour guider le modèle
vers une solution. Vous pouvez également voir le problème comme la résolution d’un problème de stable dans un
graphe.
Vous décrirez le programme dans le rapport, ainsi que sa taille. Une preuve de la validité du modèle est un plus.
N’hésitez pas à penser à des inégalités valides si vous en connaissez.
Vous coderez ensuite un algorithme construisant ce modèle, l’exécutant et déduisant une solution si elle existe
dans un fichier algorithm pl1.c (voir la section Organisation du code : l’archive opti2.tar.gz page 1). Cet
algorithme sera comparé aux programmes linéaires des autres binômes (voir la section Notation du code page 2).
Pour manipuler un programme linéaire, vous êtes invités à utiliser la bibliothèque proglin helper(.c,.h) de
l’archive opti2.tar.gz. Un exemple d’utilisation de programme linéaire vous est donné dans le fichier algorithm lpexample.c.
2.5
Évaluation des performances
Proposez une évaluation de vos algorithmes algorithm exact1 et algorithm pl1. Vous êtes invités à utiliser
les instances du dossier Benchmark de l’archive opti2.tar.gz.
Ce dossier contient 4 sous-dossiers. easy contient des instances de petite taille ou très simple pour un humain
pour vous aider à débugger. uniform contient des instances où les couleurs ont été générées aléatoirement avec une
loi uniforme. Les tailles n et m varient de 5 à 50. Le nombre de couleurs k varie de 2 à 10. geometric contient des
instances où les couleurs ont été générées aléatoirement avec une loi géométrique de paramètre p. Les tailles n et
m varient de 5 à 50. Le nombre de couleurs k vaut 2, 4, 6, 8 ou 10 et p vaut 0.3, 0.6 ou 0.9. Enfin unique contient
des instances où, pour chaque pièce, il n’y a au plus une pièce candidate pour chaque voisin (la couleur qui les uni
est unique). Les tailles n et m varient de 5 à 100.
Vous pouvez voir comment ces instances ont été générées avec le fichier generate.py, en python2. Vous pouvez
si vous le souhaitez utiliser ce fichier pour générer d’autres instances.
Le paramètre le plus intéressant à évaluer est le temps de calcul. Cependant, vous pouvez en imaginer d’autres
qui peuvent expliquer le temps de calcul. Par exemple, si votre algorithme algorithm exact1 contient plusieurs
parties distinguables, il peut être intéressant de savoir laquelle a pris plus de temps en fonction du type d’instance.
Pour votre algorithme algorithm pl1, si vous l’avez décrit comme un problème d’optimisation, il est intéressant
de savoir à quelle distance de l’optimal il se trouve, combien d’itérations il a effectuées, ...
6
3
Développement
Dans cette section vous choisirez un des développements proposés et ne traiterez que celui ci.
Dans cette partie, on s’intéresse au problème de maximiser le nombre de pièces posées tout en respectant la
contrainte des couleurs, quitte à laisser des trous. On nomme ce problème (max-PZL). Si le puzzle est réalisable,
alors une solution optimale consiste à placer toutes les pièces. Si le puzzle n’est pas réalisable, alors la solution
optimale contiendra strictement moins de n × m pièces.
3.1
Approximabilité
Soit r < 1, on dit qu’un algorithme A pour (max-PZL) est une approximation polynomiale de rapport r ou une
r-approximation si et seulement si
— La complexité en temps de A est polynomiale
— Quelque soit l’instance I de solution optimale s∗ , l’algorithme A renvoie une solution s ≥ r · s∗ . Autrement
dit, si le puzzle est faisable, l’algorithme arrive à placer au moins r · (n × m) pièces du puzzle.
Décrivez dans votre rapport une 21 -approximation triviale (en plaçant la moitié des pièces) pour (max-PZL).
On souhaite montrer qu’il est possible de construire une 95 -approximation. Voici quelques indications :
1. On suppose que le puzzle est de taille 3p × 3q et est réalisable. Montrer que, en regroupant la plupart des
pièces en carrés de taille 2 pièces par 2 pièces, on peut placer au moins 59 des pièces.
2. Montrer que c’est toujours possible si le puzzle est de taille quelconque et réalisable.
3. Le problème de k-Set Packing est le suivant : soit X un ensemble quelconque et S un ensemble de sousensembles de X de taille au plus k, il faut trouver le plus grand sous-ensemble P de S constitué uniquement
d’ensembles 2 à 2 disjoints. L’algorithme 1 est une k2 -approximation polynomiale pour ce problème. Utilisez
ce problème et cette approximation pour construire une 59 -approximation pour (max-PZL) quand le puzzle
est réalisable.
Décrivez vos preuves ou vos idées dans le rapport.
Algorithme 1
2
k
approximation pour le problème du k-Set packing
P ← ∅.
Pour s ∈ S Faire
Si s est disjoint avec tous les ensembles de P Alors
Ajouter s à P
Tant que Vrai Faire
Pour p ∈ P , s, s0 ∈ S Faire
Si s et s0 sont disjoints avec tous les ensembles de P \{p} Alors
Retirer p de P
Ajouter s et s0 à P
Si P n’a pas changé Alors
Renvoyer P
Implémentez et évaluer la 12 et la 59 -approximations.
Voici quelques perspectives en plus que vous pouvez ajouter au rapport si vous en avez le temps.
— Généraliser ces résultats au cas où le puzzle n’est pas réalisable.
— Que se passe-t-il si on essaie de regrouper les pièces autrement ? Peut-on améliorer le rapport d’approximation ?
3.2
Heuristique Monte Carlo
On souhaite développer un algorithm de Backtracking probabiliste pour résoudre ce problème.
L’idée est la suivante : savoir à tout instant si une pièce peut être placée sur des coordonnées i,j.
On propose l’algorithme de backtracking suivant : poser une pièce sur un emplacement autorisé, compter, pour
chaque case, combien de pièces on peut placer dessus, et, pour chaque pièce, sur combien de cases on peut la poser.
Si aucune valeur ne fait 0, on continue avec une autre pièce sur une autre case. Si on tombe sur 0, le puzzle est
impossible, on enlève la pièce qu’on a posée. On essaie alors de la poser ailleurs ou de changer de pièce.
7
Cet algorithme revient à faire un branch and bound très simpliste sur l’ensemble des solutions partielles. On
appelle une solution partielle une configuration où certaines pièces sont déjà posées et d’autres non. Une solution
partielle peut avoir des successeurs si on peut encore poser des pièces. Sinon c’est une feuille.
Cet algorithme est beaucoup trop lent, car les solutions partielles sont très nombreuses. On va essayer de le
guider avec une méthode de Monte Carlo. À partir d’une solution partielle P , on peut déterminer un score s(P ) et
une solution réalisable f (P ) descendant de P de manière probabiliste avec l’algorithme 2.
Algorithme 2 Score d’une solution partielle P et solution réalisable
Entrées: Un entier h
Pour i de 1 à h Faire
En partant de la solution partielle P , placer au hasard des pièces jusqu’à ce que ce qu’on tombe sur une feuille
fi
Compter le nombre de pièces posées xi
s(P ) ← la moyenne des xi , 1 ≤ i ≤ h
f (P ) ← la meilleure solution fi , 1 ≤ i ≤ h
Renvoyer s(P ), f (P )
On utilise ensuite l’algorithme 3 pour trouver une bonne solution partielle à notre problème.
Algorithme 3 Algorithme de MonteCarlo pour (max-PZL)
Entrées: Un entier w et un entier h
f ← P0 ← une solution partielle où aucune pièce n’est posée.
L ← [P0 ]
Tant que L est non vide Faire
P ← le premier élément de L, qu’on retire
Pour successeur Q de P Faire
Déterminer s(Q) et f (Q) avec l’algorithme 2 et l’entier h.
Insérer Q dans L
Si f contient moins de pièce que f (Q) Alors
f ← f (Q)
Trier L par score décroissant de score
Ne conserver que les w premiers éléments de L
Renvoyer f
Implémentez et évaluez cet algorithme. Vous pouvez éventuellement le modifier. Dans quelle catégorie d’instances
fonctionne-t-il le mieux ? Quel est l’impact des paramètres h et w ? ...
Voici quelques perspectives en plus que vous pouvez ajouter au rapport si vous en avez le temps.
— On considère qu’un générateur pseudo-aléatoire a une période après laquelle il recommence à renvoyer les
mêmes chiffres. En c, la fonction int rand() de base renvoie un nombre pseudo aléatoire avec une période
de 232 . En fonction de n et m, pourriez vous dire à partir de quand l’algorithme du calcul de score devient
nécessairement biaisé ?
— Pour revenir à l’idée du backtracking initial, on peut orienter le générateur aléatoire. Au lieu de choisir la case
et la pièce à placer uniformément, on va choisir une autre loi de probabilité. Pour chaque case, on compte le
nombre de pièces qu’on peut placer dessus. Trouvez une loi qui aura tendance à choisir la case avec le moins
de pièces. Puis, pour toutes ces pièces, comptez le nombre de cases où on peut les placer. Trouvez une loi
qui aura tendance à choisir la pièce avec le moins de cases. Ce faisant, on supprime assez peu de possibilités
sur l’ensemble du puzzle, on aura donc plus de chances de placer plein de pièces. Vérifiez si les performances
sont meilleures.
8
Téléchargement