Previous Up Next

Chapitre 7  Les automates

7.1  Pourquoi étudier les automates

Ce chapitre est une très succincte introduction à la théorie des automates que vous aurez l’occasion de voir de façon détaillée si vous choisissez un cursus d’informatique. Ce chapitre est, par nature, un peu plus “théorique” et un peu moins algorithmique que les précédents.

Les automates sont des objets mathématiques, très utilisés en informatique, qui permettent de modéliser un grand nombre de systèmes (informatiques). L’étude des automates a commencé vers la fin des années cinquante. Elle se base sur de nombreuses techniques (topologie, théorie des graphes, logique, algèbre, etc.). De façon très informelle, un automate est un ensemble “d’états du système”, reliés entre eux par des “transitions” qui sont marquées par des symboles. Étant donné un “mot” fourni en entrée, l’automate lit les symboles du mot un par un et va d’état en état selon les transitions. Le mot lu est soit accepté par l’automate soit rejeté.

Avant de donner une définition plus formelle des concepts décrits ci-dessus, citons quelques exemples classiques d’utilisation d’automates :

En dehors de ces utilisations “pratiques” des automates, notons qu’ils sont aussi utilisés pour modéliser les ordinateurs et pour comprendre ce qu’un ordinateur peut faire (décidabilité) et ce qu’il sait faire efficacement (complexité). C’est donc une notion fondamentale de l’informatique.

7.2  Rappel : alphabets, mots, langages et problèmes

Nous reprenons ici les notations du chapitre 6. L’alphabet Σ est un ensemble de caractères (ou symboles). un mot est une suite finie de caractères. L’ensemble des mots sur Σ est noté Σ*. Un langage est un sous-ensemble de Σ*, c’est-à-dire un ensemble particulier de mots. Parmi les mots de Σ* on distingue le mot vide noté є. Le mot vide est l’unique mot de longueur zéro.

7.3  Automates finis déterministes

Un automate fini déterministe est un quintuplé (Q, Σ, δ, q0, F) constitué des éléments suivants

7.3.1  Fonctionnement d’un automate fini déterministe

L’automate prend en entrée un mot et l’accepte ou la rejette. On dit aussi qu’il le reconnaît ou ne le reconnaît pas. Le langage associé à un automate est constitué de l’ensemble des mots qu’il reconnaît. Voici comment l’automate procède pour décider si un mot appartient à son langage.

De façon plus formelle, pour définir exactement le langage reconnu par un automate, nous introduisons la fonction de transition étendue aux mots, δ. Elle se définit récursivement comme suit.

Nous pouvons maintenant définir le langage L(A) accepté par un automate fini déterministe A = (Q, Σ, δ, q0, F).

L(A) = {c | δ(q0c) ∈ F}

7.3.2  Des représentation “compactes” des automates

On peut associer à un automate une table de transition qui décrit de manière extensive la fonction de transition δ :

La valeur δ(q, a) pour qQ, a ∈ Σ correspond à l’état indiqué à l’intersection de la ligne q et de la colonne a. Notons qu’à partir de cette table il est aisé de retrouver l’ensemble des états ainsi que l’alphabet et donc d’identifier exactement l’automate.

Exemple 7.1   Considérons la table de transition ci-dessous.
 ab
→ 112
* 212
Il correspond à l’automate (Q, Σ, δ, q0, F) avec Il est facile de voir que le langage de cet automate est constitué exactement des mots composés de a et de b qui se terminent par un b.

Pour représenter de façon très intuitive un automate fini déterministe (Q, Σ, δ, q0, F), on peut utiliser un graphe de transition constitué des éléments suivants :

L’automate de l’exemple 7.1 est ainsi représenté sur la figure 7.1.


Figure 7.1: Un automate fini déterministe

Pour simplifier encore cette représentation, un arc entre deux sommets q, q′ peut être valué par plusieurs symboles s1, ..., sn séparés par des virgules. Cette dernière convention signifie simplement que ∀ in, δ(q, si) = q′ et elle permet d’éviter une multiplication d’arcs sur le graphe. La figure 7.2 illustre une telle simplification.


Figure 7.2: Deux représentations équivalentes du même automate fini

