Théorie des langages · C2 Langages réguliers · Chapitre 1 · 5 h
Automates finis déterministes
États, transitions, état initial, états acceptants ; exécution et acceptation d'un mot ; construire un AFD pour un langage donné ; automate complet.
Voici le cœur du cours, et l'objet le plus utile de toute l'année. Un automate fini est une machine d'une simplicité extrême — une poignée d'états, une flèche par lettre — et pourtant elle est partout : dans le premier étage de tout compilateur, dans la barre de recherche qui valide une adresse de courriel, dans le protocole réseau qui suit l'état d'une connexion, dans le distributeur de billets. Chaque fois qu'un programme doit reconnaître une forme dans un flux de symboles avec une mémoire bornée, il y a un automate fini, explicite ou caché.
Ce chapitre le définit, montre comment l'exécuter, et surtout comment en construire un pour un langage donné. Le mot d'ordre du bloc — implémenter plutôt que réciter — commence ici : à la fin, vous aurez codé le moteur d'un automate quelconque.
La définition : un quintuplet
Un automate fini déterministe (AFD) est la donnée de cinq éléments, notés :
| Symbole | Nom | Ce que c'est |
|---|---|---|
| ensemble des états | fini ; chaque état est une « situation » de la lecture | |
| l'alphabet | les symboles lus en entrée (chapitre 1) | |
| la fonction de transition | (chapitre 2) | |
| l'état initial | où l'on commence ; | |
| les états acceptants | ; où l'on a le droit de finir |
L'idée à retenir dès maintenant, et qui donne le sens de tout le reste : chaque état est une mémoire. Il ne mémorise pas les symboles déjà lus — un automate fini ne peut pas se souvenir de tout — mais la seule chose qui reste utile pour la suite : « où en suis-je de ma reconnaissance ? ». Comme est fini, l'automate ne peut distinguer qu'un nombre fini de situations. C'est à la fois sa force et sa limite, et le chapitre 6 en tirera les conséquences.
Le déterminisme
Le mot « déterministe » a un sens précis : depuis un état donné, en lisant une lettre donnée, il y a exactement une transition possible. C'est l'exigence de fonction du chapitre 2 — une seule sortie par entrée. L'automate ne fait donc jamais de choix : à chaque instant, l'état courant et la lettre lue déterminent entièrement l'état suivant.
Cette propriété a une conséquence pratique majeure : l'exécution est rapide et simple. On lit le mot une seule fois, de gauche à droite, en suivant les flèches, sans jamais revenir en arrière ni explorer plusieurs possibilités. Le temps de reconnaissance est proportionnel à la longueur du mot — on ne fait pas mieux. C'est pourquoi les analyseurs lexicaux réels (chapitre 9) sont des AFD.
Exécution et acceptation
Exécuter un AFD sur un mot , c'est suivre un chemin :
q0 --a1--> q1 --a2--> q2 --a3--> ... --an--> qnOn part de , on applique à chaque lettre, on arrive dans un état final . Puis la règle d'acceptation, unique et sans nuance :
Le mot est accepté si et seulement si l'état d'arrivée appartient à .
Sinon, il est rejeté. Le langage reconnu par l'automate , noté , est l'ensemble de tous les mots acceptés. Un langage est dit régulier s'il existe un automate fini qui le reconnaît — c'est la définition que tout le bloc II va explorer.
Notez le cas du mot vide : l'automate ne lit aucune lettre, reste en , et est accepté exactement quand . Encore une fois, tout se joue sur le statut du mot vide.
Construire un AFD : penser en situations
C'est le savoir-faire du chapitre, et il s'apprend par une seule question, posée à l'envers de l'intuition :
Quelles situations dois-je distinguer pour décider, à la fin, si j'accepte — et rien de plus ?
Chaque situation distincte devient un état. Prenons « les mots sur contenant un nombre pair de ». Qu'ai-je besoin de retenir pendant la lecture ? Uniquement la parité du nombre de vus jusqu'ici. Deux situations, donc deux états :
b b ┌──┐ ┌──┐ ▼ │ ▼ │ ──▶( pair )──a──▶( impair ) ▲ │ └───────a───────┘pairest l'état initial (zéro , c'est pair) et le seul état acceptant ;- lire un
abascule la parité ; lire unbne change rien.
Plutôt que de l'imaginer, regardez-le fonctionner. L'animation ci-dessous fait lire le mot abba
à cet automate : le ruban d'entrée défile en haut, l'état courant s'allume, et un jeton portant la
lettre lue parcourt la transition prise. Comme abba contient deux a, le chemin revient à l'état
pair et le mot est accepté.
Animation · 6 étapes
Un AFD lit « abba » : nombre pair de a ?
- État initial — L'automate démarre dans l'état pair. Mot à lire : « abba ».
- Lecture de « a » (1/4) — Depuis pair, la lettre « a » mène à impair.
- Lecture de « b » (2/4) — Depuis impair, la lettre « b » mène à impair.
- Lecture de « b » (3/4) — Depuis impair, la lettre « b » mène à impair.
- Lecture de « a » (4/4) — Depuis impair, la lettre « a » mène à pair.
- Mot accepté — Tout le mot est lu et l'état d'arrivée pair est acceptant : le mot « abba » appartient au langage.
Deux réflexes de conception valent d'être énoncés.
Distinguer juste ce qu'il faut. Retenir le nombre exact de demanderait une infinité d'états — impossible. Ne retenir que la parité suffit, parce que c'est la seule information qui sert à la décision finale. Un bon AFD oublie tout le reste.
Ni trop, ni trop peu d'états. Trop d'états distinguent des situations qui mènent au même comportement (gaspillage) ; trop peu confondent des cas qu'il fallait séparer (erreur). Le chapitre 6 donnera l'automate minimal, qui a le nombre exact d'états nécessaires.
Quiz · 1 question
Dans un AFD reconnaissant « les mots contenant un nombre pair de a », que représente chaque état ?
- Le nombre exact de a lus depuis le début du mot — compte exact
- La parité (pair/impair) du nombre de a lus jusqu'ici — la seule information utile pour la décision finale — parité
- La dernière lettre lue — dernière lettre
Réponse : Un automate fini a un nombre FINI d'états : il ne peut pas mémoriser le nombre exact de a, qui peut croître sans borne. Il ne retient que ce qui sert à décider — ici, la parité, deux valeurs, donc deux états. C'est le principe de conception d'un AFD : chaque état est une mémoire réduite à la stricte information nécessaire. Retenir la dernière lettre ne suffirait pas à connaître la parité de tout le préfixe.
L'automate complet
Un détail technique qui a des conséquences. Un AFD est complet si est défini pour tout état et toute lettre : depuis n'importe quel état, chaque lettre a une flèche sortante. Notre exemple ci-dessus est complet.
Souvent, on dessine un automate incomplet : on omet les transitions qui « mènent à l'échec », pour ne pas surcharger le schéma. Pour le rendre complet, on ajoute un état puits (ou état poubelle) : un état non acceptant, absorbant, vers lequel partent toutes les transitions manquantes, et qui boucle sur lui-même. Une fois entré dans le puits, on n'en sort plus — le mot est condamné au rejet.
Pourquoi s'en soucier ? Parce que certaines constructions l'exigent. La plus importante : pour reconnaître le complément d'un langage — les mots que l'automate rejetait —, il suffit d'échanger les états acceptants et non acceptants. Mais cet échange ne donne le bon résultat que si l'automate est complet : sinon, un mot qui « bloquait » faute de transition ne serait ni dans le langage ni dans son complément. La complétude est le petit soin qui rend les propriétés de clôture du chapitre 6 correctes.
Quiz · 1 question
Un AFD complet reconnaît un langage L. On échange ses états acceptants et non acceptants (F devient Q\\F). Quel langage reconnaît le nouvel automate ?
- Le même langage L — inchangé
- Le complément de L : tous les mots que l'ancien automate rejetait — complément
- Le langage vide — vide
Réponse : Puisque l'automate est complet, tout mot atteint un unique état d'arrivée. Échanger acceptants et non acceptants inverse exactement la décision pour chaque mot : ceux qui finissaient dans F (acceptés) finissent maintenant hors de F (rejetés) et inversement. On obtient donc le complément de L. C'est une propriété de clôture des langages réguliers (chapitre 6) — mais elle EXIGE la complétude : sur un automate incomplet, un mot bloqué en cours de route échapperait aux deux langages.
À vous
Le TP fondateur du bloc : implémenter un AFD. Vous écrivez d'abord le moteur d'exécution, une poignée de lignes qui exécute n'importe quel automate décrit par sa table de transition — la preuve que toute la logique du langage tient dans , pas dans le code. Vous le testez sur « nombre pair de », puis vous concevez vous-même l'automate de « mots se terminant par ».
C'est ce passage du papier au code que le programme du cours désigne comme le plus formateur : écrire le moteur fait comprendre l'automate mieux que n'importe quel exercice de tracé.
Exercice de code
Écrivez le moteur d'exécution générique d'un AFD (états, alphabet, transitions, initial, acceptants), testez-le sur « nombre pair de a », puis concevez vous-même l'AFD du langage « mots se terminant par ab ».
Point de départ
// Un AFD est un quintuplet (Q, Σ, δ, q0, F). On le décrit ici comme une
// donnée, et on écrit UNE fois le moteur qui exécute n'importe quel AFD.
//
// Langage visé : L = { mots sur {a,b} contenant un nombre PAIR de a }.
const afd = {
etats: ["pair", "impair"], // Q
alphabet: ["a", "b"], // Σ
initial: "pair", // q0 : zéro 'a' lu, c'est pair
acceptants: ["pair"], // F
// δ : table de transition. delta["etat"]["lettre"] = etat d'arrivée.
delta: {
pair: { a: "impair", b: "pair" }, // lire un a change la parité ; b ne
impair: { a: "pair", b: "impair" }, // change rien
},
};
// ── À VOUS (1) : le moteur d'exécution ──────────────────────────────────────
// Partir de l'état initial, lire le mot lettre par lettre en suivant δ,
// puis accepter si l'état final est dans F.
function accepte(afd, mot) {
let etat = afd.initial;
// à compléter : boucle sur les lettres de 'mot', mise à jour de 'etat'
return false; // à remplacer : etat est-il acceptant ?
}
// ── Vérification ────────────────────────────────────────────────────────────
const tests = [
["", true], // 0 a : pair
["aa", true], // 2 a
["a", false], // 1 a
["abab", true], // 2 a
["baaab", false], // 3 a
["bbbb", true], // 0 a
];
for (const [mot, attendu] of tests) {
const r = accepte(afd, mot);
console.log("'" + mot + "'".padEnd(8) + " -> " + r + (r === attendu ? " ok" : " ✗"));
}
// ── À VOUS (2) : concevoir un AFD ───────────────────────────────────────────
// Décrivez ci-dessous un AFD 'afd2' pour L2 = { mots qui SE TERMINENT par 'ab' }.
// Indice : 3 états — "rien de spécial", "vient de voir un a", "vient de voir ab".
const afd2 = null; // à construire, sur le même modèle que 'afd'
if (afd2) {
for (const mot of ["ab", "aab", "abab", "aba", "b", "ba"]) {
console.log("L2 '" + mot + "' -> " + accepte(afd2, mot));
}
}
Solution
function accepte(afd, mot) {
let etat = afd.initial;
for (const lettre of mot) {
etat = afd.delta[etat][lettre]; // δ(etat, lettre) : une seule transition
}
return afd.acceptants.includes(etat);
}
// Le moteur tient en trois lignes, et il est GÉNÉRIQUE : il exécute n'importe
// quel AFD décrit par la donnée. C'est toute la force du modèle — la logique
// du langage est entièrement dans la table δ, pas dans le code.
// ── L'AFD pour « se termine par ab » ────────────────────────────────────────
const afd2 = {
etats: ["q0", "qa", "qab"],
alphabet: ["a", "b"],
initial: "q0",
acceptants: ["qab"], // on accepte si on VIENT de lire ...ab
delta: {
// q0 : on n'a rien vu d'utile. Un 'a' amorce un possible "ab".
q0: { a: "qa", b: "q0" },
// qa : on vient de voir un 'a'. Un 'b' complète "ab" ; un autre 'a' garde
// l'espoir (le dernier a peut encore commencer un ab).
qa: { a: "qa", b: "qab" },
// qab : on vient de voir "ab". Un 'a' relance (qa) ; un 'b' casse (q0).
qab: { a: "qa", b: "q0" },
},
};
// ── Ce que le TP enseigne ───────────────────────────────────────────────────
//
// 1. Chaque état est une MÉMOIRE : "où en suis-je de ma reconnaissance ?".
// L'AFD ne peut retenir qu'un nombre FINI de situations — ici la parité des
// a, ou les 2 derniers symboles utiles. C'est sa force (rapidité, mémoire
// bornée) et sa limite (chapitre 6 : il ne sait pas compter jusqu'à n).
//
// 2. L'exécution est DÉTERMINISTE : depuis un état, une lettre mène à UN seul
// état. Le moteur ne fait jamais de choix — d'où sa simplicité et sa
// vitesse (une passe, temps proportionnel à la longueur du mot).
//
// 3. Concevoir un AFD, c'est répondre à : « quelles situations dois-je
// distinguer, et rien de plus ? ». Trop d'états = on distingue l'inutile ;
// trop peu = on confond des cas qu'il fallait séparer. Le chapitre 6
// (minimisation) donnera le nombre EXACT d'états nécessaires.
Ce que la suite en fait
Vous savez maintenant ce qu'est un langage régulier — un langage reconnu par un AFD — et comment en construire un. Mais l'AFD est parfois pénible à concevoir directement : son exigence de déterminisme force à tout anticiper.
Le chapitre 4 lève cette contrainte avec l'automate non déterministe, bien plus facile à écrire, puis montre le résultat central du cours : on peut toujours transformer un automate non déterministe en AFD équivalent. C'est la déterminisation — le premier des deux points qui coincent, et celui où le de l'ensemble des parties (chapitre 2) va enfin se manifester.
À retenir
Flashcards · 4 cartes
- Quels sont les cinq éléments d'un AFD, et quand un mot est-il accepté ?
- Un AFD est un quintuplet (Q, Σ, δ, q0, F) : états, alphabet, fonction de transition δ : Q×Σ→Q, état initial q0, états acceptants F ⊆ Q. Un mot w est accepté si et seulement si, en partant de q0 et en suivant δ lettre par lettre, l'état d'arrivée appartient à F. Le langage reconnu L(A) est l'ensemble des mots acceptés ; un langage est régulier s'il existe un AFD qui le reconnaît.
- Que signifie « déterministe », et quel avantage cela procure-t-il ?
- Depuis un état, chaque lettre mène à EXACTEMENT UN état (δ est une fonction) : l'automate ne fait jamais de choix. Avantage : l'exécution lit le mot une seule fois, de gauche à droite, sans retour arrière, en temps proportionnel à sa longueur. C'est pourquoi les analyseurs lexicaux réels sont des AFD.
- Comment conçoit-on un AFD pour un langage donné ?
- En répondant à : « quelles situations dois-je distinguer pour décider à la fin, et rien de plus ? ». Chaque situation utile devient un état — une mémoire réduite à l'information nécessaire (ex. la parité, pas le compte exact). Trop d'états distinguent l'inutile, trop peu confondent des cas à séparer ; le chapitre 6 donne le nombre exact via la minimisation.
- Qu'est-ce qu'un AFD complet, et pourquoi la complétude est-elle nécessaire pour le complément ?
- Complet : δ(q,a) est défini pour tout état et toute lettre (on ajoute au besoin un état puits absorbant et non acceptant). Pour reconnaître le complément d'un langage, on échange acceptants et non acceptants — mais cet échange n'est correct que sur un automate complet : sinon un mot « bloqué » faute de transition n'appartiendrait ni à L ni à son complément.
Exercices d'entraînement
Exercice 1
Construire un AFD
Sur , construire un AFD reconnaissant les mots qui contiennent le facteur aa. Donner les états (avec leur signification), l'état initial, les états acceptants et la table de transition.
Correction
Il suffit de retenir « combien de a consécutifs viens-je de voir, au plus 2 ». Trois états :
q0: je n'ai pas deaen attente (état initial) ;q1: je viens de voir una;q2: j'ai déjà vuaa(état acceptant, absorbant).
Table de transition :
| état | a | b |
|---|---|---|
q0 | q1 | q0 |
q1 | q2 | q0 |
q2 | q2 | q2 |
Une fois aa rencontré, on reste dans q2 quoi qu'il arrive : le facteur est là pour de bon. Lire un b en q1 remet le compteur à zéro (q0), car la série de a est interrompue.
Exercice 2
Lire un AFD
Un AFD sur a pour états p (initial) et q (acceptant), et pour transitions : , , , . Quel langage reconnaît-il ?
Correction
On accepte lorsqu'on termine en q. Observons les transitions : lire a mène toujours en q (depuis p comme depuis q), et lire b mène toujours en p. L'état d'arrivée ne dépend donc que de la dernière lettre lue : q si c'est a, p si c'est b.
Le langage reconnu est donc : les mots qui se terminent par a. (Le mot vide reste en p, non acceptant : il est rejeté, ce qui est cohérent — il ne se « termine pas par a ».)
Exercice 3
Complément d'un langage
En repartant de l'AFD de l'exercice 2 (mots se terminant par a), donner un AFD reconnaissant le complément de ce langage, et décrire ce complément.
Correction
L'AFD est complet ( est définie pour tout état et toute lettre), donc il suffit d'échanger états acceptants et non acceptants : on prend à la place de , sans rien changer aux transitions.
Le complément est l'ensemble des mots qui ne se terminent pas par a, c'est-à-dire le mot vide (aucune dernière lettre) et tous les mots se terminant par b. L'échange n'aurait pas donné le bon résultat sur un automate incomplet : un mot bloqué faute de transition n'aurait été ni dans le langage ni dans son complément.