• Aucun résultat trouvé

Nous allons à présent examiner quelques programmes Prolog III qui utili- sent des contraintes booléennes.

Un ou sur deux variables

Un premier prédicat, tout à fait élémentaire, calcule le ou de deux variables. Le voici :

OuSimple(b1, b2, b1|b2) ->;

Quelques exécutions nous donnent une idée plus précise du traitement effectué, lorsqu'on pose ce type de contraintes.

> OuSimple(1',0',b); { b = 1'}

Le résultat de ou appliqué à vrai et faux est vrai.

> OuSimple(b1,0',b); { b => b1,

b1 => b }

Le résultat de ou appliqué à faux et à une variable a la valeur de cette variable (l'égalité est représentée ici par une double implication)

> OuSimple(b1,b2,0'); { b1 = 0',

b2 = 0' }

Enfin, pour que le résultat d'un ou appliqué à deux variables soit faux, il faut que les deux variables représentent la valeur faux.

Un ou sur une liste

Le calcul d'un ou sur une liste de booléens est un tout petit peu plus compliqué. En fait, on veut exprimer les contraintes équivalentes à l'expres- sion "il y a au moins un élément vrai dans la liste", le second argument du prédicat Ou ayant la valeur de cette expression.

Voici le programme :

Ou(<>, 0') ->;

Ou(<b1>.L, b1|b2) -> Ou(L, b2);

La règle d'arrêt exprime qu'il n'y a pas au moins un élément vrai dans la liste vide (il n'y en a bien sûr aucun). L'autre procède récursivement, en calculant le ou sur la tête de liste et sur le ou de la queue de liste.

Quelques exécutions pour y voir un peu plus clair :

> Ou(<1',b1,0'>,1'); { b1 !bool }

La liste comportant d'ores et déjà un élément dont la valeur est vrai, aucune contrainte ne porte sur b1, si l'on excepte le fait que b1 doit représenter une valeur booléenne.

> Ou(<b1,b1,b1>,1'); { b1 = 1'}

Ici, pour qu'il y ait au moins un élément vrai dans une liste composée de trois fois la même variable, il faut bien sûr que cette variable représente la valeur

vrai. > Ou(<b1,b2,b3>,b); { b => b1|b2|b3, b1 => b, b2 => b, b3 => b }

En laissant variables toutes les valeurs, le système de contrainte équivalent exprime l'égalité entre la valeur de b et celle du ou des éléments de la liste.

Au plus un de vrai

On complique quelque peu le problème. On veut imposer cette fois-ci qu'il y ait au plus une variable de vraie dans une liste.

L'écriture de ce prédicat n'est pas vraiment triviale, au sens ou il n'y a pas de correspondance immédiate entre la valeur de au plus un de vrai sur une liste de tête b1 et de queue L et sur celle de la liste L.

Si l'on observe le problème de manière un peu plus précise, on se retrouve devant l'alternative suivante :

Soit la tête de liste est vrai, et le ou de la queue de liste doit être faux, soit la valeur de la tête de liste est faux, et il faut qu'il y ait au plus un élément de vrai dans la queue de liste. On peut alors proposer une première solution de la forme suivante :

AuPlusUnVrai(<>,1')->; AuPlusUnVrai(<b1>.L, b)->

Ou(L,b2)

AuPlusUnVrai(L,b3)

{ b = (b1&~b2) | (~b1&b3) };

Voici quelques exécutions :

> AuPlusUnVrai(<1',b2,b3>,1'); { b2 = 0', b3 = 0'} > AuPlusUnVrai(<b1,0',b2>,b); { b&b1&b2 = 0', b|b1 = 1', b|b2 = 1'}

Pas de commentaires particuliers pour le premier exemple. Dans le second, le système de contraintes équivalent exprime les affirmations suivantes : • b1, b2 et b ne peuvent pas être vrais en même temps. (dans le cas

contraire, AuPlusUnVrai renverrai la valeur 1' avec deux éléments vrais dans la liste)

b1 ou b est vrai (dans le cas contraire, AuPlusUnVrai renverrai la valeur

0', alors que deux des éléments de la liste sur les trois sont faux) • b2 ou b est vrai (même raison que précédemment)

On peut également écrire ce programme sans renvoyer la valeur de l'expression, cela permettant de l'optimiser. On peut en effet considérer le problème de la manière suivante. Pour avoir au plus un élément vrai dans une liste de tête b1 et de queue L, la première contrainte à vérifier est que le

et de la tête et du ou de la liste soit faux. Il faut cependant que cette contrainte s'applique également récursivement sur chacune des sous listes auxquelles on a enlevé la tête de la précédente.

Par exemple, si on considère une liste de cinq éléments on aura :

{ b1 & (b2|b3|b4|b5) = 0' b2 & (b3|b4|b5) = 0'

b3 & (b4|b5) = 0' b4 & b5 = 0' }

Voici donc une autre écriture possible :

AuPlusUnVrai(L) -> OuSurAuPlusUnVrai(L, b); OuSurAuPlusUnVrai(<>, 0') ->;

OuSurAuPlusUnVrai(<b1>.L, b1|b2) -> OuSurAuPlusUnVrai(L, b2)

{b1 & b2 = 0');

Avec les mêmes exemples d'exécution que pour l'exemple précédent :

> AuPlusUnVrai(<1',b2,b3>); { b2 = 0', b3 = 0'}

> AuPlusUnVrai(<b1,0',b2>); { b1&b2 = 0'}

K éléments vrais dans une liste de n booléens

Voici, enfin, un dernier exemple de manipulation de contraintes sur des listes de booléens. Dans le programme suivant, le prédicat Vrais(k,L,b) a été construit de façon à ce que la valeur booléenne de b soit celle de l'expression :

Vrais(k, <>, 0') -> {k ! 0}; Vrais(0, <>, 1') ->; Vrais(0, <b1>.L, ~b1&b2) -> Vrais(0, L, b2); Vrais(k, <b1>.L, b) -> Vrais(k-1, L, b2) Vrais(k, L, b3), {k ! 0, b = (b1 & b2) | (~b1& b3)};

L'idée générale de ce programme est de considérer le problème de la manière suivante :

Il y a exactement k éléments vrais dans une liste de booléens de tête b1 et

de queue L, si et seulement si l'une des deux propositions suivantes est vraie :

(i) b1 est vrai et il y a exactement k-1 éléments vrais dans la liste L

(ii) b1 est faux et il y a exactement k éléments vrais dans la liste L

L'utilisation du paramètre b permet, en outre, de rendre le programme entièrement déterministe si k et la taille de la liste Lsont connus.

Voici un exemple d'exécution de ce programme :

> vrais(2,<X,Y,Z,T>,1'); { X & Y & Z = 0', X & Y & T = 0', X & Z & T = 0', Y & Z & T = 0', X | Y | Z = 1', X | Y | T = 1', X | Z | T = 1', Y | Z | T = 1' }

On notera que la réponse fournie, après simplification et élimination des variables inutiles du système de contraintes final, induit une formalisation différente du problème posé, que l'on peut exprimer de la manière suivante :

k booléens sont vrais parmi n si et seulement si

toute disjonction de (n-k+1) éléments est vraie, et toute conjonction de (k+1) éléments est fausse.

On remarquera également que l'exécution d'une question dans laquelle k

n'est pas connu donne un ensemble de réponses correspondant aux valeurs possibles de k.

Il existe également une manière de programmer cet exemple en utilisant un simple appel récursif, ce qui le rend nettement plus efficace. Nous laissons au lecteur le soin d'écrire ce programme à titre d'exercice.