Vous êtes sur la page 1sur 47

Exercices Corrigés

Module GIM23S04
Théorie des langages et compilation

4ème Année Génie informatique

Redouane Ezzahir

r.ezzahir@uiz.ac.ma

2014/2015

mis-à-jours 2020

Table des matières

TD N° 1 3

TD N° 2 5

TD N° 3 7

TP N° 1 : Expressions régulières 8

TP N° 2 : Automates 13

Correction TD N° 1 19

Correction TD N° 2 22

Correction TD N° 3 28

Correction TP N° 1 : Expressions régulières 31

Correction TP N° 2 : Automates 36
TD N° 1
Exercice 1

1. Prouver que (ρ implique σ) si et seulement si( ρ ∪ σ = E×E).


2. Reprendre la rela=on ρ définie dans la ques=on 1. Construire la rela=on ρ.ρ.
3. Montrer qu’une rela=on ρ sur E est transi=ve si et seulement si ρ.ρ ⊂ ρ.

Exercice 2
1. Montrer que V n con=ent |V |n éléments (on pourra u=liser la conven=on 00 = 1).

1. Soit ρ la rela=on sur l'ensemble {1, 2, 3, 4, 5, 6} définie par le


graphe de la figure ci-contre. Tracer les graphes des rela=on ρ+ et
ρ∗

2. Définir un algorithme qui, étant donnée une rela=on sur un


ensemble fini, calcule sa fermeture transi=ve.

Exercice 3

1. Déterminer les nombres maximal et minimal de sous-chaînes, de préfixes et de suffixes


d’un mot de longueur donnée.

2. Montrer que toute chaîne sur {a, b}, de longueur supérieure ou égale à 4, admet deux
occurrences consécu=ves d’une même sous-chaîne non vide.

Exercice 4

1. Prouver que, pour tout L ∈ V∗, ∅.L=L.∅ =∅.


2. Etudier le monoïde ⟨P(V∗),.,{ε}⟩ quand V = ∅ .

Remarque Il ne faut pas confondre le langage vide ∅,qui ne con=ent aucun mot, avec le langage
{ε}, qui con=ent le mot vide ε. Le premier est l’élément absorbant et le second est l’élément neutre
de la concaténa=on des langages.

Exercice 5
1. Montrer que les égalités suivantes sont vraies pour tout langage L :
• (L∗)∗ = L∗
• (ε+L)∗=L∗
• L .L = L
2. Montrer que, pour tout langage L, M, N et P, si L⊂M et N⊂P ,alors L∗⊂M∗et L.N ⊂ M.P
(en d’autres termes, l’étoile et la concaténation sont des opérations croissantes.)
3. Montrer que les égalités suivantes sont vraies pour tous langages L et M
— (L .M ) =(L+M)

— (L.M)∗L = L.(M.L)∗
— (L.M+L) .L=L.(M.L+L)*

Montrer que l’égalité suivante nést pas valide : (L + M)∗ = L∗ + M∗
4. Soit V={0,1}. Donner les définitions des langages suivants en seservant des opérations
de concaténation, d’union et de l’étoile :
• (a)  L'ensemble des chaînes sur V contenant au moins une occurrence de 0.

• (b)  L'ensemble des chaînes sur V de longueur impaire.

• (c)  L'ensemble des chaînes sur V ne contenant pas deux occurrences


consécutives de 0, ni deux occurrences consécutives de 1.

Techniques de compilation
4ème année G. Informatique
Ecole Nationale des TD 1 Prof. Redouane Ezzahir
Sciences Appliquées Agadir
Université Ibn Zohr TD N° 2

Ex. 1 — Démontrez, à l’aide de la définition inductive des langages réguliers, que les deux
langages suivants sont réguliers (l’alphabet considéré est ⌃ = {0, 1}):
1. L’ensemble des mots composés d’un nombre arbitraire de 1, suivis de 01, suivis d’un
nombre arbitraire de 0.
2. L’ensemble des nombres binaires impairs.

Ex. 2 — Langages non réguliers


1. Démontrez que tout langage fini est régulier.
2. Le langage L = 0n 1n |n = 0, 1, 2, ... est-il régulier? Expliquez.

Ex. 3 — Caractériser les langages reconnus par les ER suivantes :


•E1 : (a + b)* -E2 : (a + b)(a + b + 0 + 1)ú -E3 : (aa)*b(bb)*
•E4 : 0(0 + 1)*0 -E5 : ((e + 0)1*)* -E6 : 0*10*10*

Ex. 4 — Donner une expression rationnelle pour :


1. Le langage contenant les mots contenant un nombre pair de 0 et un nombre impair de 1.
2. Le langage ne contenant pas de mot avec le facteur 011

Ex. 5 — Simplifier les expressions rationnelles suivantes:


•E1 : (a*b*)*(a*b*)* -E2 : ab((ab*)* +(ba*)*)ba -E3 : (a* + b*)*
•E4 : (aa*ba + aaba + aba + ba) -E5 : ((a + aaaa)*b*)*

Ex. 6 — Donnez une expression régulière (définis sur l’alphabet = {0, 1}) qui accepte chacun
des langages suivants :
1.Toutes les chaînes qui se terminent par 00.
2.Toutes les chaînes dont le 10ème symbole, compté à partir de la fin de la chaîne, est un 1.
3.Ensemble de toutes les chaînes dans lesquelles chaque paire de 0 apparaît devant une paire
de 1.
4.Ensemble de toutes les chaînes ne contenant pas 101.
5.Tous les nombres binaires divisibles par 4

Ex. 7 — Determiniser les automates suivants :

automate 1 automate 2

1
Ex. 8 — Construire un automate reconnaissant l’expression rationnelle (a+b)*aa(a+b)*, le
déterminiser si nécessaire.
Ex. 9 — Determiniser et minimiser l’automate suivant :

Ex. 10 — Donner, pour chacun des langages suivants, un automate qui le reconnaît.
•L1 = { a*bb*}, L2 = { aa*bb*}
•L3 = { mots dans {a,b}* commençant par n b, n > = 0, finissant par un nombre pair de a}
•L4 = { mots dans {a,b}* commençant par ab et finissant par bb}
•L5 = { mots engendres par aa*bb* de longueur impaire}
•L6 = { mots dans {a,b}* n’ayant pas aa comme facteur}
•L7 = { mots dans {a,b}* de longueur paire} L8 = { mots dans {0,1}* dont l’écriture en
base 2 est un multiple de 3}

Ex. 11 — A quelle phase de la compilation peut-on détecter les erreurs suivantes?


•Identificateur mal formé en java ?
•Conflit de type sin("a")
•Variable non déclarée
•Commentaire non fermé
•Parenthèse non fermée
•Begin non fermé
•Mot réservé (if par exemple) utilisé comme identificateur
•Non-conformité entre le nombre de paramètres formels et effectifs

2
TD N° 3

 Université Ibn Zohr Compilation TD N°2 2019/2020
ENSA Agadir R. EZZAHIR

I) Grammaire de réécriture
Soit G = {{a, b, c}, {S, A, B}, S, P} la grammaire définie par les regles suivantes :
S → aAbc A → aABbc A→ a B → bc
Questions :
1. Donner les dérivations gauches des quatre chaines les plus courtes du langage engendré par
la grammaire G.
2. Donner la forme des chaines de ce langage.
3. Démontrer la forme précédente par récurrence sur les chaines générées par A.

II) Automate FN et Expression régulière


1. En utilisant la table de transition de l’automate non déterministe ci-dessous, donner celle
d’un automate déterministe reconnaissant le meme langage.
2. Donner l’expression réguliere définissant le meme langage.

II) Minimisation d'automate

Soit l'automate

