Adloun

Probleme – Quine sur les \textsc{fnc} : ce que la simplification fait gagner

Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 20 — Logique propositionnelle

Énoncé

On représente une fnc par une liste de listes de littéraux, un littéral étant un couple (string * bool) — le booléen étant le signe.

Corrigé

1. La simplification, et c'est elle qui porte tout l'algorithme.


type litteral = string * bool          (* (nom, signe) : (p, false) est ¬p *)
type clause = litteral list            (* disjonction *)
type fnc = clause list                 (* conjonction *)

(* La FNC f où p vaut b. Deux effets, et ils sont différents :
   - une clause contenant le littéral SATISFAIT disparaît (elle est vraie) ;
   - dans les autres, le littéral contraire est RETIRÉ (il est faux).
   Précondition : aucune. Complexité : Theta(taille de f). *)
let simplifier f p b =
  f
  |> List.filter (fun c -> not (List.mem (p, b) c))
  |> List.map (List.filter (fun (q, _) -> q <> p))

La dissymétrie des deux lignes est le point délicat. La clause qui contient sous le bon signe est effacée — elle est vraie, elle ne contraint plus rien. La clause qui contient sous le mauvais signe maigrit — ce littéral-là ne peut plus la sauver. Confondre les deux, c'est écrire un algorithme qui rend « satisfiable » sur une formule qui ne l'est pas.

2. Quine.


(* Un modèle de f, ou None. Le modèle est partiel : les variables absentes
   du résultat peuvent prendre n'importe quelle valeur.
   Précondition : aucune. *)
let rec quine f =
  if f = [] then Some []                          (* plus de clause : VRAI *)
  else if List.exists (fun c -> c = []) f then None  (* clause vide : FAUX *)
  else
    let (p, _) = List.hd (List.hd f) in
    match quine (simplifier f p true) with
    | Some m -> Some ((p, true) :: m)
    | None ->
        match quine (simplifier f p false) with
        | Some m -> Some ((p, false) :: m)
        | None -> None

3. Terminaison et arrêts.

Variant : le nombre de variables distinctes de f. La deuxième ligne de simplifier retire toute occurrence de ; aucune variable n'est ajoutée. Le variant décroît donc strictement de à chaque appel récursif, et il est entier positif : la récursion a profondeur au plus .

Les deux conditions d'arrêt sont duales, et il faut les tester dans cet ordre.

`f = []`aucune clausela conjonction vide est vraie
`List.mem [] f`une clause videla disjonction vide est fausse

Ces deux conventions ne sont pas des choix arbitraires : ce sont les éléments neutres. Une conjonction sans facteur vaut , une disjonction sans terme vaut — exactement comme une somme vide vaut et un produit vide vaut .

Correction : par récurrence sur le variant. Si est vide, toute valuation convient. Si une clause est vide, aucune ne convient. Sinon, est satisfiable si et seulement si l'est ou l'est — car toute valuation donne à l'une des deux valeurs —, et simplifier f p b est bien d'après la spécification ci-dessus.

Complexité : appels dans le pire cas, chacun en ; mais la clause vide interrompt une branche avant sa profondeur maximale, et c'est ce qui fait toute la différence en pratique.

4. Les mesures.

Instancevariablesappels de Quinevaluations
pigeonnier
pigeonnier
pigeonnier

La dernière ligne est la plus parlante : la même formule, traitée par le quine sans simplification de l'exercice 20.5, demande appels — soit l'arbre binaire complet. Avec simplification : trois. Le facteur ne tient qu'à ceci : dès que est fixée, la clause ou la clause devient vide, et les valuations des autres variables ne sont jamais visitées.

ImportantC'est un élagage, et c'est le chapitre chap:exploration

Le retour sur trace ne devient utilisable que si l'on détecte l'échec tôt. Ici, la détection est la clause vide, et la propagation est la simplification. Les solveurs sat modernes ajoutent à cela deux idées — la propagation unitaire (forcer une clause d'un seul littéral au lieu de la deviner) et l'apprentissage de clauses (retenir la cause d'un échec pour ne pas le refaire). Aucune ne change la complexité au pire cas ; toutes changent le temps d'exécution réel de plusieurs ordres de grandeur.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.