web-dev-qa-db-fra.com

Moteurs DFA vs moteurs NFA: Quelle est la différence dans leurs capacités et leurs limites?

Je recherche une explication non technique de la différence entre les moteurs DFA et NFA, en fonction de leurs capacités et limitations.

42
blunders

Les automates finis déterministes (DFA) et les automates finis non déterministes (NFA) ont exactement les mêmes capacités et limitations. La seule différence est la commodité de la notation.

Un automate fini est un processeur qui a des états et lit une entrée, chaque caractère d'entrée pouvant le mettre dans un autre état. Par exemple, un état peut être "il suffit de lire deux Cs de suite" ou "je commence un mot". Ceux-ci sont généralement utilisés pour des analyses rapides de texte pour trouver des modèles, tels que l'analyse lexicale du code source pour le transformer en jetons.

Un automate fini déterministe est dans un état à la fois, ce qui est implémentable. Un automate fini non déterministe peut se trouver dans plusieurs états à la fois: par exemple, dans une langue où les identifiants peuvent commencer par un chiffre, il peut y avoir un état "lecture d'un nombre" et un autre état "lecture d'un identifiant", et un NFA peut être dans les deux en même temps lors de la lecture de quelque chose commençant par "123". Quel état s'applique réellement dépendrait s'il a rencontré quelque chose de non numérique avant la fin du mot.

Maintenant, nous pouvons exprimer "lire un nombre ou un identifiant" comme un état lui-même, et tout à coup, nous n'avons pas besoin du NFA. Si nous exprimons des combinaisons d'états dans un NFA en tant qu'états eux-mêmes, nous avons un DFA avec beaucoup plus d'états que le NFA, mais qui fait la même chose.

C'est une question qui est plus facile à lire, à écrire ou à traiter. Les DFA sont plus faciles à comprendre en soi, mais les NFA sont généralement plus petits.

71
David Thornley

Voici une réponse non technique de Microsoft:

Les moteurs DFA fonctionnent en temps linéaire car ils ne nécessitent pas de retour en arrière (et donc ils ne testent jamais le même caractère deux fois). Ils peuvent également garantir la correspondance de la chaîne la plus longue possible. Cependant, puisqu'un moteur DFA ne contient qu'un état fini, il ne peut pas faire correspondre un modèle avec des références arrières et parce qu'il ne construit pas d'expansion explicite, il ne peut pas capturer de sous-expressions.

Les moteurs NFA traditionnels exécutent ce que l'on appelle des algorithmes de retour en arrière "avides", testant toutes les extensions possibles d'une expression régulière dans un ordre spécifique et acceptant la première correspondance. Parce qu'un NFA traditionnel construit une expansion spécifique de l'expression régulière pour une correspondance réussie, il peut capturer des correspondances de sous-expression et des références arrières correspondantes. Cependant, comme un NFA traditionnel revient en arrière, il peut visiter exactement le même état plusieurs fois si l'état est atteint par des chemins différents. En conséquence, il peut s'exécuter de façon exponentielle lentement dans le pire des cas. Parce qu'un NFA traditionnel accepte la première correspondance qu'il trouve, il peut également laisser d'autres correspondances (éventuellement plus longues) non découvertes.

Les moteurs POSIX NFA sont comme les moteurs NFA traditionnels, sauf qu'ils continuent de revenir en arrière jusqu'à ce qu'ils puissent garantir qu'ils ont trouvé la correspondance la plus longue possible. Par conséquent, un moteur POSIX NFA est plus lent qu'un moteur NFA traditionnel, et lorsque vous utilisez un POSIX NFA, vous ne pouvez pas privilégier une correspondance plus courte par rapport à une plus longue en modifiant l'ordre de la recherche de retour en arrière.

Les moteurs NFA traditionnels sont favorisés par les programmeurs car ils sont plus expressifs que les moteurs NFA DFA ou POSIX. Bien que, dans le pire des cas, ils puissent fonctionner lentement, vous pouvez les orienter pour trouver des correspondances en temps linéaire ou polynomial en utilisant des modèles qui réduisent les ambiguïtés et limitent le retour en arrière.