1. Dire pourquoi A n’est pas déterministe. Donner, sans justification, une expression réguliere
équivalente.
2. Déterminiser A et représenter le graphe de l’automate déterministe D obtenu.
3. Minimiser l’automate D apres l’avoir éventuellement complété. Dessiner l’automate obtenu.
4. Minimiser l’automate suivant
TP N° 1 : Expressions régulières

1. Rappel
Les expressions régulières sont des suites de caractères permettant de faire des sélections. Elles fonctionnent
avec certaines commandes comme grep.

Les différentes expressions régulières sont : ^ début de ligne

. un caractère quelconque
$ fin de ligne
x* zéro ou plus d’occurrences du caractère x
x+ une ou plus occurrences du caractère x
x? une occurrence unique du caractère x
[...] plage de caractères permis
[^...] plage de caractères interdits
\ {n\} pour définir le nombre de répétition n du caractère placé devant

Exemple déxpression [a-z][a-z] * cherche les lignes contenant au minimum un caractère en minuscule. [a-z]
caractère permis, [a-z]* recherche d’occurrence des lettres permises.

Léxpression ^[0-9]\ {4\}$ a pour signification, du début à la fin du fichier $, recherche les nombres[0-9] de 4
chiffres \ {4\}.

1.1 La command grep d’Unix


La commande grep permet de rechercher une chaîne de caractères dans un fichier. Les options sont les
suivantes :

-v : affiche les lignes ne contenant pas la chaîne


-c : compte le nombre de lignes contenant la chaîne
-n : chaque ligne contenant la chaîne est numérotée
-x : ligne correspondant exactement à la chaîne
-l : affiche le nom des fichiers qui contiennent la chaîne

Créer un fichier carnet-adresse.text contenant les lignes suivant : Alhmed:29:0627523698:Casa

Salim:18:0532653625:Fès Kamal:52:0535689625:Fès Fitima:30:054214452:Casa


Younes:32:0623344433:Agadir

En utilisant grep, écrire un script shell qui permet de :


1. afficher tous les lignes commençant par les caractères compris entre F et K. 2. afficher es contacts de Casa.
3. afficher les contacts ayant un N° de téléphone fixe.

1.2 Java et les expressions régulières

Depuis la version 1.4, Java dispose d’un moteur déxpressions régulières puissant et très complet. Cést un moteur
basé sur un automate fini non déterministe, et il est donc possible pour certaines expressions régulières de
requérir un très long temps de traitement même si le moteur déxpressions régulières de Java est très rapide en
général. De plus, il supporte les quantificateurs avides, paresseux et possessifs et la plupart des autres fonctions
dont nous avons discuté jusqu’à présent.

- Pattern : elle représente un motif défini par une expression régulière ; la documentation de la classe en question
comprend un rappel très utile sur les expressions régulières.
Les classes qui nous concernent sont dans le paquetage java.util.regex. Les deux principales classes sont : -
Matcher : cette classe permet de visiter les différentes occurrences des motifs.


1.2.1 Programmation de base avec les expressions régulières

La classe « java.lang.String » en Java contient déjà un support assez avancé pour les expressions régulières. On
peut ainsi vérifier la concordance d’un motif, séparer une chaîne de caractères en sous-chaînes en utilisant des
motifs et, finalement, utiliser la fonction rechercher/remplacer.

Étant donné une chaîne de caractères en Java, la méthode « matches » permet de vérifier si elle correspond à une
expression régulière. Dans cet exemple, la variable « test » prendra la valeur « vrai ».

String s = "La vie";


boolean test = s.matches("La.*");

On peut aussi séparer une chaîne de caractères en sous-chaînes dans lesquelles les points de segmentation sont
déterminés par une expression régulière. Dans léxemple suivant, la variable « résultat » aura pour valeur ("La
vie est belle", "Jean", "il ne m’aime pas").

String s = "La vie est belle. Jean, il ne m’aime pas."; String[] resultat =
s.split(",|\\.");

Finalement, la fonction rechercher/remplacer avec des expressions régulières fait appel à la méthode «
replaceAll ». Pour remplacer les chiffres par des « X » dans une chaîne de caractères, on procède ainsi :

String s = "Mon numéro de carte de crédit est le 4243243243."; String résultat =


s.replaceAll("\\d","X");

Le résultat sera « Mon numéro de carte de crédit est le XXXXXXXXXX. ».

Notez qu’un objet String, en Java, est immuable : la méthode « replaceAll » ne modifie donc pas l’objet, mais
présente plutôt une version modifiée de l’objet.

1.2.2 Programmation avancée avec les expressions régulières


Outre l’objet java.lang.String, il existe un paquetage dédié aux expressions régulières qui permet, par exemple,
de ne compiler léxpression régulière qu’une seule fois en utilisant un objet de classe « Pattern ». Quand on doit
traiter plusieurs chaînes de caractères avec les expressions régulières, il est sans doute préférable d’utiliser ce
paquetage.

Aller chercher le paquetage


Il faut toujours commencer par :
import java.util.regex.*; quand on veut utiliser les expressions régulières en Java.

Lire une expression régulière


La première étape est toujours la même : il faut « compiler » léxpression régulière pour en faire un objet Java :

Pattern pattern = Pattern.compile("\\w+");

Appliquer léxpression à une chaîne de caractères


On applique une expression régulière à une chaîne de caractères avec la méthode « matcher ».
Matcher matcher = pattern.matcher("La vie est belle");

Tester si la chaîne est le motif


Une fois un objet « Matcher » obtenu, on peut tester si la chaîne de caractères est un motif correspondant à
léxpression régulière avec la méthode « matches » :

boolean correspond = matcher.matches();

Dans notre cas, la réponse est négative parce que notre chaîne nést pas une lettre répétée plusieurs fois. Tester la
présence du motif
Une fois un objet « Matcher » obtenu, on peut tester si le motif se trouve dans la chaîne de caractères avec la

méthode « find ».

if(trouve){
System.out.println("chaine corespond au motif est trouve");
String texte = matcher.group();
System.out.println("Trouvé \""+texte );
}

Dans notre cas, la réponse serait positive parce qu’il y a la lettre « l » répétée dans notre chaîne. La méthode «
find() » a aussi comme effet de trouver la prochaine occurrence du motif dans la chaîne. On peut ensuite aller
chercher la position et la nature du motif avec les méthodes « group », « start » et « end » :

String texte = matcher.group();

int debut = matcher.start();

int fin = matcher.end();

Pour repérer toutes les occurrences, il suffit d’écrire une boucle comme ceci :

while (matcher.find()) {

texte = matcher.group();
debut = matcher.start();
fin = matcher.end();
System.out.println("Trouvé \""+texte+"\"à la position "+debut);
} }

La fonction recherche/remplace
Supposons que l’on veuille remplacer toutes les lettres répétées par le symbole « * ». Ce résultat est aisément
obtenu avec

la méthode « replaceAll » : matcher.replaceAll("*").La seule mise en garde est que si on tente de remplacer un
texte qui contient le symbole du dollar, il faut l’indiquer comme ceci : « \$ ».

Supposons que l’on veuille remplacer toutes les lettres répétées par une seule occurrence de la même lettre, de
sorte que « belle » devienne « bele » ; on peut obtenir ce résultat avec la méthode « replaceAll ». Il faut alors
utiliser les parenthèses de capture et la convention selon laquelle « $1 » fait référence à la première parenthèse
de capture rencontrée et ainsi de suite.
pattern = Pattern.compile("(l)+");
matcher = pattern.matcher("La vie est belle");
résultat = matcher.replaceAll( "$1" );
System.out.println("replaceAll $1 Donne : "+résultat);

1.2.3 Deux exemples de programmes Java

Premier exemple : pour tester les expressions régulières


Ce premier programme vise à vous permettre de tester votre compréhension des expressions régulières. Il vérifie
que léxpression régulière correspond bien au texte donné, et il vous donne la liste des motifs correspondants
trouvés. Il s’utilise de cette manière :

