Alexandre Alapetite & Pierre Cohade   12/02/2003
Enseignant : Bernard Fade

BE de satisfaction de contraintes en LISP

Sommaire

Quitter

Sujet

On se place dans le cas de contraintes binaires. Les contraintes sont exprimées en intention. Le langage de programmation utilisé est Common Lisp.
Le projet consiste en :

Variables et domaines

Il s'agit uniquement de variables entières. On donne pour chaque variable une liste comprenant le nom de la variable puis son domaine. Celui-ci est explicité soit sous forme d'un intervalle, par exemple (toto 0 10) pour l'intervalle fermé de 0 à 10, soit sous forme énumérée grâce à une sous-liste, par exemple (var1 (1 14 52 66)).

Cette syntaxe a été implémentée, en saisie au clavier, ou à partir d'un fichier.
Deux variantes, dont la détection est automatique, sont prévues :

Contraintes

Il s'agit de contraintes unaires (pour éliminer les valeurs particulières dans un intervalle) ou binaires. Elles n'utilisent que les opérateurs arithmétiques +, −, *, /, <, <=, =, /=, >, >=.
On donne pour chaque contrainte une liste décrivant une expression infixée et parenthèsée (si nécessaire).
Exemples :

(toto /= 5)
(x1 >= x3)
(((3 * x * x) + (4 * x * y) - 3) = 0)

Nous avons tout de suite pensé le système en n-aire. Cela nous a semblé plus simple de le traiter au départ, plutôt que d'avoir à adapter le programme plus tard.
De même, dès l'origine, nous avons prévu la possibilité de saisir les contraintes en préfixé ou infixé, là aussi avec détection automatique.
Les contraintes sont analysées puis parenthèsées et transformées si nécessaire en préfixé, de manière à ce qu'elles puissent facilement être évaluées par LISP.
Exemple de contraintes valides pour notre interface :

(X1 < X2)
(< X1 X2)
((X1 + 4 + X2 + X3) <= (X4 * X5))
(<= (+ X1 (+ 4 (+ X2 X3))) (* X4 X5))

Structure du programme

Nous avons divisé le programme LISP en différents fichiers spécialisés :

TRAITEMENT_PLISTE.LISP

Ce fichier est la bibliothèque principale du programme. C'est elle qui contient les fonctions permettant de gérer le stockage, les accès et les modifications des contraintes.
Ce stockage est organisé autour d'une PListe (SYMBOL-PLIST) car sa structure était déjà proche de celle dont nous avions besoin.


INTERFACE_HM.LISP

Ce fichier gère l'interface entre le noyau du programme et le monde extérieur.
Il permet :


MAIN.LISP

Ce fichier est le point d'entrée de notre programme. C'est lui qui charge les autres bibliothèques.
Il lance en boucle les différents menus, afin de pouvoir faire plusieurs actions consécutives, jusqu'à la sortie, où il ferme LISP complètement.


Les noyaux

Voici les fichiers contenant les algorithmes qui nous ont été demandé d'implémenter. Tous contiennent leurs méthodes propres.

NOYAU_BT.LISP

Voici le backtrack.
Il fonctionne sur la base d'une seule fonction récursive, qui teste successivement pour chaque variable les différentes valeurs de son domaine. Faisant toutes les combinaisons, l'algorithme est assuré de trouver une solution si elle existe. Il n'y a aucune mémoire des combinaisons testées, ni fonction d'élagage.
Notre structure de donnée est optimisée au niveau des contraintes. Celles-ci ne sont stockées q'au niveau de la variable testée le plus tard dans l'algorithme.
ie: les contraintes ne sont pas stockées de manière symétrique. Si (A < B), la contrainte ne sera stockée qu'au niveau de B, car il faut que l'une des deux variables ait pris une valeur avant de pouvoir tester la contrainte.

NOYAU_AC3.LISP

Ce fichier contient le filtre utilisé pour AC3.
C'est un filtre partiel de type partial look future, au cours duquel on parcourt toutes les variables, pour lesquelles on essaye de réduire leur domaine, en fonction des contraintes qu'elles ont avec les autres variables. Au cours du traitement d'une variable, on ne modifie pas les domaines des autres variables.
Ce fichier contient une fonction qui boucle sur toutes les variables, et parcours leurs contraintes pour élaguer leur domaine.

NOYAU_FC.LISP

Ce fichier contient le filtre utilisé pour FC.
C'est un filtre exécuter lors du backtrack. Lorsqu'on se trouve sur une variable, il essaye d'élaguer les domaines des variables avec lesquelles elle a des contraintes.


Résultats

Nous avons essayé notre programme sur plusieurs problèmes, où l'efficacité des algorithmes c'est montrée variable.

Le problème des dames

Il s'agit de placer n dames sur un échiquier de taille n×n sans que deux dames ne soient sur la même colonne, ni sur la même ligne, ni sur la même diagonale.
X1 à Xn sont les positions des dames sur un échiquier dont les cases sont numérotées en ligne de 1 à n2.
Nous avons fait un script de génération de contraintes. Voici le fichier de contraintes pour n=5 :

(D (4 8 12 16))
(C (3 7 11 15))
(B (2 6 10 14))
(A (1 5 9 13))
(B /= (A + 1))
(B /= (A + 5))
(B /= (A - 3))
(C /= (A + 2))
(C /= (B + 1))
(C /= (A + 10))
(C /= (B + 5))
(C /= (A - 6))
(C /= (B - 3))
(D /= (A + 3))
(D /= (B + 2))
(D /= (C + 1))
(D /= (A + 15))
(D /= (B + 10))
(D /= (C + 5))
(D /= (A - 9))
(D /= (B - 6))
(D /= (C - 3))
NIL

Et voilà la solution trouvée par le backtrack.

résultat avec 4 dames
X1 = 5
X2 = 14
X3 = 3
X4 = 12

Voici le problème résolu pour n=20.

X1 = 1
X2 = 42
X3 = 83
X4 = 24
X5 = 65
X6 = 246
X7 = 287
X8 = 228
X9 = 349
X10 = 390
X11 = 331
X12 = 172
X13 = 313
X14 = 374
X15 = 155
X16 = 196
X17 = 137
X18 = 278
X19 = 119
X20 = 220

Le temps de calcul pour n=20 a été raisonnable (~10mn) avec le backtrack.
On constate un gain avec FC (2 min 12 avec n=17 en backtrack, 1 min 15 en FC).
Par contre, le AC3 ne filtre rien sur ce problème, il n'est donc qu'une perte de temps.
Le problème a été résolu jusqu'à n=25.


Conclusion

Ce projet a permis de prendre contact avec LISP et avec les problèmes à satisfaction de contraintes.
Accés aux sources (ZIP 25.6Ko)

Sommaire
https://alexandre.alapetite.fr

Quitter