DFA和NFA

1.历史:

引用

正则表达式萌芽于1940年代的神经生理学研究,由著名数学家Stephen Kleene第一个正式描述。具体地说,Kleene归纳了前述的神经生理学研究,在一篇题为《正则集代数》的论文中定义了“正则集”,并在其上定义了一个代数系统,并且引入了一种记号系统来描述正则集,这种记号系统被他称为“正则表达式”。在理论数学的圈子里被研究了几十年之后,1968年,后来发明了UNIX系统的Ken Thompson第一个把正则表达式用于计算机领域,开发了qed和grep两个实用文本处理工具,取得了巨大成功。在此后十几年里,一大批一流计算机科学家和黑客对正则表达式进行了密集的研究和实践。在1980年代早期,UNIX运动的两个中心贝尔实验室和加州大学伯克利分校分别围绕grep工具对正则表达式引擎进行了研究和实现。与之同时,编译器“龙书”的作者Alfred Aho开发了Egrep工具,大大扩展和增强了正则表达式的功能。此后,他又与《C程序设计语言》的作者Brian Kernighan等三人一起发明了流行的awk文本编辑语言。到了1986年,正则表达式迎来了一次飞跃。先是C语言顶级黑客Henry Spencer以源代码形式发布了一个用C语言写成的正则表达式程序库(当时还不叫open source),从而把正则表达式的奥妙带入寻常百姓家,然后是技术怪杰Larry Wall横空出世,发布了Perl语言的第一个版本。自那以后,Perl一直是正则表达式的旗手,可以说,今天正则表达式的标准和地位是由Perl塑造的。Perl 5.x发布以后,正则表达式进入了稳定成熟期,其强大能力已经征服了几乎所有主流语言平台,成为每个专业开发者都必须掌握的基本工具。

2.DFA和NFA

引用

理解DFA和NFA
正则表达式引擎分成两类,一类称为DFA(确定性有穷自动机),另一类称为NFA(非确定性有穷自动机)。两类引擎要顺利工作,都必须有一个正则式和一个文本串,一个捏在手里,一个吃下去。DFA捏着文本串去比较正则式,看到一个子正则式,就把可能的匹配串全标注出来,然后再看正则式的下一个部分,根据新的匹配结果更新标注。而NFA是捏着正则式去比文本,吃掉一个字符,就把它跟正则式比较,匹配就记下来:“某年某月某日在某处匹配上了!”,然后接着往下干。一旦不匹配,就把刚吃的这个字符吐出来,一个个的吐,直到回到上一次匹配的地方。
DFA与NFA机制上的不同带来5个影响:
1. DFA对于文本串里的每一个字符只需扫描一次,比较快,但特性较少;NFA要翻来覆去吃字符、吐字符,速度慢,但是特性丰富,所以反而应用广泛,当今主要的正则表达式引擎,如Perl、Ruby、Python的re模块、Java和.NET的regex库,都是NFA的。
2. 只有NFA才支持lazy和backreference等特性;
3. NFA急于邀功请赏,所以最左子正则式优先匹配成功,因此偶尔会错过最佳匹配结果;DFA则是“最长的左子正则式优先匹配成功”。
4. NFA缺省采用greedy量词(见item 4);
5. NFA可能会陷入递归调用的陷阱而表现得性能极差。

我这里举一个例子来说明第3个影响。

例如用正则式/perl|perlman/来匹配文本 ‘perlman book’。如果是NFA,则以正则式为导向,手里捏着正则式,眼睛看着文本,一个字符一个字符的吃,吃完 ‘perl’ 以后,跟第一个子正则式/perl/已经匹配上了,于是记录在案,往下再看,吃进一个 ‘m’,这下糟了,跟子式/perl/不匹配了,于是把m吐出来,向上汇报说成功匹配 ‘perl’,不再关心其他,也不尝试后面那个子正则式/perlman/,自然也就看不到那个更好的答案了。

如果是DFA,它是以文本为导向,手里捏着文本,眼睛看着正则式,一口一口的吃。吃到/p/,就在手里的 ‘p’ 上打一个钩,记上一笔,说这个字符已经匹配上了,然后往下吃。当看到 /perl/ 之后,DFA不会停,会尝试再吃一口。这时候,第一个子正则式已经山穷水尽了,没得吃了,于是就甩掉它,去吃第二个子正则式的/m/。这一吃好了,因为又匹配上了,于是接着往下吃。直到把正则式吃完,心满意足往上报告说成功匹配了 ‘perlman’。