Exercice 7.1   Quel est le langage reconnu par l’automate de la figure 7.2 ?
Solution. Tous les mots qui contiennent un “b”. □
Exercice 7.2   Écrire la table de transition de l’automate suivant. Quel est le langage reconnu ?
Solution. La table de transition de l’automate est
 01
ABA
BBC
* CCC
Cet automate reconnaît les mots qui contiennent “01”. □
Exercice 7.3   Soit l’automate fini déterministe ({q0, q1 , q2 , q3}, {0, 1}, δ, q0 , {q0}) donné par la table
δ01
→ * q0q2q1
q1q3q0
q2q0q3
q3q1q2
Dessiner l’automate et montrer qu’il accepte “110101”.
Solution.







  □
Exercice 7.4   Construire un automate fini déterministe qui reconnaît le langage
L = {x ∈ {0, 1}* | n1(x) ≡ 0  mod  4}
n1(x) est le nombre d’occurrence du symbole 1 dans le mot x.
Solution.
Exercice 7.5   Construire les automates finis déterministes qui reconnaissent les langages suivants
L1 = {m ∈ (a + b)* | chaque  a  de  m  est immédiatement précédé et immédiatement suivi d’un  b } 
L2 = {m ∈ (a + b)* | m  contienne à la fois  ab  et  ba } 
L3 = {m ∈ (a + b)* | m  contienne exactement une occurrence de  aaa}

7.4  Automates finis non-déterministes

Un automate fini non-déterministe est un automate tel que dans un état donné, il peut y avoir plusieurs transitions avec le même symbole. le fonctionnement d’un tel automate n’est donc pas totalement « déterminé », car on ne sait pas quel état l’automate va choisir.

Les automates non-déterministes permettent de modéliser facilement des problèmes complexes. Ils peuvent être convertis en des automates finis déterministe. Ces derniers peuvent être exponentiellement plus grand que les automates non déterministe dont ils sont issus.

Un automate fini non-déterministe est un quintuplé : (Q, Σ, δ, q0, F)

C’est la fonction de transition δ qui diffère ici de celle utilisée par les automates finis déterministes. Remarquons que tout automate fini déterministe est aussi un automate fini non-déterministe.

Les représentations compactes des automates finis déterministes s’étendent naturellement aux automates finis non-déterministes. Une cellule de la table de transition contient un sous-ensemble d’états (éventuellement vide).

7.4.1  Fonctionnement d’un automate fini non-déterministe

Comme pour un automate fini déterministe, l’automate prend en entrée un mot et l’accepte ou le rejette. Le langage associé est constitué de l’ensemble des mots qu’il reconnaît.

Exemple 7.2   Voici automate qui reconnaît les mots définis sur l’alphabet {a, b, c} qui commencent par a et qui finissent par c.
La table associée à cet automate est alors :
 abc
q0{q1}
q1{q1}{q1}{q1, q2}
* q2

Comme pour les automates déterministes, nous nous introduisons la fonction de transition étendue aux mots, δ. Elle se définit récursivement comme suit.

Nous pouvons maintenant définir le langage L(A) accepté par un automate fini déterministe A = (Q, Σ, δ, q0, F).

L(A) = {c | δ(q0c) ⋂ F ≠ ∅}
Exercice 7.6   Construire l’automate fini non-déterministe associé à la table ci-dessous.
 ab
→ 0{0,1, 3} {2}
1{3}
2{3}
* 3{1}




Solution.
Exercice 7.7   Construire un automate fini non-déterministe qui reconnaît les mots qui contiennent “church” ou “chomsky”.
Solution.
Exercice 7.8   Construire un automate finis non-déterministe qui reconnaît les mots de l’alphabet {a,b} qui terminent par bab.
Solution.
Exercice 7.9   Construire un automate fini non-déterministe et un automate fini déterministe qui reconnaît les mots sur l’alphabet {a,b,c} décrits par l’expression régulière (a+b+c)*b(a+b+c).
Exercice 7.10   Construire un automate fini non-déterministe qui reconnaît les nombres dont le dernier chiffre n’apparaît qu’une fois.
Exercice 7.11   Modélisation d’un jeu (d’après la page de Jean-Eric Pin). Le joueur a les yeux bandés. Face à lui, un plateau sur lequel sont disposés en carré quatre jetons, blancs d’un côté et noirs de l’autre. Le but du jeu est d’avoir les quatre jetons du côté blanc. Pour cela, le joueur peut retourner autant de jetons qu’il le souhaite, mais sans les déplacer. A chaque tour, le maître de jeu annonce si la configuration obtenue est gagnante ou pas, puis effectue une rotation du plateau de zéro, un, deux ou trois quarts de tours. La configuration de départ est inconnue du joueur, mais le maître de jeu annonce avant le début du jeu qu’elle n’est pas gagnante. Chaque annonce prend une seconde, et il faut 3 secondes au joueur pour retourner les jetons. Pouvez-vous aider le joueur à gagner en moins d’une minute ?

