3.1- TP - Automate des motifs
On s'interesse dans ce TP à "l'automate des motifs", une structure qui permet de rechercher rapidement un motif (= un mot) dans un texte. Le principe de l'automate des motifs n'est pas à retenir, il s'agit simplement de travailler les automates sur une application réelle.
I. Préliminaires : Automates en OCaml
On définit une structure d'automate en OCaml en utilisant un type enregistrement.
Types enregistrement
Ce sont des types qui permettent d'associer plusieurs objets au sein d'une même variable. Pour les déclarer, on utilise la syntaxe suivante :
type nom_enregistrement = {nom1: type1; nom2: type2; nom3: type3};; Pour créer une variable de type nom_enregistrement, on utilise la syntaxe suivante :
let val_enregistrement = {nom1 = valeur1; nom2 = valeur2; nom3 = valeur3};; On peut ensuite accéder aux valeurs des champs de la manière suivante :
val_enregistrement.nom1 (*ceci vaut valeur1*) Il est possible de rendre un champs d'un enregistrement mutable (sa valeur peut être modifiée) en le déclarant avec mutable nom1: typ1 au lieu de simplement nom1: type1.
On va travailler avec l'alphabet
Loading editor...
type lettre = A | B;; type mot = lettre list;;
Ainsi, un automate sera un type enregistrement avec :
- Q l'ensemble des états, qui sera en fait simplement un entier
nsymbolisant que les états de 0 à n-1 sont présents. - q0 l'état initial
- F l'ensemble des états terminaux, qui est un tableau qui à chaque état (chaque entier entre 0 et n-1) associe un booléen
- delta les transitions, qui est un tableau de
transition, un type enregistrement.
Le type transition est un enregistrement contenant deux champs, "a" et "b", indiquant l'état d'arrivée de la transition pour chaque lettre.
Le type array en OCaml
En plus des listes, le langage OCaml a un type array. Il s'agit de tableau indexables (donc on peut prendre directement l'élément n°i directement), mutables (on peut changer la valeur de l'élément n°i), mais non redimensionnables : on les déclare avec une taille, et ils ne changent jamais de taille !
Comme les listes, le type d'un tableau d'entiers s'écrit int array, de booléens bool array, etc.
On les utilise de la manière suivante :
- création :
let tab = Array.make n xcréé un tableautabde longueurnrempli dexlet tab = [|1; 2; 3; 10|]créé un tableau contenant 1, 2, 3 et 10.
- accès à un élément
tab.(i)lis l'élément i.
- modification
tab.(i) <- xécrit x dans la case i de tab. Cette opération renvoie unit, on la succède donc en général d'un simple;.
Question 1
Complétez le type dfa (pour "deterministic finite automaton") correspondant à un automate déterministe.
Loading editor...
type dfa_transition = {a: (*compléter*); b: (*compléter*)};;
type dfa = {q: (*compléter*); q0: (*compléter*); f: bool array; delta: (*compléter*) array};;type dfa_transition = {a: int; b: int};;
type dfa = {q: int; q0: int; f: bool array; delta: dfa_transition array};;Question 2
Dans une variable, définir l'automate déterministe représenté par ce dessin :
Loading editor...
Dessiner un automate
Le site fournit une fonction dessine aut, qui affiche un automate. Testez-la sur l'automate de la question précédente !
Loading editor...
let dessine (aut: dfa) =
let finals = List.filter (fun q -> aut.f.(q)) (List.init aut.q Fun.id) in
let transitions = List.concat (List.init aut.q (fun q ->
[(q, "A", aut.delta.(q).a); (q, "B", aut.delta.(q).b)])) in
afficher_automate aut.q [aut.q0] finals transitions;;Loading editor...
Question 3
Ecrivez une fonction delta_f de type dfa -> int -> lettre -> int qui correspond à la fonction
Loading editor...
Question 4
Ecrivez une fonction delta_star de type dfa -> int -> mot -> int qui correspond à la fonction
Loading editor...
Question 5
Ecrivez une fonction accepte de type dfa -> mot -> bool qui renvoie true si l'automate accepte le mot, false sinon. Testez votre fonction sur quelques mots exemples.
Loading editor...
II. L'automate des motifs
L'idée de l'automate des motifs, c'est d'avoir un automate qui, lorsqu'on recherche un mot dans un texte, dans lis les lettres du texte une par une, et est dans un état acceptant à chaque fois que le mot est reconnu.
1. Construction
L'automate des motifs est construit par rapport au mot
- Si le mot est de taille
n, l'automate àn+1états : un pour le mot vide, puis un pour chaque lettre du mot. - L'état initial est le 0 (le mot vide)
- L'état acceptant est la dernière lettre
L'idée est que, à tout moment, l'état de l'automate correspond à l'endroit où, au maximum, on est dans le mot à l'état actuel. Voici un exemple sur le mot aba et le texte abbaaababa : l'automate à 4 états, et est dans les états suivants :
a b b a a a b a b a
0 1 2 0 1 1 1 2 3 2 3
^ ^ Pour formaliser cela, on défini
le préfixe de taille de . - pour
la taille du plus grand suffixe de qui est également préfixe de - on prend alors, pour la fonction de transition,
Dans l'exemple ci-dessus, on a :
, , , ,
Question 6
Dessinez l'automate correspondant au motif aba, puis détaillez le chemin parcouru en lisant le mot aabaabba. Quand est-ce que l'automate est dans un état acceptant ?
Loading editor...
Question 7
Ecrivez une fonction prefixe m i de type mot -> int -> mot qui renvoie le préfixe de taille i de m.
Loading editor...
Question 8
Ecrivez une fonction suffixe_max p m de type mot -> mot -> int qui calcule la taille du plus grand suffixe de p qui est aussi préfixe de m. Vous pouvez vous aider d'une fonction auxiliaire est_prefixe p m.
Loading editor...
Question 9
Ecrivez une fonction sigma m i x qui calcule la valeur de m.
Loading editor...
Question 10
Enfin, écrivez une fonction automate_motif m de type mot -> dfa qui calcule l'automate des motifs pour un mot m.
Loading editor...
2. Utilisation de l'automate
Question 11
On prend comme exemple le mot aba. Votre automate accepte-il aba ? abbababa ? abaaa ?
Loading editor...
let m1 = [A; B; A];; let m2 = [A; B; B; A; B; A; B; A];; let m3 = [A; B; A; A; A];;
Question 12
Ecrivez une fonction recherche automate texte de type dfa -> mot -> int list qui renvoie une liste de tous les
Loading editor...
let texte = [A; A; B; A; B; A; B; A; A; B; B; A; A; B; A; B; B];;
Loading editor...
Question 13
Quelle est la complexité de la création de l'automate ? Et celle de la recherche dans un texte une fois l'automate créé ?
Loading editor...
3. Reconnaitre plusieurs mots ?
On aimerait maintenant reconnaitre plusieurs mots, au choix. On propose pour cela d'utiliser l'automate union.
Question 14
Ecrivez une fonction union a1 a2 de type dfa -> dfa -> dfa qui renvoie l'automate union de a1 et a2.
Indication : ici, on a que des automates deterministes. L'idée est de faire l'union et la determinisation en une seule étape.
Loading editor...
Les modules Queue et Hashtbl
OCaml fournit des files et des tables de hachage mutables : les fonctions qui les modifient renvoient unit.
Files (module Queue), de type 'a Queue.t :
let f = Queue.create ()crée une file vide ;Queue.add x fajoutexà la fin de la filef;Queue.pop fretire l'élément en tête de la file et le renvoie (exceptionQueue.Emptysi la file est vide) ;Queue.is_empty fteste si la file est vide,Queue.length frenvoie son nombre d'éléments.
Tables de hachage (module Hashtbl), de type ('a, 'b) Hashtbl.t (clés de type 'a, valeurs de type 'b) :
let t = Hashtbl.create 16crée une table vide (16 est une taille initiale indicative, la table s'agrandit toute seule) ;Hashtbl.replace t k vassocie la valeurvà la clék(en remplaçant l'ancienne valeur s'il y en avait une) ;Hashtbl.find t krenvoie la valeur associée àk(exceptionNot_foundsikn'est pas dans la table) ;Hashtbl.find_opt t krenvoieSome vsikest associée àv,Nonesinon ;Hashtbl.mem t kteste si la clékest dans la table ;Hashtbl.remove t ksupprime la clék;Hashtbl.length trenvoie le nombre de clés.
N'importe quelle valeur peut servir de clé, par exemple un couple d'entiers : Hashtbl.replace t (2, 3) 7.
Attention : Hashtbl.add t k v existe aussi, mais il masque l'ancienne valeur de k au lieu de la remplacer (elle réapparaît après un Hashtbl.remove).
Question 15
Dessinez l'automate union de la question précédente : beaucoup de ses états ne sont pas accessibles depuis l'état initial, ils ne servent à rien. Ecrivez une fonction union_accessible a1 a2, de type dfa -> dfa -> dfa, qui ne construit que les états accessibles de l'automate union.
Indication : faites un parcours (en largeur, par exemple) depuis le couple d'états initiaux, en numérotant les couples Queue) contient les couples à traiter, et une table de hachage (Hashtbl) permet de retrouver le numéro d'un couple déjà vu.
Loading editor...
Entrez le code donné en cours pour accéder à la solution :