Vous êtes sur la page 1sur 5

Programmation par contraintes Chapitre 3

Chapitre 3 : Programmation logique par contraintes avec Gnu-Prolog

1 - Variables sur les domaines finis


Une variable sur domaine fini « FD » est représentée par une chaîne alphanumérique
commençant par une majuscule. Cependant, une variable FD ne peut prendre qu'une valeur
entière, positive ou nulle, et possède les caractéristiques suivantes :

Prédicat Description Exemple


fd_max_integer/1 Donne la valeur constante maximum
d’une variable FD ayant pour
domaine l'intervalle
[0..fd_max_integer]
fd_domain/3 fd_domain(L,Min,Max) fd_domain([X1,X2,X3,X4],1,4)
fd_domain(X,Min,Max) fd_domain(X1,1,4)
contraint chaque variable FD de la
liste L (ou la variable X) à prendre
une valeur comprise entre Min et
Max.
fd_domain/2 fd_domain(L,Valeurs) fd_domain([A1,A2],[8,10,12])
fd_domain(X,Valeurs) contraint une fd_domain(A,[8,10,12])
variable X, ou une liste de variables
L, à prendre leurs valeurs dans une
liste donnée d'entiers :

fd_min/2 Donne la plus petite valeur, d’une


variable FD.
fd_max/2 Donne la plus grande valeur d’une
variable FD.
fd_dom/2 Donne la liste des valeurs d’une
variable FD.
fd_size/2 Donne le nombre de valeurs d’une
variable FD.
2 - Contraintes sur les domaines finis
2.1 - Contraintes arithmétiques
un solveur de contraintes intégré à Gnu-Prolog propose un filtrage les domaines des
variables par rapport à deux types de consistance : consistance d'arc partielle ou une
consistance d'arc totale : la consistance d'arc totale, aussi appelée 2-consistance; la
consistance d'arc partielle est une consistance légèrement plus faible que la consistance
d'arc totale, qui revient à considérer les domaines de valeurs des variables FD comme des
intervalles de valeurs, et à ne vérifier la consistance d'arc qu'aux bornes minimales et
maximales de ces intervalles. Cette consistance partielle est plus rapidement établie, mais en
contrepartie elle enlève moins de valeurs des domaines des variables.
Ainsi, les prédicats décrits dans le tableau suivant posent des contraintes d'égalité, de
différence, ou d'inégalité entre deux expressions arithmétiques Expr1 et Expr2 ; pour ces
contraintes, le solveur de contraintes effectuera un filtrage des domaines des variables par
rapport à une consistance d'arc partielle.

1
Programmation par contraintes Chapitre 3

Prédicat Description Exemple


Expr1 #= Expr2 contraint Expr1 à être
#=
égale à Expr2
Expr1 #\= Expr2 contraint Expr1 à être
#\=
différente de Expr2
Expr1 #< Expr2 contraint Expr1 à être
#<
inférieure à Expr2
Expr1 #=< Expr2 contraint Expr1 à être
#=<
inférieure ou égale à Expr2
Expr1 #> Expr2 contraint Expr1 à être
#>
supérieure à Expr2
Expr1 #>= Expr2 contraint Expr1 à être
#>=
supérieure ou égale à Expr2

Remarque

Pour que le solveur de contraintes effectue un filtrage des domaines par rapport à une
consistance d'arc totale, il faudra utiliser les prédicats suivants :
Expr1 #=# Expr2, Expr1 #\=# Expr2, Expr1 #<# Expr2, Expr1 #=<# Expr2, Expr1 #>#
Expr2, Expr1 #>=# Expr2.

2.2 - Contraintes booléennes et contraintes sur les contraintes


Une contrainte booléenne sur les domaines finis est une expression composée à partir des
opérateurs booléens suivants :
 l'équivalence : E1 #<=> E2
 la non équivalence : E1 #\<=> E2
 l'implication : E1 #==> E2
 le non logique : #\E1
 le ou logique : E1 #\/ E2
 le ou exclusif : E1 ## E2
 le et logique : E1 #/\ E2

Les opérandes d'une contrainte booléenne peuvent être la valeur entière 0 (interprétée comme
"faux"), la valeur entière 1 (interprétée comme "vrai") ou une variable (interprétée comme une
variable FD dont le domaine est restreint aux valeurs 0 et 1). Les opérandes d'une contrainte
booléenne peuvent aussi être des contraintes, ce qui permet de poser des contraintes sur les
contraintes ! Lorsqu'une contrainte c apparaît dans une contrainte booléenne, elle est "réifiée",
c'est-à-dire que dès lors que le solveur de contraintes peut déduire que cette contrainte c est
vraie, alors elle est remplacée par la valeur 1, tandis que s'il arrive à prouver quelle est fausse,
elle est remplacée par 0.

Par exemple, la contrainte booléenne