7.4.2  Déterminisation d’un automate fini non-déterministe

Un automate fini déterministe est aussi non-déterministe. Donc tout langage reconnu par un automate fini déterministe est reconnu par un automate fini non-déterministe. Plus surprenant, la réciproque est aussi vraie (Théorème de Rabin-Scott).

Considérons un automate fini non-déterministe An = (Qn, Σ, δn, q0, Fn) et construisons un automate fini déterministe Ad = (Qd, Σ, δd, {q0}, Fd) qui reconnaît exactement le même langage.

Nous illustrons le théorème de Rabin-Scott sur quelques exemples.

Exemple 7.3   reprenons l’exemple de l’exercice 7.8. Il s’agissait de construire un automate fini non-déterministe reconnaissant les mots de l’alphabet {a,b} qui terminent par bab. L’automate suivant répond à la question.
Essayons maintenant de le déterminiser en construisant un nouvel état à partir de chaque sous ensemble d’état possible.
Remarquons que les états {1}, {2}, {3}, {0,2}, {0,3}, {1,2}, {2,3}, {0,1,2}, {1,2,3}, {0,2,3} sont inatteignables et peuvent être “retirés” de l’automate.

En pratique, lors de la conversion, on ne crée pas immédiatement tous les états de l’automate fini déterministe. Les états “utiles” sont crées quand on en a besoin en suivant la méthode de construction ci-dessous :

Exercice 7.12   Déterminiser l’automate de l’exercice 7.7 (long).

7.4.3  Les є transitions

Rappelons qu’є représente le mot vide. Une є transition (notée є sur l’arc d’un automate) permet de passer d’un état à l’autre d’un automate sans lire de symbole. Cette facilité permet de programmer facilement des automates complexes.

Une table de transition peut être associée à un automate contenant des є transition. La table est identique à celle utilisée pour un automate fini non-déterministe à ceci près qu’on la complète d’une colonne associée au caractère vide є.

Exemple 7.4   Pour illustrer les є transitions, construisons un automate fini non déterministe qui reconnaît les nombres décimaux. Rappelons qu’un nombre décimal est un nombre réel qui est le quotient d’un entier relatif par une puissance de dix. Plus précisément, on souhaite pouvoir écrire le nombre décimal en commençant par un “+” ou un “-’, suivi d’une suite de chiffres, d’une virgule et d’une suite de chiffres. Bien entendu, le “+” ou le “-” sont optionnels, la première chaîne de chiffres ne peut pas être vide et ne commence pas par “0” (sauf si le nombre décimal est 0). La seconde chaîne ne se termine pas par “0”. Si seconde chaîne est vide, on omet la “,”.

La transition de l’état A à l’état B est régie par є,+,−. Ainsi, on peut passer de A à B soit en lisant +, soit en lisant − soit enfin en ne lisant rien.

La table de transition associée à cet automate est alors :

 є+,0129
A{B}{B}{B}
B{F}{C}{C}{C}
C{D}{C}
D{D}{D,E}{D,E}{D,E}
* E
* F
Exercice 7.13   On cherche à construire un automate qui reconnaît les mots qui se terminent par bab ou qui commencent par aba.

L’introduction des є transition ne change pas la nature des langages reconnus par les automates. Comme pour les automates non-déterministes que l’on peut toujours déterminiser, il est toujours possible d’éliminer les є transition et d’obtenir un automate fini déterministe équivalent. Nous n’aborderons pas ici cette élimination.