java Motif "foo.*" "foolalala"

Voici le code source : Motif

Pour tester les limites du traitement des expressions régulières en Java, vous pouvez essayer léxemple suivant :

java Motif "^(\\s*foo\\s*)*$" "foo foo foo foo foo"+


" foo foo foo foo foo foo foo foo foo foo foo"+
" foo foo foo foo foo foo foo foo foo fo"

Second exemple : un programme simulant grep


On peut écrire un programme qui ressemble beaucoup à grep en utilisant Java (Regex). Le programme
appliquera alors une expression régulière à chaque ligne d’un programme donné et il n’affichera une ligne que si
elle contient un motif correspondant.

Pour trouver toutes les lignes dans le fichier toto.txt qui contiennent deux guillemets, il suffit de faire :

java Regex ’".*"’ toto.txt

On peut aussi traiter plusieurs fichiers ainsi :

java Regex ’".*"’ toto1.txt toto2.txt

Pour traiter un ensemble de fichiers dans le répertoire courant, on peut aussi utiliser l’astérisque :

java Regex ’".*"’ *.txt


Pour obtenir plus de détails sur le traitement, il suffit d’ajouter le drapeau « -v » juste après « Regex » sur la
ligne de commande :

java Regex -v ’".*"’ *.txt

Finalement, il est toujours possible de rediriger le résultat vers un fichier qu’on pourra ouvrir plus tard avec un
éditeur de texte comme Bloc-notes, comme ceci :

java Regex ’".*"’ *.txt > resultats.txt Zip - 1 ko

1.3 Exercice : (Devoir à rendre le jours de TP)


En utilisant les classes Pattern et Matcher du paquetage java.util.regex (Javadoc ici ), Écrire un programme qui
reconnaît dans les lignes d’un fichier passé en argument les URL HTTP de la forme http://
user:passwd@machine:port/path et qui affiche, s’ils existent, l’utilisateur, le mot de passe, la machine, le port et
le chemin. La syntaxe des expressions rationnelles Java est décrite dans la documentation de la classe Pattern.
Celle-ci est proche de celle vues en cours. Pour lire un fichier ligne par ligne, utiliser la méthode readLine() de la
classe BufferedReader. Vous pourrez utilisez le fichier déxemple exempleURLExtractor.txt pour tester votre
programme.
TP N° 2 : Automates

1. AUTOMATES FINIS NON-DÉTERMINISTES À EPSILON TRANSITIONS

Le but de cet exercice est d'apprendre à manipuler les automates. Les automates sont principalement utilisés
pour l'analyse lexicale, et servent à représenter/implementer les expression régulières. Nous allons écrire deux
classes au cours de ce TD : State et Automaton.

Les états sont codés par la classe State. L'état poubelle est représenté par l'objet null.
Un état contient l'ensemble des états vers lequel mène une epsilon transition.
En plus un état contient la table de transition. Cést une table dont les indices sont des caractères, et les entrées
les états destinations.
Les ensembles d'états sont codés par un arbre AVL.
La classe Automaton réalise un automate fini non-déterministe à epsilon transitions. Elle contient l'ensemble de
ses états, l'état initial et l'ensemble des états finaux.

REMARQUE 1.1 - TREESET<STATE>

On va représenter les ensembles d'états par des arbres AVL. La classe TreeSet réalise un ensemble d'objets par
des arbres AVL. Nous auront besoin des méthodes suivantes

a.add(s) permet d'ajouter un état s à l'ensemble a.


a.addAll(b) permet de remplacer l'ensemble a par l'union avec un autre ensemble b donné. a.contains(s) permet
de tester si l'état s est un élément de l'ensemble a.
a.isEmpty() permet de tester si a est vide.
for (State s: a) permet d'itérer sur tous les états s de l'ensemble a.

QUESTION 1 - STATE
Pour que la classe TreeSet sache comparer les états il faut une méthode compareTo à la classe State. L'appel à
s.compareTo(t) doit retourner un entier <0, l'entier 0 ou un entier >0 suivant que s<t, s=t ou s>t. On va
tout simplement utiliser l'ordre sur les identifiant et donc retourner id-t.id.

Pour que la classe TreeSet sache que State est munie d'une méthode compareTo, il faut déclarer que State
implémente l'interface Comparable<State>.

Écrivez maintenant la classe State qui représente les états. Munissez la des attributs/méthodes suivants : (pensez
à importer java.util.* pour TreeSet)

int id; // identificateur unique de l'état


State() // le constructeur pose id.
attribuer succéssivement 0,1,2,.. à chaque nouvelle création (utiliser un atribut static int nbState)
public int compareTo(State t) //comparer cet objet avec t
State transition[]; // la table de transition
void addTransition(char c, State next) // ajoute une transition pour la lettre c
TreeSet<State> epsilon; // l'ensemble des états cibles des transitions epsilon
void addTransition(State next) // ajoute une epsilon transition
static void epsilon_closure(TreeSet<State> set) // ajoute à l'ensemble set tous les états
atteignables par des epsilon-transitions.

Pour la méthode epsilon_closure notez qu'on ne peut pas modifier en Java l'ensemble qu'on est en train de
parcourir. Une solution serait alors de produire en une itération intérieure l'ensemble des états à ajouter à set.
Puis l'itération extérieure prendra fin quand cet ensemble sera enfin vide.
Testez en avec cette méthode.

public static void main(String []args) {



State []tab = {new State(), new State(), new State(), new State()};
tab[2].addTransition('x', tab[3]);

tab[2].addTransition(tab[0]);

tab[1].addTransition(tab[2]);

tab[0].addTransition(tab[1]);

for (State s: tab) {



for (State n: s.epsilon)
System.out.println(""+s.id+"->"+n.id); for (char c=0; c<256; c++)
if (s.transition[c]!=null) System.out.println(""+s.id+"-"+c+"->"+s.transition[c].id);
}
TreeSet<State> set = new TreeSet<State>(); set.add(tab[1]);
State.epsilon_closure(set); System.out.print("cloture(1) =");
for (State s: set) System.out.print(" "+s.id);
System.out.println(); }

Vous devriez avoir l'affichage suivant.

0->1

1->2

2->0

2-x->3

cloture(1) = 0 1 2

QUESTION 2 - AUTOMATON
Ecrivez la classe Automaton avec les attributs, méthodes suivants : (La méthode dump a besoin qu'on importe
java.io.*. )

TreeSet<State> states; // l'ensemble des états de l'automate


State initial; // l'état initial
TreeSet<State> accept; // l'ensemble des états acceptants
Automaton() // crée un automate qui ne reconnait rien du tout Automaton(TreeSet<State> _states,
State _initial, TreeSet<State> _accept) // crée un automate avec les valeurs données

static Automaton new_WORD(String word) // crée un automate qui reconnait un mot donné et rien d'autre