由此可知,要让NFA正确工作,应该使用 /perlman|perl/ 模式。

通过以上例子,可以理解为什么NFA是最左子式匹配,而DFA是最长左子式匹配。实际上,如果仔细分析,关于NFA和DFA的不同之处,都可以找出道理。而明白这些道理,对于有效应用正则表达式是非常有意义的。

写道

正则表达式的形式定义故意非常精简,避免定义多余的量词 ? 和 +,它们可以被表达为: a+ = aa* 和 a? = (a|ε)。有时增加补算子 ~ ;~R 指示在 Σ* 上的不在 R 中的所有字符串的集合。补算子是多余的,因为它使用其他算子来表达(尽管计算这种表示的过程是复杂的,而结果可能指数性的增大)。
这种意义上的正则表达式可以表达正则语言,精确的是可被有限状态自动机接受的语言类。但是在简洁性上有重要区别。某类正则语言只能用大小指数增长的自动机来描述,而要求的正则表达式的长度只线性的增长。正则表达式对应于乔姆斯基层级的类型-3文法。在另一方面,在正则表达式和不导致这种大小上的爆炸的非确定有限状态自动机(NFA)之间有简单的映射;为此 NFA 经常被用作正则表达式的替代表示。
我们还要在这种形式化中研究表达力。如下面例子所展示的,不同的正则表达式可以表达同样的语言: 这种形式化中存在着冗余。
有可能对两个给定正则表达式写一个算法来判定它们所描述的语言是否本质上相等,简约每个表达式到极小确定有限自动机,确定它们是否同构(等价)。
这种冗余可以消减到什么程度? 我们可以找到仍有完全表达力的正则表达式的有趣的子集吗? Kleene 星号和并集明显是需要的,但是我们或许可以限制它们的使用。这提出了一个令人惊奇的困难问题。因为正则表达式如此简单,没有办法在语法上把它重写成某种规范形式。过去公理化的缺乏导致了星号高度问题。最近 Dexter Kozen 用克莱尼代数公理化了正则表达式。
很多现实世界的“正则表达式”引擎实现了不能用正则表达式代数表达的特征。

目前正则引擎支持的语言种类:

引擎类型 程序
DFA awk(大多数版本)、egrep(大多数版本)、flex、lex、MySQL、Procmail
传统型 NFA GNU Emacs、Java、grep(大多数版本)、less、more、.NET语言、PCRE library、Perl、PHP(所有三套正则库)、Python、Ruby、set(大多数版本)、vi
POSIX NFA mawk、Mortice Lern System‘s utilities、GUN Emacs(明确指定时使用)
DFA/NFA混合 GNU awk、 GNU grep/egrep、 Tcl

引申阅读:

为什么输入这种正则会导致假死?http://rdc.taobao.com/team/jm/archives/2432

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

原文地址:https://www.cnblogs.com/siwnchs/p/10462627.html

时间: 2024-08-01 21:51:32

DFA和NFA的相关文章

DFA和NFA的区别

正则表达式引擎分成两类,一类称为DFA(确定性有穷自动机),另一类称为NFA(非确定性有穷自动机).两类引擎要顺利工作,都必须有一个正则式和一个文本串,一个捏在手里,一个吃下去.DFA捏着文本串去比较正则式,看到一个子正则式,就把可能的匹配串全标注出来,然后再看正则式的下一个部分,根据新的匹配结果更新标注.而NFA是捏着正则式去比文本,吃掉一个字符,就把它跟正则式比较,匹配就记下来:“某年某月某日在某处匹配上了!”,然后接着往下干.一旦不匹配,就把刚吃的这个字符吐出来,一个个的吐,直到回到上一次

NFA 与 DFA 的转换

声明 本文是对编译原理中NFA到DFA的转换做的总结,该代码参考了编译原理中词法分析的相关内容. 转换方式 NFA即不确定有穷状态机,而DFA是确定有穷状态机. 从本质上讲NFA与利用其构造而成的DFA是等价的,但因为NFA某一状态离开的路径可能有多条,因此常常在构造出NFA后将前面的状态集合做一抽象以构建对于每一状态离开路径只有一条的DFA. NFA到DFA的构造方法常用到两点,即子集构造法与闭包传递. 伪代码说明 1.0-closure(s) 能够从NFA的状态S开始只通过0转换到达的NFA