(X#=Y) #<=> (Y#=<3)


exprime le fait que les 2 variables X et Y doivent être égales si et seulement si Y est inférieure
ou égale à 3. On aurait tout aussi bien pu écrire :
((X#=Y) #==> (Y#=<3)) #/\ ((X#\=Y) #==> (Y#>3))
ou autrement dit, si X=Y alors Y≤3 sinon Y>3.

2
Programmation par contraintes Chapitre 3

Le tableau suivant regroupe des prédicats exprimant le nombre de contraintes satisfaites :


Prédicat Description Exemple
fd_cardinality(L,X) unifie X fd_cardinality([C1,C2,C3,C4,C5],3)
avec le nombre de contraintes
de la liste L qui sont Exactement 3 contraintes parmi les 5
satisfaites. contraintes C1, C2, C3, C4 et C5 doivent
être vérifiées.
fd_cardinality/2
fd_cardinality([C1,C2,C3,C4,C5],NbVrai),
NbVrai#>=3

Exprime qu'au moins 3 contraintes doivent


être satisfaites.
permettent de poser des
fd_cardinality/3,
contraintes sur le nombre de
fd_at_least_one/1,
contraintes satisfaites.
fd_at_most_one/1 t
fd_only_one/1

2.3 - Contraintes symboliques


On peut poser des contraintes symboliques, sur des ensembles de variables, à l'aide de
prédicats prédéfinis Gnu-Prolog :

Prédicat Description Exemple


fd_all_different/1 fd_all_different(L) fd_all_different([X1,X2,X3,X4])

contraint les variables de la liste L à


prendre des valeurs toutes
différentes.
fd_relation/2 fd_relation(L_Valeurs,Vars) fd_relation([[0,1,2],[3,4,5]],[X,Y,Z])
pose la contrainte "(X=0 et Y=1 et
contraint la liste Vars à être égale à Z=2) ou (X=3 et Y=4 et Z=5)"
une des listes de la liste L_Valeurs.
fd_element/3 contraignent une variable à prendre
une valeur de rang donné dans une
fd_element_var/3 liste de valeurs ou de variables
donnée
fd_atmost/3, imposent qu'un nombre donné de
fd_atleast/3 variables, parmi une liste de
fd_exactly/3 variables, prennent une valeur
donnée.

3
Programmation par contraintes Chapitre 3

3 - Résolution de CSPs par Gnu-Prolog

Pour demander à Gnu-Prolog de rechercher une affectation de valeurs à une liste de variables,
on utilise le prédicat fd_labeling.

Prédicat Description Exemple


fd_labeling/1 fd_labeling(L) Pb des 4 reines

Affecte une valeur à chaque fd_labeling([X1,X2,X3,X4]).


variable FD de L de telle sorte que
toutes les contraintes portant sur ces
variables soient satisfaites. Lorsque
Prolog "backtrack" sur ce prédicat,
il cherche à chaque fois une
solution différente.
fd_labeling/2 fd_labeling(L,O) affecte une valeur fd_labeling([X1,X2,X3,X4],
à chaque variable FD de L de telle [variable_method(first_fail)])
sorte que toutes les contraintes
portant sur ces variables soient Enumére les affectations en utilisant
satisfaites ; O est une liste d'options l'heuristique "échec-d'abord"
qui spécifient l'ordre d'affectation
des variables et des valeurs fd_labeling([X1,X2,X3,X4],
[variable_method(first_fail),
value_method(max)])
les valeurs sont énumérées de la
plus grande à la plus petite.
fd_labeling([X1,X2,X3,X4],
[variable_method(first_fail),
value_method(max),backtracks(B)])
Pour savoir combien de retours en
arrière ont été effectués

Remarque :

- L'option correspondant à l'heuristique "échec-d'abord", consistant à affecter en premier


la variable dont le domaine a le plus petit nombre de valeurs localement consistantes
avec l'affectation partielle en cours, est variable_method(first_fail).
- variable_method(most_constrained) a comme effet que la variable choisie pendant la
résolution est celle qui est la plus contrainte.
- L'option backtracks(B) unifie B avec le nombre de retours en arrières effectués par
l'algorithme pour trouver la solution.

4
Programmation par contraintes Chapitre 3

4. Problème des 4 reines

Programme correspondant à la première modélisation :

reines4_modele1(Vars,Options) :-
/* on déclare les variables */
Vars = [C1,C2,C3,C4,L1,L2,L3,L4],
/* le domaine de chaque variable est {1,2,3,4} */
fd_domain(Vars,1,4),
/* Les reines doivent être sur des lignes différentes */
fd_all_different([L1,L2,L3,L4]),
/* Les reines doivent être sur des colonnes différentes */
fd_all_different([C1,C2,C3,C4]),
/* Les reines doivent être sur des diagonales montantes différentes */
C1+L1 #\= C2+L2, C1+L1 #\= C3+L3, C1+L1 #\= C4+L4,
C2+L2 #\= C3+L3, C2+L2 #\= C4+L4, C3+L3 #\= C4+L4,
/* Les reines doivent être sur des diagonales descendantes différentes */
C1-L1 #\= C2-L2, C1-L1 #\= C3-L3, C1-L1 #\= C4-L4,
C2-L2 #\= C3-L3, C2-L2 #\= C4-L4, C3-L3 #\= C4-L4,
/* on recherche une affectation des variables qui soit solution */
fd_labeling(Vars,Options).

Programme correspondant à la deuxième modélisation :

reines4_modele2(Vars,Options) :-
/* on déclare les variables */
Vars = [X1,X2,X3,X4],
/* le domaine de chaque variable est {1,2,3,4} */
fd_domain(Vars,1,4),
/* Les reines doivent être sur des lignes différentes */
fd_all_different(Vars),
/* Les reines doivent être sur des diagonales montantes différentes */
1+X1 #\= 2+X2, 1+X1 #\= 3+X3, 1+X1 #\= 4+X4,
2+X2 #\= 3+X3, 2+X2 #\= 4+X4, 3+X3 #\= 4+X4,
/* Les reines doivent être sur des diagonales descendantes différentes */
1-X1 #\= 2-X2, 1-X1 #\= 3-X3, 1-X1 #\= 4-X4,
2-X2 #\= 3-X3, 2-X2 #\= 4-X4, 3-X3 #\= 4-X4,
/* on recherche une affectation des variables qui soit solution */
fd_labeling(Vars,Options).

Vous aimerez peut-être aussi