Ajoutez la méthode d'affichage suivante, qui écrit l'automate dans un fichier dont le
nom sera tmp??.dot où ?? est l'entier qui est passé en paramètre à dump.

 /** affiche l'automate comme un graphe en notation DOT
        ... enfin retourne une chaine qui correspond a l'affichage plutot
    */
    public void dump(String word, int i, Set<State> marked) {
        try {
            PrintWriter out = new PrintWriter(String.format("tmp%02d.dot",i));
            String label="";
            if (word!=null) {
                label=" label=\"";
                for (int j=0; j<=word.length(); j++) {
                    if (j==i)
                        label+='>';
                    else
                        label+=' ';
                    if (j<word.length())
                        label+=word.charAt(j);
                }
                label += "\";\n";
            }
            
            // imprimer tout ce qui est spécifique au graphe
            out.println("digraph {\n"+
                        " rankdir=LR;\n"+
                        label+
                        " init [shape=point];\n"+
                        " init->"+initial.id+";\n"      );
            
            // imprimer les états
            for (State s: states) {
                out.print(" "+s.id+" [");
                if (accept.contains(s))
                    out.print("shape=doublecircle");
                else
                    out.print("shape=circle");
                if (marked!=null && marked.contains(s))
                    out.print(",style=filled");
                out.println("];");
            }
            
            out.println();
            // imprimer les epsilon transitions
            for (State s: states) 
                for (State n: s.epsilon) 
                    out.println(" "+s.id+"->"+n.id+";");
            
            out.println();
            // imprimer les autres transitions 
            for (State s: states) {
                char  c1 = 0;
                State n1 = null;
                for (char c2=0; c2<=256; c2++) {
                    State n2 = (c2<256) ? s.transition[c2] : null;
                    if (n1!=n2) {
                        if (n1!=null) {
                            if (c1==0 && c2==256)
                                label = "\"?\"";
                            else if (c1==c2-1)
                                label = "\""+c1+"\"";
                            else
                                label = "\""+c1+"-"+(c2-1)+"\"";
                            out.println(" "+s.id+"->"+n1.id+"[label="+label+"];");
                        }
                        c1 = c2;
                        n1 = n2;
                    }
                }
            }
            out.println("}");
            out.close();
        } 
        catch (FileNotFoundException e) {
            throw new Error("fichier tmp??.dot n'a pas pu ^etre crée");
        }
    }
Testez avec ce code qui produit un fichier tmp00.dot.

static public void main(String args[]) { Automaton isa = new_WORD("isa");


isa.dump(null, 0, null); }

Puis dans une commande shell visualisez le résultat avec la commande dot -Tgif -o
tmp00.gif tmp00.dot && xv tmp00.gif

NB. pour intaller dot : sudo apt-get install graphviz



Vous devriez voir le graphe suivant, à renommage des sommets près.

QUESTION 3 - CONCATENATION, UNION, REPETITION


Ajoutez les fonctions suivantes. Ce nést pas grave si les paramètres sont modifiés.
Pour les deux premières, servez vous de new_WORD.

static Automaton new_EMPTY() // crée un automate qui reconnait le mot vide



static Automaton new_CHAR(char c) // crée un automate qui reconnait un caractère donné
static Automaton new_WILD() // crée un automate qui reconnait un caractère quelconque

static Automaton new_OR(Automaton a1, Automaton a2) // crée un automate qui reconnait
l'union des languages rec. par a1 et a2

static Automaton new_SEQ(Automaton a1, Automaton a2) // crée un automate qui reconnait
la concaténation des languages

static Automaton new_STAR(Automaton a) // crée un automate qui reconnait la répétition


du langage reconnu par a

Testez avec ce code.