从正则表达式到 NFA 到 DFA 到最简 DFA (二)

从正则表达式到 NFA 到 DFA 到最简 DFA (二) NFA $ \rightarrow $ DFA (子集构造法) 这里我们用一个例子来解释. 如上图所示,这是上一篇文章中的正则表达式化成的 NFA,这里拿来接着用. 我们首先看开始状态 n0.n0 在接收了一个字符 a 之后可以转换到 n1,这个时候我们要看 n1 是否存在 $ \varepsilon $ 转移.若存在,则递归的将所有能 $ \varepsilon $ 转移的状态添加到一个集合里(包括 n1).然后再看我们所创造的这个集

NFA的确定化

NFA的确定化:这里指的 NFA 到 DFA的转换(不包括 ε 自动机),构造一个和 NFA 等价的 DFA.书中有介绍两种确定化的方法(子集法和造表法),这里只介绍造表法,造表法是比子集法简单而有效的一种确定化方法. 1,为什么不用子集法?? 在子集法中,如果 NFA 的状态个数 n 比较大,那么,确定化后的 DFA 的状态个数 2^n-1 将更大,其中不少状态是不可达状态. 2,造表法算法的基本思想 把 DFA 中的每一个状态对应 NFA中的一组状态.即由于 NFA 中的 t 是一个多值映射

NFA引擎匹配原理

1       为什么要了解引擎匹配原理 一个个音符杂乱无章的组合在一起,弹奏出的或许就是噪音,同样的音符经过作曲家的手,就可以谱出非常动听的乐曲,一个演奏者同样可以照着乐谱奏出动听的乐曲,但他/她或许不知道该如何去改变音符的组合,使得乐曲更动听. 作为正则的使用者也一样,不懂正则引擎原理的情况下,同样可以写出满足需求的正则,但是不知道原理,却很难写出高效且没有隐患的正则.所以对于经常使用正则,或是有兴趣深入学习正则的人,还是有必要了解一下正则引擎的匹配原理的. 2       正则表达式引擎

编译原理——正规式转DFA算法概述

一.概念概述 给定一个单词,判断该单词是否满足我们给定的单词描述规则,需要用到编译原理中词法分析的相关知识,其中涉及到的两个很重要的概念就是正规式(Regular Expression)和有穷自动机(Finite Automata).正规式是描述单词规则的工具,首先要明确的一点是所有单词组成的是一个无穷的集合,而正规式正是描述这种无穷集合的一个工具:有穷自动机则是识别正规式的一个有效的工具,它分为确定的有穷自动机(Deterministic Finite Automata,DFA)和不确定的有穷

Linux     grep命令,正则表达式

grep命令及正则表达式 grep命令 grep , egrep , fgrep grep,sed,awk 文本处理三剑客 grep: Global search REgular expression and Print out the line;全面查找正则表达式并将匹配到的行显示出来; 正则表达式 正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用;许多程序设计语言都支持利用正则表达式进行字符串操作; 主流的正则引擎又分为三类:DFA;传统型NFA;POSIX NFA; DFA

正则表达式基础、原理及优化

前言 正则表达式使用单个字符串来描述.匹配一系列匹配某个句法规则的字符串.在很多文本编辑器里,正则表达式通常被用来检索.替换那些匹配某个模式的文本.简单说就是一个特殊的字符串去匹配一个字符串.定义了一些规则,用于匹配满足这些规则的字符串. 对于正则表达式应该很多人第一感觉就是很难,完全没有规律,看不懂. 我觉得可能有以下几个原因: 1.读不懂. 各种不同的字符组合一起,难以理解.确实,对于熟悉正则表达式的人来说,一个稍微长点的正则表达式想要理解起来,可能也要花费一定的功夫.可读性差是正则表达式的

刨根究底正则表达式之零——前言

前言 一.缘起 1. 前面在字符编码系列文章的前言中曾说过,类似于字符编码这样基础.重要.应用广泛而又特别容易让人困惑的主题还有字节序(即大小端表示).正则表达式以及浮点数实现.日期时间处理等等.其中,字节序.正则表达式跟字符编码的关系非常密切.字符编码以及字节序的问题已经在字符编码系列文章中介绍过了,这个系列再来讨论正则表达式. 不同于字符编码,正则表达式目前市面上并不缺乏专业著作,比如那本被誉为正则表达式学习圣经的<精通正则表达式>就很值得一读,另外该书的译者余晟先生所写的<正则指引