[http://msdn.Microsoft.com/en-us/library/0yzc2yb0.aspx]

16
james.garriss

Une explication simple et non technique, paraphrasée du livre de Jeffrey Friedl Mastering Regular Expressions .

[~ # ~] mise en garde [~ # ~] :

Bien que ce livre soit généralement considéré comme la "bible des regex", il semble que la distinction faite ici entre DFA et NFA soit correcte. Je ne suis pas informaticien, et je ne comprends pas l'essentiel de la théorie derrière ce qu'est vraiment une expression "régulière", déterministe ou non. Après le début de la controverse, j'ai supprimé cette réponse à cause de cela, mais depuis lors, elle a été référencée dans les commentaires à d'autres réponses. Je serais très intéressé à en discuter davantage - est-ce que Friedl a vraiment tort? Ou ai-je eu tort Friedl (mais j'ai relu ce chapitre hier soir, et c'est comme si je m'en souvenais ...)?

Edit: Il semble que Friedl et moi avons en effet tort. Veuillez consulter les excellents commentaires d'Eamon ci-dessous.


Réponse originale:

Un moteur DFA parcourt la chaîne de saisie caractère par caractère et essaie (et se souvient) de toutes les manières possibles que l'expression rationnelle pourrait correspondre à ce stade. S'il atteint la fin de la chaîne, il déclare le succès.

Imaginez la chaîne AAB et l'expression régulière A*AB. Nous parcourons maintenant notre chaîne lettre par lettre.

  1. A:

    • Première branche: peut être mise en correspondance par A*.
    • Deuxième branche: peut être mise en correspondance en ignorant le A* (zéro répétition est autorisé) et en utilisant le deuxième A dans l'expression régulière.
  2. A:

    • Première branche: peut être mise en correspondance en développant A*.
    • Deuxième branche: impossible de faire correspondre B. La deuxième branche échoue. Mais:
    • Troisième branche: peut être mise en correspondance en ne développant pas A* et en utilisant le deuxième A à la place.
  3. B:

    • Première branche: ne peut pas être mise en correspondance en développant A* ou en passant dans l'expression régulière au jeton suivant A. La première branche échoue.
    • Troisième branche: peut être appariée. Hourra!

Un moteur DFA ne revient jamais en arrière dans la chaîne.


Un moteur NFA parcourt le regex jeton par jeton et essaie toutes les permutations possibles sur la chaîne, en reculant si nécessaire. S'il atteint la fin de l'expression régulière, il déclare le succès.

Imaginez la même chaîne et le même regex qu'avant. Nous passons maintenant en revue notre expression régulière jeton par jeton:

  1. A*: Correspond à AA. N'oubliez pas les positions de retour en arrière 0 (début de la chaîne) et 1.
  2. A: ne correspond pas. Mais nous pouvons revenir en arrière et réessayer. Le moteur d'expression régulière recule d'un caractère. Maintenant, A correspond.
  3. B: correspond. Fin de l'expression régulière atteinte (avec une position de retour en arrière à revendre). Hourra!
7
Tim Pietzcker

Les NFA et les DFA sont des automates finis, comme leur nom l'indique.

Les deux peuvent être représentés comme un état de départ, un état de réussite (ou "accepter") (ou un ensemble d'états de réussite) et une table d'état répertoriant les transitions.

Dans la table d'état d'un DFA, chaque <state₀, input> la clé transitera vers un et un seul state₁.

Dans la table d'état d'un NFA, chaque <state₀, input> passera à un set d'états.

Lorsque vous prenez un DFA, réinitialisez-le à son état de départ, une séquence de symboles d'entrée, et vous savez exactement dans quel état final il se trouve et s'il s'agit d'un état de réussite ou non.

Cependant, lorsque vous prenez un NFA, il recherchera, pour chaque symbole d'entrée, l'ensemble des états de résultat possibles et (en théorie) au hasard, de façon non déterministe, en sélectionner un. S'il existe un ensemble de sélections aléatoires qui conduit à l'un des états de réussite de cette chaîne d'entrée, le DFA est réputé réussir pour cette chaîne. En d'autres termes, vous êtes censé prétendre qu'il sélectionne toujours comme par magie le bon.

Une des premières questions dans l'informatique était de savoir si les NFA étaient plus puissants que les DFA, en raison de cette magie, et la réponse s'est avérée non car tout NFA pouvait être traduit en DFA équivalent. Leurs capacités et limitations sont exactement les mêmes les unes que les autres.

4
BenGoldberg

Je trouve que l'explication donnée dans Expressions régulières, le didacticiel complet de Jan Goyvaerts est la plus utilisable. Voir page 7 de ce PDF:

https://www.princeton.edu/~mlovett/reference/Regular-Expressions.pdf

Parmi les autres points soulevés à la page 7, Il existe deux types de moteurs d'expression régulière: les moteurs orientés texte et les moteurs orientés regex. Jeffrey Friedl les appelle respectivement moteurs DFA et NFA. ... certaines fonctionnalités très utiles, telles que les quantificateurs paresseux et les références arrières, ne peuvent être implémentées que dans l'expression régulière -moteurs dirigés.

0
RBV