TP 4 : Algorithme CYK (Cocke-Younger-Kasami)
Solution


type grammaire = {
n_variables : int;
regles1 : (int * char) list;
regles2 : (int * int * int) list;
epsilon : bool
}
(* 6 *)
let g0 = {
n_variables = 5;
regles1 = [(0, 'b'); (1, 'a'); (2, 'b'); (4, 'a')];
regles2 = [(0, 1, 2); (0, 2, 1); (0, 3, 1); (1, 1, 4); (3, 1, 2)];
epsilon = false
}
let init g u =
let n = String.length u in
let k = g.n_variables in
let t = Array.make (n+1) [||] in
for i = 0 to n do
t.(i) <- Array.make_matrix n k false
done;
if g.epsilon then t.(0).(0).(0) <- true;
let rec aux = function
| [] -> ()
| (i, a)::q ->
for d = 0 to n - 1 do
if u.[d] = a then t.(1).(d).(i) <- true
done;
aux q
in
aux g.regles1;
t
(* 7 *)
let cyk g u =
let t = init g u in
let n = String.length u in
for l = 2 to n do
for d = 0 to n - l do
let rec aux = function
| [] -> ()
| (i, j, k)::q ->
for p = 1 to l - 1 do
if d + p < n && t.(p).(d).(j) && t.(l-p).(d+p).(k)
then t.(l).(d).(i) <- true
done;
aux q
in
aux g.regles2
done
done;
t.(n).(0).(0);;
(* 8 *)
cyk g0 "aab";;
cyk g0 "aabb";;
cyk g0 "abaa";;
cyk g0 "aababa";;
(* 9 *)
type arbre = Vide | Un of int * char | Deux of int * arbre * arbre
let arbre_derivation g u : arbre option =
let n = String.length u in
let k = g.n_variables in
let t = Array.make (n+1) [||] in
for i = 0 to n do
t.(i) <- Array.make_matrix n k None
done;
if g.epsilon then t.(0).(0).(0) <- Some Vide;
let rec aux = function
| [] -> ()
| (i, a)::q ->
for d = 0 to n - 1 do
if u.[d] = a then t.(1).(d).(i) <- Some (Un (i, a))
done;
aux q
in
aux g.regles1;
for l = 2 to n do
for d = 0 to n - l do
let rec aux = function
| [] -> ()
| (i, j, k)::q ->
for p = 1 to l - 1 do
if d + p < n
then match t.(p).(d).(j), t.(l-p).(d+p).(k) with
| Some a1, Some a2 -> t.(l).(d).(i) <- Some (Deux (i, a1, a2))
| _ -> ()
done;
aux q
in
aux g.regles2
done
done;
t.(n).(0).(0);;
(* 10 *)
arbre_derivation g0 "aab";;
L'algorithme CYK permet de déterminer, par programmation dynamique, si un mot est généré par une grammaire non contextuelle.
Il permet aussi de trouver un arbre de dérivation pour ce mot, par analyse syntaxique ascendante (bottom-up : des feuilles à la racine).
L'algorithme prend en entrée une grammaire en forme normale de Chomsky, c'est-à-dire que toutes les règles sont de la forme ou ou , où , et .
On note les variables, où .
Équation de récurrence
Cette partie est à faire sur papier.
Soit . On veut savoir si . Pour cela, on va calculer, pour , et :
- À quelle condition a t-on ?
- Que vaut ?
- Que vaut ?
Pour avoir , il faut que avec et .
- En déduire une équation de récurrence pour .
Solution (pour vérifier votre réponse)
- En déduire un algorithme en pseudo-code pour remplir le tableau . Quelle est sa complexité ?
Implémentation
On définit le type suivant :
type grammaire = {
n_variables : int;
regles1 : (int * char) list;
regles2 : (int * int * int) list;
epsilon : bool
}
Les variables sont numérotées de 0 à n_variables - 1 (int) et les lettres sont des caractères (char).
regles1 contient un couple pour chaque règle et regles2 contient un triplet pour chaque règle .
epsilon est vrai si .
- Définir une variable
g0représentant la grammaire suivante :
- Écrire une fonction
init : grammaire -> string -> bool array array arrayqui prend en argument une grammaire et un mot et renvoie un tableautinitialisé pour les cas de base et .
init g0 "aba";;
- : bool array array array =
[|[|[|false; false; false; false; false|];
[|false; false; false; false; false|];
[|false; false; false; false; false|]|];
[|[|false; true; false; false; true|];
[|true; false; true; false; false|];
[|false; true; false; false; true|]|];
[|[|false; false; false; false; false|];
[|false; false; false; false; false|];
[|false; false; false; false; false|]|];
[|[|false; false; false; false; false|];
[|false; false; false; false; false|];
[|false; false; false; false; false|]|]|]
-
Écrire une fonction
cyk : grammaire -> string -> boolqui prend en argument une grammaire et un mot et renvoie vrai si le mot est dans le langage de la grammaire, en utilisant l'algorithme CYK décrit en première partie. -
Justifier que
g0engendre le langage et tester la fonctioncyksur quelques exemples.
cyk g0 "aab";;
- : bool = true
cyk g0 "aabb";;
- : bool = false
cyk g0 "abaa";;
- : bool = true
cyk g0 "aababa";;
- : bool = false
Arbre de dérivation
On définit le type suivant pour modéliser un arbre de dérivation :
type arbre = Vide | Un of int * char | Deux of int * arbre * arbre
où :
Videcorrespond à une dérivation ,Un (i, a)correspond à une dérivation ,Deux (i, j, k)correspond à une dérivation .
On rappelle que le type option est défini nativement en OCaml par :
type 'a option = None | Some of 'a
On adapte alors l'algorithme CYK avec t : arbre option array array array où t[l][d][i] contient un arbre de dérivation pour s'il en existe un, None sinon.
- Écrire une fonction
arbre_derivation : grammaire -> string -> arbre optionqui prend en argument une grammaire et un mot et renvoie un arbre de dérivation pour ce mot si celui-ci est dans le langage de la grammaire ouNonesinon.