7.5  Automates finis et expressions régulières

Les automates finis et les expressions régulières ont la même expressivité. En effet, le théorème d’équivalence des expressions régulières et des automates finis (Kleene) établit que Le langage accepté par un automate fini correspond à la valeur d’une expression régulière et réciproquement.

7.6  Un peu de Java

Il est aisé de représenter les automates finis sous la forme d’un programme java. Notre objectif est de

Sans perte de généralité, nous allons supposer que l’alphabet est l’ensemble des caractères ASCII et que les états sont numérotés à partir de 0.

7.6.1  Modèle

Le modèle de données est alors très simple :

Soit donc en Java les deux classes List et Automate.

class Liste {
    int val;
    Liste suivant;
    Liste(int v, Liste x) {
        val = v;
        suivant = x;
    }
}

class Automate {
    int q0;           // état initial 
    Liste[][] delta;  // fonction de transition 
    Liste finaux;     // états finaux
    Automate(int q, Liste f, Liste[][]d) {
        q0 = q;
        delta = d;
        finaux = f;
    }
}

7.6.2  Algorithme de recherche

Nous aurons besoin de quelques fonctions classiques sur les listes

static int longueur(Liste x) {            // La longueur d'une liste
    if (x == null)
        return 0;
    else
        return 1 + longueur(x.suivant);
}
static int kieme(Liste x, int k) {       // Le k ème élément
    if (k == 1)
        return x.val;
    else
        return kieme(x.suivant, k-1);
}
static boolean estDans(Liste x, int v) { // Le test d'appartenance
    if (x == null)
        return false;
    else
        return x.val == v || estDans(x.suivant, v);
}

La fonction accepter(String mot, Automate a) qui permet de vérifier qu’un mot mot est accepté apr l’automate a appelle la fonction static boolean accepter(String mot, Automate a, int i, int q).

static boolean accepter(String mot, Automate a) {
    return accepter(mot, a, 0, a.q0);
}
static boolean accepter(String mot, Automate a, int i, int q) {
    if (i == mot.length())
        return Liste.estDans(a.finaux, q);
    else {
        boolean resultat = false;
        int c = mot.charAt(i);   // le code ASCII du caractère courant
        for (Liste nv_q = a.delta[q][c]; nv_q != null; nv_q = nv_q.suivant)
            resultat = resultat || accepter(mot, a, i+1, nv_q.val);
        return resultat;
    }
}

La fonction static boolean accepter(String mot, int i, Automate a, int q) prend, en plus de l’automate et du mot que l’on étudie, deux autres paramètres :

Elle renvoie true si et seulement si le sous-mot de mot dont on a retiré les i premiers caractères était reconnu par un automate semblable à a dont l’état initial serait q.

Remarquons que si i est le dernier caractère du mot (ce qui correspond au test if (i == mot.length())) alors il suffit de tester l’appartenance de q à a.finaux. Si ce n’est pas le cas, on va essayer d’emprunter toutes les transitions possibles. on explore ainsi tous les états nv_q.val atteignable à partir de q en lisant c. Une fois dans l’état nv_q.val, on appelle récursivement accepter.

7.6.3  Mise en œuvre sur un automate

Considérons l’automate suivant qui accepte toutes les chaînes qui se terminent par “01”.

La table associée à cet automate est alors :

 01
→ 0{0,1} {0}
1{2}
* 2

Pour le construire, il nous suffit de construire la table de transition.

    public static void main(String [] arg) {
        Liste[][] delta = new Liste[3][128];
        delta[0][(int)'0'] = new Liste(0, new Liste(1, null));
        delta[0][(int)'1'] = new Liste(0, null);
        delta[1][(int)'0'] = null;
        delta[1][(int)'1'] = new Liste(2, null);
        delta[2][(int)'0'] = null;
        delta[2][(int)'1'] = null;
        Automate a = new Automate(0,
                                  new Liste(2, null),
                                  delta);
        System.out.println("accepter = " + accepter(arg[0], a));
    }

Remarquons que le code ASCII du caractère ’0’ est obtenu par (int)’0’.

Exercice 7.14   Comment procéder pour coder un automate avec des є transitions ?

Previous Up Next