static public void main(String args[]) {


Automaton a,b,c;
a = new_OR(new_WORD("cafe"),new_WORD("toi"));
b = new_SEQ(new_WORD("the", new_WORD("sur")); c = new_STAR(new_WORD("quoi"));
a.dump(null, 0, null); b.dump(null, 1, null); c.dump(null, 2, null);
}
QUESTION 4 - RECONNAISSANCE DE CARACTÈRES
Ajoutez une méthode boolean match(String word) qui teste si un mot donné est reconnu
par l'automate. Pour cela vous avez besoin d'une variable locale TreeSet<State>
current, qui contient l'ensemble des états courants, qui initialement ne contient que
la cloture par epsilon transitions de l'état initial. Puis sucessivement pour chaque
caractère c de la chaîne word vous mettez à jour current : Vous calculez un ensemble
next, composé de tous les états qu'on peut atteindre avec une transition avec c sur un
état s de current. Faites attention à ne pas mettre dans next l'état poubelle null.
Après chaque itération vous remplacerez current par la cloture transitive par epsilon
transitions de next. Si à la fin current contient un état acceptant, le mot word est
accepté.

Testez avec ce code.

final String[] strings = { "", "a", "ab", "ac",


"abc", "acb", "adc", "abcd", "abcde",
"abcdef", "abcdefe", « abcdefef »};
static void main(String [] args) {
Automaton a = a = new_SEQ(new_CHAR('a'),
new_OR( new_SEQ(new_WILD(), new_CHAR('c')), new_SEQ(new_WORD("bcd"),
new_STAR(new_WORD("ef")))));
for(String s: strings) {

System.out.println("matches " + s + " : " + a.match(s));
} }

Vous devriez avoir le résultat suivant :

java Automaton

matches : false

matches a : false
matches ab : false
matches ac : false
matches abc : true
matches acb : false
matches adc : true
matches abcd : true
matches abcde : false
matches abcdef : true
matches abcdefe : false
matches abcdefef : true
QUESTION 5 - GÉNÉRER UNE ANIMATION DE L'AUTOMATE
Maintenant à chaque itération appelez dump(word, i, current), où i et l'indice du
caractère traité dans word. Appelez une dernière fois après la boucle dump(word,
word.length(), current). De cette manière votre programme va générer différents
fichiers qui représentent le parcours de l'automate.

Le script shell make_image suivant :

#!/bin/bash
for f in tmp??.dot; do
dot -Tgif -o$f.gif $f
done

convert -loop -1 -delay 180 tmp??.dot.gif g.gif rm tmp??.dot.gif tmp??.dot
xv g.gif
• #  autre visualiseur:
• #  firefox g.gif #
• #  sous MacOS
• #  open g.gif

vous permet de transformer les fichiers produits par le programme en une image GIF
animée. On léxécute avec bash make_image. Vous devriez avoir le résultat suivant.
(Cést cool, non ?)
Correction TD N° 1
Exercice 1

Montrons d’abord que si ρ implique σ, alors ρ ∪ σ = E × E . Comme ρ et σ sont des


relations sur E, il est clair que ρ ⊆ E et σ ⊆ E, on a donc ρ∪σ ⊆ E. Il s’agit maintenant de
prouver que E ⊆ ρ∪σ. Soient x,y ∈ E, il faut montrer que (x,y) ∈ ρ∪σ. Si (x,y) ∈ ρ, le résultat
est démontré. Sinon, on a nécessairement (x, y) ∈ (ρ), et comme (ρ) = ρ, on en déduit que
(x, y) ∈ ρ. Comme ρ implique σ par hypothèse, on a bien le résultat.

Montrons maintenant que si ρ∪σ = E ×E, alors ρ implique σ. Ceci est évident : si (x,y) ∈ ρ,
alors par définition (x, y) ∈/ ρ et donc, nécessairement (x, y) ∈ σ, d’où le résultat.

Exercice 2

Soit V un vocabulaire non vide. Nous allons montrer la propriété par induction sur n.

Cas de base: V0 ={ε}, on a donc |V0 |=1=(#V)0.

Induction Soit n ∈ N, et supposons que la propriété est vérifiée.

n+1 n
V = {w1…wn+1|∀i wi ∈V} = {wwn+1|w ∈ V , wn+1 ∈ V}

D’où #(Vn+1) = #(Vn)#(V) = (#V)n(#V) = (#V)n+1

Conclusion Par propriété de récurrence on peut donc conclure :

∀n∈N,#(Vn)=(#V)n

Exercice 3

1. SoitV un vocabulaire non vide, n ∈ N et w ∈ V .

Remarquons tout d’abord que ∀i ∈ {0...n} il existe toujours au moins une sous- chaine/un
préfixe/un suffixe de longueur i. De plus en notant a un élément de V , la chaine w = a . . .
a contient exactement une sous-chaine/un préfixe/un suffixe de longueur i, ∀i. On en
déduit que :

toute chaine de longueur n + 1 admet au minimum n sous-chaines/préfixes/suffiIdée pour


le maximum : ∑ n = (N +1)N /2

2. Commençons par démontrer le résultat pour une chaine de longueur 4 exactement.



Si la chaine contient deux éléments égaux consécutifs, alors la sous chaine de longueur 1
correspondante est non vide est apparait de manière consécutive dans la chaine. Sinon, la
chaine est forcément de la forme abab ou baba est contient donc une sous chaine de
longueur 2 (ab ou ba) qui apparait de manière consécutive dans la chaine.

On étends le résultat à toute chaine de longueur supérieure ou égale à 4 en remarquant
quélle contient toujours une sous chaine de longueur 4, et que toute sous chaine d’une
sous chaine d’une chaine et également une sous chaine de cette chaine.

Exercice 4

1. Soit L ∈ P(V∗)

∅.L= {w∈V∗|w=uv,u∈∅,v∈L}=∅ car il n'existe aucun u tel que u ∈∅ De meme, on
montre que L.∅ = ∅

2. Si V = ∅ on a :

Exercice 5

1. Soit E un ensemble de langages V fermé par la concaténation, l’étoile, et la réunion, et


contenant les languages finis.

E contenant les langages finis il contient alors par fermeture l'ensemble des langages
réguliers. D’où {Langages Réguliers} ⊆ E 


2. Soit L1 un langage contenant L et ε, stable par concaténation.



On note P(n) = ”∀k ≤ n,Ln ⊂ L1” et nous allons raisonner par récurrence.

Initialisation : L0 = {ε} d’où P (0).



Récurrence On suppose P (n).

L1 étant stable par concaténation, et contenant L et {Lk | k ≤ n}

on a

L.{Lk |k ≤ n} = {Lk |k ≤ (n+1)} ⊂ L1 soit P(n+1).

Par principe de récurrence, L∗ ⊂ L1, ce qui achève la démonstration. 


3.
— (L∗)∗ = ⋃n≥0(L∗)n d’où en particulier L∗ ⊂ (L∗)∗ (n=1).
Soit w∈(L∗)n Alors w=w1w2…wn tq ∀i wi ∈ L .
Par définition on a
∀wi∃ni, tq w=v(i,1)...v(i,ni) et ∀jvj ∈L.
(∑nn) ∗ Alors w = v(1,1) ...v(1,n1) …v(n,1) v(n, nn) ∈ L (∑ i=1 ni )⊂ L* .

D’où (L∗)∗ ⊂ L∗ et par double inclusion (L∗)∗ = L∗

— Si ε ∈ L alors (L+ε) = L est on a bien le résultat demandé.

Si ε ∉ L alors comme L ⊂ (L+ε)

on a trivialement L* ⊂ (L+ε)* .

De plus, soit w = v1 ...vn ∈ (L+ε)∗,tq∀ivi ∈ (L+ε)

En supprimant tous les epsilons, on obtient

w=vi...vm ∈ Lm ⊂ Ln avec 0< m ≤n

ou bien w= ε ∈ L0 ⊂ L* .

D’où (L + ε)∗ ⊂ L∗, et donc par double inclusion: (L + ε)∗ = L∗

— Soit w ∈ L∗ Alors w=ε.w ∈ L0 .L∗ ⊂ L∗ .L∗ .D’où L∗ ⊂ L∗ .L∗

Soit w ∈ L* .L* , tq w= uv, u ∈ Ln , v ∈ Lm .

Alors w ∈ Ln+m ⊂ L∗ ,d’où L∗ .L∗ ⊂ L∗ .

Par double inclusion on a alors L∗ ⊂ L∗ .L∗

4. à faire
5. à faire
6. (a) Avec L = ∅ , et M != ∅ l’équation se réécrit ∅ = M (∅ élément absorbant) qui n’as
pas de solution. 

(b) Soit L et M deux languages. On pose X0 = L∗M.

On a alors L.X0 +M = L.L M + M

= L.LM + ε.M = L.LM+ L0.M=(L.L + L0).M=L .M

Soit, X0 =L.X0+M, pour X0 =L∗M


Correction TD N° 2
Correction TD N° 3

 Compilation TD N°2
I. Grammaire de réécriture :
Soit G = {{a, b, c}, {S, A, B}, S, P} la grammaire définie par les règles suivantes :

S aAbc , A aABbc , A a,B bc

1- les dérivations gauches des quatre chaines les plus courtes du langage
engendré par la grammaire G :
1.1. S aAbc aabc a2bc
1.2. S aAbc aaABbcbc aaaBbcbc aaabcbcbc a3(bc)3
1.3. S aAbc aaABbcbc aaaABbcBbcbc aaaaBbcBbcbc aaaabcbcBbcbc
aaaabcbcbcbcbc a4(bc)5
1.4. S aAbc aaABbc aaaABbcBbcbc aaaaABbcBbcBbcbc
aaaaaBbcBbcBbcbc aaaaabcbcBbcBbcbc aaaaabcbcbcbcBbcbc
aaaaabcbcbcbcbcbcbc a5(bc)7
2- la forme des chaines de ce langage : an(bc)2n-3.
3- Démontrer la forme précédente par récurrence sur les chaines générées par
A:
Pour n 2 , la forme est vraie. On suppose que la forme an(bc)2n-3 est vraie pour toute n 2
, et on montre an+1(bc)2(n+1)-3 = an+1(bc)2n-1
S aAbc aa ABbcbc aaaABbcBbcbc aaaaABbcBbcBbcbc
aaaaaABbcBbcBbcBbcbc aaaaaabcbcbcbcbcbcbcbcbc

...
an-1A(Bbc)n-2 bc
an-1 aABbc (Bbc)n-2 bc
an+1(bc)2n-1
II. Automate FN et Expression régulière:

1- En utilisant la table de transition de l a tomate non déterministe ci-dessus, donner


celle d n a oma e d erminis e reconnaissant le même langage :
Table de transitions :
a b ɛ ɛ-fermeture
S0 S1 ∅ ∅ S0
S1 S4 S3 S2 {S1, S2}
S2 ∅ S4 ∅ S2

S3 S2 ∅ ∅ S3
S4 ∅ ∅ S0 {S0, S4 }
Déterminisation :
a b
S0 {S1, S2} ∅
{S1, S2} {S0, S4 } { S0 , S3, S4 }
{ S 0 , S4 } {S1, S2} ∅
{ S0 , S3, S4 } {S1, S2} S0
Représentation de l a oma e :

2- Donne l e p e ion g lière définissant le même langage :


ER(A) : a(ɛ|aa|ba)*
III. Minimi a i n d a ma e :
Soi l a oma e :

1- Di e po oi A n e pa d e mini e.
Donner, sans justification, une expression régulière équivalente :
- A n e pa déterministe car on a pour un mot w deux transitions possibles
pa i de l état q0.
Par exemple : à partir de q0 pour la transition 1 on peut passer à q1 ou
e e l a 0.
- ER(A) : (0|1)*101(0|1)*.
2- Déterminiser A et représenter le graphe de l a oma e d e mini e D ob en :
Soit le table de transition :
Etat/transition 0 1
q0 q0 { q0 , q1 }
q1 q2
q2 q3
q3 q3 q3
Soi le no ea ablea po d e mini e l a oma e :
0 1
{ q0 } { q0 } { q0 , q1 }
{ q0 , q1 } { q0 , q2 } { q0 , q1 }
{ q0 , q2 } { q0 } { q0 , q1, q3 }

{ q0 , q1, q3 } { q0 , q2 , q3 } { q0 , q1, q3 }
{ q0 , q2, q3 } { q0 , q3 } { q0 , q1, q3 }
{ q0 , q3 } { q0 , q3 } { q0 , q1, q3 }

Donc l a oma e D d e mini e de A e la i an e :

3- Minimiser l a oma e D ap l a oi en ellemen compl . De ine l a oma e ob en :


0
1 X
2 X X
3 X X X
4 X X X
5 X X X
E X X X X X X
0 1 2 3 4 5 E

4- Minimi e l a oma e i an :

1
2 X
3 X X
4 X X X
5 X X X
6 X X X X X
E X X X X X X
1 2 3 4 5 6 E

L’automate minimal est : 



Correction TP N° 1 : Expressions régulières

package regex;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExpReg {

public static void main(String[] args){

//**************************************//
//********** teste 1 *******************//
String s = "La vie";
boolean test = s.matches("La.*");
if(test)
System.out.println("l'ER: La.* match (filtre ou correspond)
bien a : "+ s);
//*************************************//
System.out.println();
//********** teste 2 ************************//
s = "La vie est belle. Jean, il ne m’aime pas.";
String[] resultat = s.split(",|\\.");
System.out.println("Le découpage de la chaine : \""+ s +
"\" \nEn utlisant l'ER (,|\\.) comme filtre du
séparateur est :\n"+
Arrays.toString(resultat).replace(',', '\n'));
//********** teste 3 ***********************/
System.out.println();
s = "Mon numéro de carte de crédit est le 4243243243.";
String résultat = s.replaceAll("\\d","X");
System.out.println("Remplacement les chiffres par X dans la chaine :
\n"+s);
System.out.println("Donne : "+résultat);
System.out.println();
//**************************************************************
// Package Regex de java
//*************************************************************
// compiler l’expression régulière pour en faire un objet Java
Pattern pattern = Pattern.compile("(\\w*\\s*)*");
//Appliquer l’expression à une chaîne de caractères
Matcher matcher = pattern.matcher("La vie est belle");
boolean correspond = matcher.matches();
if(correspond)
System.out.println("Regex1: match (filtre ou correspond) bien
a \"La vie est belle\"" );
else
System.out.println("Regex1: ne match (filtre ou correspond)
pas \"La vie est belle\""+
"essayez avec (\\w*\\s*)*");

System.out.println();
//
***************************************************************************//
pattern = Pattern.compile("\\w+");
matcher = pattern.matcher("La vie est belle");
boolean trouve = matcher.find();
if(trouve){
System.out.println("chaine corespond au motif est trouve");
String texte = matcher.group();
System.out.println("Trouvé \""+texte );
}

//
**************************************************************************//
System.out.println();
while (matcher.find()) {
String texte = matcher.group();
int debut = matcher.start();
int fin = matcher.end();
System.out.println("Trouvé \""+texte+"\"à la position "+debut+
" fin="+fin);
}

//
****************************************************************************
/*
* Supposons que l’on veuille remplacer toutes les lettres répétées,
par
* une seule occurrence de la même lettre, de sorte que « belle »
devienne « bele » ;
* on peut obtenir ce résultat avec la méthode « replaceAll ».
* Il faut alors utiliser les parenthèses de capture et la
convention selon
* laquelle « $1 » fait référence à la première parenthèse
* de capture rencontrée et ainsi de suite.
*/
/* notons la parenthèse capturante */
pattern = Pattern.compile("(l)+");
matcher = pattern.matcher("La vie est belle");
// le $1 fait référence à la première lettre
résultat = matcher.replaceAll( "$1" );

System.out.println("replaceAll $1 Donne : "+résultat);


System.out.println();
//
****************************************************************************
String aString = "77433211";
aString = aString.replaceAll("(\\d)\\1+", "$1");

System.out.println("replaceAll avec $1 dans String \n "


+ "String aString = \"77433211\"; \n"+
" aString = aString.replaceAll(\"(\\d)\\1+\", \"$1\");
\n" +
"Donne : " + aString);
//
****************************************************************************

System.out.println();
System.out.println("====================adresse IP
==========================");
pattern = Pattern.compile("\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\
\d{1,3}\\b");
matcher = pattern.matcher("192.21.45.36");
correspond = matcher.matches();
if(correspond)
System.out.println("IP 192.21.45.36 ok");
matcher = pattern.matcher("999.999.999.999");
correspond = matcher.matches();
if(correspond) {
System.out.println("Mais l'adrsse non IP : 999.999.999.999 est
aussi filtré");
}
System.out.println();
System.out.println("====================limite de regex
==========================");
//
***************************************************************************//
System.out.println("ctrl + chift + c pour décommeter les lignes
suivantes:");
// pattern = Pattern.compile("^(\\s*foo\\s*)*$"); ;
// String foo = "foo foo foo foo foo"+
// " foo foo foo foo foo foo foo foo foo foo foo"+
// " foo foo foo foo foo foo foo foo foo fo";
// matcher = pattern.matcher(foo);
// correspond = matcher.matches();
// if(correspond) {
// System.out.println("ok");
// }
//
}
}
Solution: 1.1 et 1.3
package regex;

/** *************
* Utilisation:
*
* java Regex monexpression mesfichiers
*
* par exemple:
*
* java Regex '".*"' toto.txt
*
* donne tout texte entre parantheses dans le fichier toto.txt. Le
* traitement se fait ligne par ligne. Pour avoir le texte sans les
* parantheses, faire
*
* java Regex '"(.*)"' toto.txt
*
* Pour avoir plus d'information, vous pouvez utiliser le drapeau "-v"
* comme ceci :
*
* java Regex -v '".*"' toto.txt
*
**/

import java.util.Arrays;
import java.util.regex.*;
import java.io.*;

public class Regex {

public static void main(String[] args) {


boolean verbose = false;
int pos = 0;
if(args[pos].equals("-v")) {
verbose = true;
++pos;
System.out.println("Mode verbose");
}
if(verbose) System.out.println("Expression regulieres: "+args[pos]);
Pattern RegexCompile = Pattern.compile(args[pos]);
++pos;
long debut = System.currentTimeMillis();
for(; pos < args.length ; ++pos) {
try {
if(verbose) System.out.println("Je traite le fichier: "+args[pos]);
match(new File(args[pos]),RegexCompile, verbose);
if(verbose) System.out.println("J'ai traite le fichier: "+args[pos]);
} catch (IOException ioe) {
System.out.println("Impossible de traiter le fichier: "+args[pos]);
ioe.printStackTrace();
}
}
long fin = System.currentTimeMillis();
System.out.println("Temps ecoule: "+ ((fin-debut)/1000.0)+" s");
}
public static void match(File f, Pattern RegexCompile, boolean verbose) throws
IOException {
if(!f.isFile()) {
//System.err.println(f.getAbsolutePath()+ "n'est pas un fichier" );
return;
}
BufferedReader br = new BufferedReader(new FileReader(f));
try {
String ligne;
boolean trouve = false;
while((ligne = br.readLine()) != null) {
//System.out.println("analyse : "+ ligne);
Matcher m = RegexCompile.matcher(ligne);
while( m.find() ) {
trouve = true;
String correspond = m.group(m.groupCount());
System.out.println(f.getPath()+": "+ correspond);
//Correction du devoir
// String[] resultat = correspond.replaceAll("http://", "").split(":|
@|//");
// String[] s = {"utilisateur", "le mot de passe", "la machine", "le
port et le chemin"};
// System.out.println(Arrays.toString(s ));
// System.out.println(Arrays.toString(resultat ));
}
}
if(verbose && !trouve)
System.out.println("Le motif n'a pas ete trouve!");
} finally {
br.close();
}
}
}
// solution du devoir
//http://\w+@\w+ exempleURLExtractor.txt
//http://\w+:\w+@.+:\d+/\w+/\w+ exempleURLExtractor.txt
Correction TP N° 2 : Automates
La classe State:

package automates;

import java.util.HashMap;
import java.util.TreeSet;

public class State implements Comparable<State>{

int id;
private boolean visited;

static int nbState = 0 ;


// l'ensemble des états cibles des transitions epsilon
TreeSet<State> epsilon;
// la table de transition
// au lieu d'utilser un tableau d'états trans= State[256]
// trans[a] = s1 pour la transition ( this ----a---> {s1, S3})
HashMap<Character, TreeSet<State>> transition ;

public State(){
this.id = nbState++;
this.epsilon =new TreeSet<>();
this.transition = new HashMap<>();
}

public boolean isVisited() {


return visited;
}

public void setVisited(boolean visited) {


this.visited = visited;
}
// ajoute une transition pour la lettre c
void addTransition(char c, State next){
if(!transition.containsKey(c))
transition.put(c, new TreeSet<>());
transition.get(c).add(next);
}
// ajoute une epsilon transition
void addTransition(State next){
epsilon.add(next);
}
// ajoute à l'ensemble set tous les états atteignables par des epsilon-
transitions.
static void epsilon_closure(TreeSet<State> set){
TreeSet<State> toAdd = new TreeSet<State>();
do {
toAdd = new TreeSet<State>();
for (State state: set)
for (State next: state.epsilon)
if (!set.contains(next)) {
toAdd.add(next);
}
set.addAll(toAdd);
} while (!toAdd.isEmpty());
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(State s) {
return this.id - s.id;
}

public String toString(){


return id+"";
}

public static void main(String []args) {


State []tab = {new State(), new State(), new State(), new
State()};
tab[2].addTransition('x', tab[3]);
tab[2].addTransition('x', tab[0]);
tab[2].addTransition(tab[0]);
tab[1].addTransition(tab[2]);
tab[0].addTransition(tab[1]);
for (State s: tab) {
for (State n: s.epsilon)
System.out.println(""+s.id+"->"+n.id);
for (char c=0; c<256; c++)
if (s.transition.get(c)!=null){
for(State ss : s.transition.get(c))
System.out.println(""+s.id+"-"+c+"->"+ss.id);
}
}
TreeSet<State> set = new TreeSet<State>();
set.add(tab[1]);
State.epsilon_closure(set);
System.out.print("cloture(1) =");
for (State s: set)
System.out.print(" "+s.id);
System.out.println();
}
}
La classe Automaton:

package automates;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.Map.Entry;

public class Automaton {


// l'ensemble des états de l'automate
TreeSet<State> states;
// l'état initial
State initial;
// l'ensemble des états acceptants
TreeSet<State> accept;
// crée un automate qui ne reconnait rien du tout
public Automaton() {
this.states = new TreeSet<>();
this.initial = new State();
this.accept = new TreeSet<>();
this.states.add(initial);
}

public Automaton(Automaton a){


this();
HashMap<State, State> map = new HashMap<>();
map.put(a.initial, this.initial);
for(State s: a.states){
if(!s.equals(a.initial)){
State e = new State();
this.states.add(e);
map.put(s, e);
}
}
for(State s: a.states){
for(State n : s.epsilon){
map.get(s).addTransition(map.get(n));
}
for( Entry<Character, TreeSet<State>> en:
s.transition.entrySet()){
for(State ss: en.getValue())
map.get(s).addTransition(en.getKey(),
map.get(ss));
}
}
for(State s: a.accept)
this.accept.add(map.get(s));
}

public void removeInacessibleStates(){ //using DFS (Depth first search)


Stack<State> pile = new Stack<>();
pile.push(initial);
while (!pile.isEmpty()){
State s = pile.pop();
s.setVisited(true);
for(TreeSet<State> t : s.transition.values()){
for( State ss: t){
if(!ss.isVisited())
pile.push(ss);
}
}
}
Iterator<State> it = states.iterator();
while(it.hasNext()){
State s = it.next();
if(!s.isVisited())
it.remove();
}
}

// cr'ee un automate qui reconnait un mot donné et rien d'autre


static Automaton new_WORD(String word) {
Automaton a = new Automaton();
State f = a.initial;
for (int i=0; i<word.length(); i++) {
State n = new State();
a.states.add(n);
f.addTransition(word.charAt(i), n);
f = n;
}
a.accept.add(f);
return a;
}
// cr'ee un automate qui reconnait le mot vide
static Automaton new_EMPTY() {
return new_WORD("");
}
// cr'ee un automate qui reconnait un caractère donné
static Automaton new_CHAR(char c){
return new_WORD(""+c);
}
// cr'ee un automate qui reconnait un caractère quelconque
static Automaton new_WILD() {
Automaton a = new Automaton();
State f = new State();
a.states.add(f);
a.accept.add(f);
for (char c=0; c <= 255; c++) {
a.initial.addTransition(c, f);
}
return a;
}
// cr'ee un automate qui reconnait l'union des languages rec. par a1 et a2
static Automaton new_OR(Automaton a1, Automaton a2) {
Automaton a = new Automaton();
a.initial.addTransition(a1.initial);
a.initial.addTransition(a2.initial);

a.accept.addAll(a1.accept);
a.accept.addAll(a2.accept);

a.states.addAll(a1.states);
a.states.addAll(a2.states);
return a;
}
// cr'ee un automate qui reconnait la concaténation des languages
static Automaton new_SEQ(Automaton a1, Automaton a2) {
Automaton a = new Automaton(a1);
for(State s: a.accept){
s.addTransition(a2.initial);
}
a.states.addAll(a2.states);
a.accept.clear();
a.accept.addAll(a2.accept);
return a;
}

// cr'ee un automate qui reconnait la répétition du langage reconnu par a


static Automaton new_STAR(Automaton a) {
Automaton a2 = new Automaton(a);
for(State s: a2.accept){
s.addTransition(a2.initial);
a2.initial.addTransition(s);
}
return a2;
}
/** teste si un mot donne est accept'e
*/
boolean matches(String word) {
// initially the current state contains only
// the initial state and all epsilon transition reachable states
TreeSet<State> current = new TreeSet<State>();
current.add(initial);
State.epsilon_closure(current);
// loop over all letters in the word
boolean error = false;
for (int i=0; i<word.length(); i++) {
TreeSet<State> next = new TreeSet<State>();
char c = word.charAt(i);
for(State s : current){
if(s.transition.containsKey(c)){
next.addAll(s.transition.get(c));
}
}
if(next.isEmpty()){
error = true; break;
}
current = next;
State.epsilon_closure(current);
}
if(!error )
// is one of the current states accepting ?
for (State f: current) {
if (accept.contains(f))
return true;
}
return false;
}

static final String[] strings = {


"", "a", "ab", "ac",
"abc", "acb", "adc",
"abcd", "abcde",
"abcdef", "abcdefe", "abcdefef"
};

public boolean isDeterministe(){


for( State s: states){
if(!s.epsilon.isEmpty())
return false;
for(Character c: s.transition.keySet())
if(s.transition.get(c).size()> 1)
return false;
}
return true;
}

private Set<Character> getAlphabet(){


Set<Character> alpha= new HashSet<>();
for( State s: states){
alpha.addAll(s.transition.keySet());
}
return alpha;
}

public Automaton determinize(){ // à corriger


if(!isDeterministe()){
Automaton a = new Automaton();
List<TreeSet<State>> E = new LinkedList<>();
TreeSet<State> i = new TreeSet<>();
i.add(this.initial);
E.add(i);

HashMap<State, TreeSet<State>> map2 = new HashMap<State,


TreeSet<State>>();
HashMap< TreeSet<State>, State> map1 = new HashMap<
TreeSet<State>, State>();
map1.put(i, a.initial);
map2.put(a.initial, i);

while(!E.isEmpty()){
TreeSet<State> e = E.remove(0);
for(Character c: this.getAlphabet()){
TreeSet<State> tr = transition(e, c);
if(tr.isEmpty()) continue;
State.epsilon_closure(tr);
E.add(tr);

if(map1.containsKey(tr)){
map1.put(tr, new State());
map2.put(map1.get(tr), tr);
a.states.add(map1.get(tr));
}
map1.get(e).addTransition(c, map1.get(tr));

}
for(State s: a.states){
TreeSet<State> sss = new TreeSet<>();
sss.addAll(map2.get(s));
State.epsilon_closure(sss);
for(State ss: sss){

if(this.accept.contains(ss)){
a.accept.add(s);
break;
}
}
}
return a;
}
return this;
}

private TreeSet<State> transition(TreeSet<State> e, Character c) {


TreeSet<State> r = new TreeSet<>();
for(State s: e){
if(s.transition.containsKey(c))
r.addAll(s.transition.get(c));
}
return r;
}

public Automaton minimize() { // à Corriger

HashSet<Pair<State>> marque = new HashSet<>();


HashSet<Pair<State>> non_marque = new HashSet<>();

List< State> sList = Arrays.asList(states.toArray(new State[]{}));

for(int i=0;i<sList.size(); i++){


for(int j=i+1;j<sList.size(); j++){
Pair<State> p =new Pair<State>(sList.get(i),
sList.get(j));
non_marque.add(p);
}
}
for(int i=0;i<sList.size(); i++){
State s = sList.get(i);
if(!accept.contains(s))
for(State ss: accept){
Pair<State> p = new Pair<State>(s, ss);
marque.add(p);
non_marque.remove(p);
}
}

boolean change = true;


while(change){
change =false;
Iterator<Pair<State>> it = non_marque.iterator();
while(it.hasNext()){
Pair<State> p = it.next();
for(Character c: getAlphabet()){
TreeSet<State> l = p.p.first().transition.get(c);
TreeSet<State> ll = p.p.last().transition.get(c);
if(l!=null && ll!=null){
if(marque.contains(new
Pair<State>(l.first(), ll.first()))){
marque.add(p);
it.remove();
change =true;
}
}
else if(l!=null || ll!=null){
marque.add(p);
it.remove();
change =true;
}
}
}
}
//classes d'equivalence
TreeMap<State, TreeSet<State>> map = new TreeMap<>();
for(State s: states) {
map.put(s, new TreeSet<>());
map.get(s).add(s);
}
for(Pair<State> p : non_marque){
map.get(p.p.first()).add(p.p.last());
map.get(p.p.last()).add(p.p.first());
}
for(State s: states) {
if(map.containsKey(s)){
for( State ss : map.get(s)){
if(!ss.equals(s)){
map.remove(ss);
}
}
}
}

Automaton a = new Automaton();


TreeMap<State, TreeSet<State>> map2 = new TreeMap<>();

for(TreeSet<State> k: map.values()) {
if(k.contains(initial)){
map2.put(a.initial, k);
}
else{
map2.put(new State(), k);
}

}
for(State s: map2.keySet()){
TreeSet<State> sv = map2.get(s);
for(State ss: map2.keySet()){
TreeSet<State> ssv = map2.get(s);
for(State e: sv){
for(State ee : ssv ){
for(Character c: e.transition.keySet()){
if(e.transition.get(c).contains(ee)){
s.addTransition(c, ss);
}
}
}
}
}
}
a.states = new TreeSet<>(map2.keySet());
for( State s: a.states){
for(State ss: map2.get(s)){
if(accept.contains(ss))
a.accept.add(s);
}
}
return a;
}

public void print() {


System.out.print("\t");
for(Character c: getAlphabet()){
System.out.print(c+"\t");
}
System.out.println();
for(State s: states){
System.out.print(s+"\t");
for(Character c: getAlphabet()){
TreeSet<State> ss = s.transition.get(c);
System.out.print( (ss==null?"": ss)+"\t");
}
System.out.print( (s.epsilon.isEmpty()?"": s.epsilon)+"\t");
System.out.println();
}
}

public static void main(String args[]) {


// a(ba)*b | ba(ba)*b
Automaton a1 = new_OR(new_WORD("JAVA"), new_WORD("KOTLIN"));
System.out.println(a1.isDeterministe());
Automaton a1d = a1.determinize();
System.out.println(a1.isDeterministe());

Automaton aam = new_SEQ( new_SEQ(new_WORD("a"),


new_STAR(new_WORD("ba"))),
new_WORD("b"));

aam.dump(null, 11, null);


//Automaton bbm = aam.determinize();
aam.removeInacessibleStates();
aam.dump(null, 12, null);
// Automaton ad = aam.determinize();
//
// ad.removeInacessibleStates();

Automaton bonjour = new_WORD("bonjour");


bonjour.print();

bonjour.dump(null, 8, null);
Automaton a,b,c;
//
// //==========================================================
a = new_OR(new_WORD("JAVA"), new_WORD("KOTLIN"));
b = new_SEQ(new_WORD("Etude"), new_WORD(" A
ENSA"));
c = new_STAR(new_WORD("ENSA"));
//
a.dump(null, 0, null);
b.dump(null, 1, null);
c.dump(null, 2, null);
// //
//
//===========================================================

a = new_SEQ(new_CHAR('a'),
new_OR( new_SEQ(new_WILD(), new_CHAR('c')),
new_SEQ(new_WORD("bcd"),
new_STAR(new_WORD("ef")))));
a.dump(null, 5, null);
for(String s: strings) {
System.out.println("matches " + s + " : " + a.matches(s));
}

/** affiche l'automate comme un graphe en notation DOT


... enfin retourne une chaine qui correspond a l'affichage plutot
*/
public void dump(String word, int i, Set<State> marked) {
String file = String.format("tmp%02d",i);
try {
PrintWriter out = new PrintWriter(file+".dot");
String label="";
if (word!=null) {
label=" label=\"";
for (int j=0; j<=word.length(); j++) {
if (j==i)
label+='>';
else
label+=' ';
if (j<word.length())
label+=word.charAt(j);
}
label += "\";\n";
}

// imprimer tout ce qui est sp'ecifique au graphe


out.println("digraph {\n"+
" rankdir=LR;\n"+
label+
" init [shape=point];\n"+
" init->"+initial.id+";\n" );

// imprimer les 'etats


for (State s: states) {
out.print(" "+s.id+" [");
if (accept.contains(s))
out.print("shape=doublecircle");
else
out.print("shape=circle");
if (marked!=null && marked.contains(s))
out.print(",style=filled");
out.println("];");
}

out.println();
// imprimer les epsilon transitions
for (State s: states)
for (State n: s.epsilon)
out.println(" "+s.id+"->"+n.id+";");

out.println();
// imprimer les autres transitions
for (State s: states) {
char c1 = 0;
State n1 = null;

TreeSet<State> nn = new TreeSet<>();


nn.add(new State());

for (char c2=0; c2<=256; c2++) {

TreeSet<State> setn2 = (c2<256) ?


s.transition.get( c2 ): null;

if(setn2==null)
setn2=nn ;

for(State n2 : setn2){
if(setn2==nn) n2=null;
if (n1!=n2) {
if (n1!=null) {
if (c1==0 && c2==256)
label = "\"?\"";
else if (c1==c2-1)
label = "\""+c1+"\"";
else
label = "\""+c1+"-"+
(c2-1)+"\"";
out.println(" "+s.id+"-
>"+n1.id+"[label="+label+"];");
}
c1 = c2;
n1 = n2;
}
}
}
}
out.println("}");
out.close();
}
catch (FileNotFoundException e) {
throw new Error("fichier tmp??.dot n'a pas pu ^etre cr'ee");
}
try {
Process p = Runtime.getRuntime().exec("dot -Tgif -o
"+file+".gif "+file+".dot ");
p.waitFor();
} catch (IOException e) {

e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

class Pair<E>{
TreeSet<E> p = new TreeSet<>();
Pair(E a, E b){
p.add(a);
p.add(b);
}
@Override
public int hashCode() {
return p.hashCode();
}
@Override
public boolean equals(Object obj) {
return p.equals( ((Pair<E>)obj).p);
}

}
}

Vous aimerez peut-être aussi