文章目录
关于课程
这门课程的名字是Decision,Comuptation and Language,但其实它的内容和Theory of Computation是高度一样的,所以如果你对课程有不理解的地方或者想学习更多,都可以通过《Introduction to the Theory of Compuation》这本书去学习。
所以为什么要学习这门课呢?首先计算机的硬件和软件的背后都是对应到了数学,这门课程就是想揭开计算机背后的基础数学。这一门课程会试图解答什么是不能计算的,什么是能计算的,可以计算多快,需要使用多少存储,采用什么计算模型等。
我们之前在INT102中,就接触到了计算机在一些问题的局限性——停机问题。而我们这门课更多是了解计算机背后的数学模型,数学上其实我们的哥德尔不完全性定理也可以指出计算机的局限性。
而Decision,Comuptation and Language是计算理论的一个重要部分,它涉及了计算问题、计算过程、语言研究,这些都是计算问题的本质和特性。
1. Overview
这个课程主要谈论3个部分:
1.Languages
2.Grammars
3.Automata
其中最重要的是Languages(语言)部分,我们会讨论Formal Language(形式语言)的表示符号,这涉及了:
1.如何精确定义语言
2.如何构建识别语言的编译器
3.如何检查字符串是否属于语言
4.如何检查两种语言描述是否相同
它还涉及到了分析语言的工具,这些工具源于对Natural languages(自然语言)和Programming languages(编程语言)的分析。其中:
1.NL(natural languages,自然语言)中,我们希望工具可以识别和理解有效的句子,以便进行语言的语法和语义分析。
2.PL(programming languages,编程语言)中,我们希望工具可以识别和分析有效的程序,以便进行编译、解释和语法分析等操作。
除了直接列出语言的元素外,我们还有一些用于描述语言的符合和方法:
1.Finite state automation(有限状态自动机)
2.Regular expression(正则表达式)
3.Context-free grammar(上下文无关文法)
4.Pushdown Automation(下推自动机)
5.Turing machine(图灵机)
1.1 Languages
语言:一种用于表达特定思想、事实或概念的系统,包括一组符号和用于操作这些符号的规则。
这种语言无法应付我们接下来的计算理论的问题,它们的结构、词汇、语义可能过于复杂,常常会存在歧义或者多样性等问题,我们需要对语言有更精确的定义,那便是Formal languages(形式语言)
比如我们日常口头上的语言就不是形式语言,但是编程语言是形式语言,还包括我们的database query languages(数据库查询语言),file formats(文件格式)等。
介绍Formal languages(形式语言)之前,我们需要先知道以下几个定义:
1.Alphabet(字母表):一个有限的、非空的符号集合
∑
∑
∑。
2.String/word(字符串/词):来自字母表中的符号构成的有限序列。
3.Empty string/ Empty word (空字符串/空词):不包含任何符号的字符串。
4.Length of a string(字符串的长度):字符串中符号的数量,用
∣
w
∣
|w|
∣w∣表示。
5.Reverse of a string(字符串的逆序):将字符串的符号逆序排列,用
w
R
w^R
wR表示。
palindrome(回文串):满足
w
=
w
R
w = w^R
w=wR的字符串
w
w
w。
6.Concatenation (字符串的连接):将两个字符串连接在一起,得到的新字符串是原字符串的右端添加上另一个字符串。(它符合结合律)
7.Prefix and suffix(前缀和后缀):如果
u
,
v
,
w
u, v, w
u,v,w是字符串,且
w
=
u
v
w = uv
w=uv,那么
u
u
u是
w
w
w的prefix(前缀),
v
v
v是
w
w
w的suffix(后缀)。正确前缀是不等于空字符
ϵ
ϵ
ϵ的前缀,同理正确后缀是不等于空字符
ϵ
ϵ
ϵ的后缀。
8.字符串的重复:
w
n
w^n
wn表示有n个w的连接。
9.
∑
∗
∑^*
∑∗(star closure of
∑
∑
∑,
∑
∑
∑的星闭包)表示所有由
∑
∑
∑中符号组成的字符串的集合。这个集合包含了所有可能长度的字符串以及空字符串。
10.
∑
+
∑^+
∑+(positive closure of
∑
∑
∑,
∑
∑
∑的正闭包)表示所有由
∑
∑
∑中符号组成且非空的字符串的集合。因此正闭包和星闭包的区别在于是否包含空字符串
ϵ
ϵ
ϵ。
注意:
∑
∗
∑^*
∑∗和
∑
+
∑^+
∑+都是无穷集合。
ex:假设我们现在有
∑
=
{
a
,
b
}
∑ = \{a, b\}
∑={a,b},那字母表
∑
∑
∑上的字符串可以是
a
b
a
b
,
a
a
a
a
b
b
b
a
abab, aaaabbba
abab,aaaabbba,
w
=
a
b
a
a
a
w = abaaa
w=abaaa表示名为
w
w
w的字符串具有特定的值
a
b
a
a
a
abaaa
abaaa
由于
∣
w
∣
|w|
∣w∣表示字符串的长度,所以
∣
ϵ
∣
=
0
|ϵ| = 0
∣ϵ∣=0
还有
ϵ
w
=
w
ϵ
=
w
ϵw = wϵ = w
ϵw=wϵ=w
字符串的长度满足
∣
w
1
w
2
∣
=
∣
w
1
∣
+
∣
w
2
∣
|w_1w_2| = |w_1| + |w_2|
∣w1w2∣=∣w1∣+∣w2∣
对于字符串
w
=
a
1
a
2
.
.
.
a
n
w = a_1a_2...a_n
w=a1a2...an来说,它字符串的逆序是
w
R
=
a
n
.
.
.
a
2
a
1
w^R = a_n...a_2a_1
wR=an...a2a1
回文串比如
w
=
a
b
a
,
w
R
=
a
b
a
w =aba,w^R = aba
w=aba,wR=aba
我们有两个字符串
w
=
a
1
a
2
.
.
.
a
n
,
v
=
b
1
b
2
.
.
.
b
n
w = a_1a_2...a_n,v = b_1b_2...b_n
w=a1a2...an,v=b1b2...bn,我们可以将两个字符串连接起来,就有
w
v
=
a
1
a
2
.
.
.
a
n
b
1
b
2
.
.
.
b
n
wv = a_1a_2...a_nb_1b_2...b_n
wv=a1a2...anb1b2...bn
对于
∑
=
{
a
,
b
}
∑ = \{a, b\}
∑={a,b},我们有
∑
∗
=
{
a
,
b
,
a
b
,
b
a
,
a
b
a
,
ϵ
,
.
.
.
}
∑^* = \{a, b, ab, ba, aba, ϵ,...\}
∑∗={a,b,ab,ba,aba,ϵ,...}和
∑
+
=
{
a
,
b
,
a
b
,
b
a
,
a
b
a
,
ϵ
,
.
.
.
}
∑^+ = \{a, b, ab, ba, aba, ϵ,...\}
∑+={a,b,ab,ba,aba,ϵ,...}
1.1.1 Fomral languages(形式语言)
形式语言是根据特定的语法规则从有限字母表构造出来的字符串集合。
因此语言L(或者形式语言)是给定字母表 ∑ ∑ ∑的 ∑ ∗ ∑^* ∑∗的一个子集。
我们可以使用concatenation(连接)和closure(闭包)来表示新的语言。
ex:
L
1
L
2
=
{
w
1
w
2
:
w
1
∈
L
1
a
n
d
w
2
∈
L
2
}
L_1L_2 = \{w_1w_2 : w_1 ∈ L_1\ and\ w_2 ∈ L_2\}
L1L2={w1w2:w1∈L1 and w2∈L2}
L
∗
=
{
w
1
w
2
.
.
.
w
n
:
n
≥
0
a
n
d
w
1
w
2
.
.
.
w
n
∈
L
}
L^* = \{w_1w_2...w_n : n ≥ 0\ and\ w_1w_2...w_n∈ L\}
L∗={w1w2...wn:n≥0 and w1w2...wn∈L}
1.2 Grammars
生成符合语法规则的程序或者句子的规则集合。
ex:英语中的语法规则。
注意:1.语法生成的句子可能是毫无含义的。ex:“The house sees me.(房子看到了我。)"
2.语法可以生成任意长的句子,而不需要通过枚举的方式逐个列举出来。
3.句子的语义可以通过语法来描述。ex:逻辑关系词“and”表示句子之间的逻辑关系。
4.语法可以被扩展以处理从句、副词等语法结构,使其能够涵盖更多的语言表达方式。
5.虽然自然语言的句子无法完全通过语法来定义,但对于编程语言来说,可以通过语法来完整地定义句子的结构。
1.2.1 Applications in programming languages(stages of compilation) (在编译过程不同阶段中的应用)
1.Lexical Analysis(词法分析):这是编译过程的第一个阶段,它将一个字符串分割为tokens(标记),例如变量名、运算符、标签等。
在编程语言中,tokens(标记)是基本构建块,它们代表了程序中的各种元素。
在自然语言中,tokens(标记)通常是由连续的字母组成的字符串。这使得词法分析器能够相对容易地识别和提取标记,因为它们遵循了明显的模式。
ex:
2.Parsing / Parse Tree (语法分析 / 解析树)
Parsing(语法分析):在这个阶段,词法分析产生的tokens(标记)序列被转换为语法树或语法分析树。语法分析器检查tokens(标记)序列是否符合编程语言的语法规则,并构建表示程序结构的树状数据结构。
Parse Tree(解析树):解析树是一个有序、有根的树结构,它表示了一个字符串的句法结构,这个字符串是根据某个context-free grammar(上下文无关文法)生成的。
解析树用于表示字符串的句法结构,它展示了tokens(标记)之间的关系,以及它们如何根据语法规则组合成句子的。
ex:
3.Code generation(代码生成)
Code generation(代码生成):是编译器中的一个环节,它将源代码的中间表示形式转换为目标系统可以直接执行的形式,例如机器码。这个过程将源代码转换为目标系统能够理解和执行的形式,使得程序能够在目标系统上运行。编译器在这一阶段将上一步的语法树转换为目标代码。
4.Code optimization(代码优化)
Code optimization(代码优化):这是编译过程的最后一个主要阶段,它旨在改善生成的目标代码的质量,以使其某些方面更加高效或使用更少的资源。通常,计算机程序可以被优化以使其执行速度更快,或者使其能够在更少的内存存储或其他资源下运行,或者减少功耗。
1.3 Automata
Automaton(自动机)是数字计算机的一个抽象模型。自动机可以是(Finite state machine)有限状态机、(Turing machine)图灵机等。
1.4 Mathematical preliminaries(数学基础)
这部分比较简单,就不详细叙述。
2. DFA(Deterministic Finite Automata, 确定有限自动机)
2.1 Definition
如图,这是一个DFA的state diagram(状态图),我们其实可以叫它transition graph(转移图):
它有3个状态,分别是
q
1
,
q
2
,
q
3
q_1, q_2, q_3
q1,q2,q3,其中
q
1
q_1
q1是start state(起始状态),它是用一个指向它的无出发点的箭头表示,
q
2
q_2
q2是accept state(接受状态),用双圈表示,图中这些从一个状态指向另一个状态的箭头称为transact(转移)。
如图,当这个自动机
M
1
M_1
M1接受到输入字符串,它会进行处理,最后产生一个输出,输出的结果是accept(接受)或reject(拒绝),判断的依据是其是否最后处于accept state(接受状态),而中间自动机会一个一个地接受字符串的符号,从而在状态之间转移。
下面以输入的字符串是1101给自动机
M
1
M_1
M1进行处理,步骤如下:
1.开始处于start state(起始状态)
q
1
q_1
q1。
2.读到1,沿着转移从
q
1
q_1
q1到
q
2
q_2
q2。
3.读到1,沿着转移从
q
2
q_2
q2到
q
2
q_2
q2。
4.读到0,沿着转移从
q
2
q_2
q2到
q
3
q_3
q3。
5.读到1,沿着转移从
q
3
q_3
q3到
q
2
q_2
q2。
6.输出接受,因为在输入字符串的末端
M
1
M_1
M1处于接受状态
q
2
q_2
q2。
其实这个自动机
M
1
M_1
M1还可以接受字符串1、01、11等,这种以1结尾的字符串,它还可以接受100、0100、01010000等这种在最后一个1的后面有偶数个0的字符串。
我们现在看一下DFA的定义。DFA是一种能够接受或拒绝给定字符串的有限状态机,通过一系列状态来唯一确定字符串的状态序列。它被用作识别一种特定类别的形式语言,它的应用包括:
1.Lexical analysis(词法分析):从头到尾扫描输入程序,并将其分割成标识符、常量和关键字等标记,同时移除注释和空格。换句话说它确定编程语言里的tokens(标记)。
2.Model checking(模型检查):用于推理系统,以证明它们满足有用的属性。
3.因此DFA在计算机科学、形式语言理论以及其他领域中有广泛作用。
DFA的作用很强大,但是它仍有局限性,其没有记录功能,且不能描述某些复杂的语言结构。
ex:1.包含字母a比字母b的字符串多(因为DFA无法记录输入符号的数量)。
2.判断回文子串(因为DFA无法记录字符串)。
3.没有括号嵌套限制的算术表达式(由于括号嵌套是任意的,所以这种复杂的结构超出了DFA的能力范围)。
我们现在看一下DFA的形式化定义,这方便我们更清楚地描述DFA。
DFA是一个5元组
M
=
(
Q
,
Σ
,
δ
,
q
,
F
)
M = (Q, Σ, δ, q, F)
M=(Q,Σ,δ,q,F),其中
1.
Q
Q
Q是一个有穷集合,称为状态集,是DFA里的所有状态的集合。
2.
Σ
Σ
Σ是一个有穷集合,称为字母集,指明了所有允许的输入符号。
3.
δ
:
Q
×
Σ
→
Q
δ : Q × Σ → Q
δ:Q×Σ→Q是transition function(转移函数),定义了动作规则,如果DFA有从状态x到状态y标有输入符号1的箭头,这表明当它处于状态x时读到1,则转移到状态y。
4.
q
∈
Q
q ∈ Q
q∈Q是intial state(初始状态),指明了DFA开始的状态。
5.
F
⊆
Q
F ⊆ Q
F⊆Q是a set of accepting/terminal/final states(接收状态/终止状态/终结状态的集合),指明了DFA所有接受的状态的集合。
注意:1.
F
F
F如果等于空集那就没有接受状态,这是允许的。
2.对各个可能的输入符号从每一个状态恰好引出一个转移箭头,例如前文例子中的DFA的字母表有两个元素,因此每个状态都有两个指出的箭头。
现在我们把上面的例子中的DFA转换为形式化定义的DFA。
M
1
=
(
Q
,
Σ
,
δ
,
q
1
,
F
)
M_1 = (Q, Σ, δ, q_1, F)
M1=(Q,Σ,δ,q1,F)
1.
Q
=
{
q
1
,
q
2
,
q
3
}
Q = \{q_1, q_2, q_3\}
Q={q1,q2,q3}。
2.
Σ
=
{
0
,
1
}
Σ = \{0, 1\}
Σ={0,1}。
3.
δ
δ
δ描述为
4.
q
1
q_1
q1是起始状态。
5.
f
=
{
q
2
}
f= \{q_2\}
f={q2}。
这里我们用表格来描述转移函数,这是一种更简单描述转移函数的形式。
因此我们描述DFA的transition function(转移函数)的形式有两种:1.transition graph(转移图),如图,它包含了
∣
Q
∣
|Q|
∣Q∣个顶点,每个顶点表示一个状态,并根据
δ
(
q
i
,
a
)
=
q
j
δ(q_i,a) = q_j
δ(qi,a)=qj,有从
q
i
q_i
qi指向
q
j
q_j
qj的边,这条边会被标记上输入符号a。
q
1
q_1
q1表示初始状态,而
q
f
q_f
qf表示接收状态。
它包含了DFA的全部信息。
2.transition table(状态转移表)它可以是一个表格或矩阵,如图,列出了DFA的所有状态以及输入字母表中的每个符号,并指出了从一个特定状态通过特定输入符号转移到另一个状态的情况。
它只是DFA形式化表达的一部分。
其实对于转移函数来说,我们可以拓展它的输入,以便它可以处理由多个输入letter(符号)组成的word(单词)。
因此我们从
δ
:
Q
×
Σ
→
Q
δ : Q × Σ → Q
δ:Q×Σ→Q得到
δ
∗
:
Q
×
Σ
∗
→
Q
δ^* : Q × Σ ^* → Q
δ∗:Q×Σ∗→Q,详细如下:
δ
∗
(
q
,
ϵ
)
=
q
δ^*(q, ϵ) = q
δ∗(q,ϵ)=q for all
q
∈
Q
q ∈ Q
q∈Q
δ
∗
(
q
,
w
a
)
=
δ
(
δ
∗
(
q
,
w
)
,
𝑎
)
δ^*(q, wa) = δ (δ^* (q, w) , 𝑎)
δ∗(q,wa)=δ(δ∗(q,w),a) for all
q
∈
Q
;
w
∈
Σ
∗
;
a
∈
Σ
q ∈ Q; w ∈ Σ^* ; a ∈ Σ
q∈Q;w∈Σ∗;a∈Σ
ex:
δ
(
q
0
,
𝑎
)
=
q
1
δ (q_0, 𝑎) = q_1
δ(q0,a)=q1 and
δ
(
q
1
,
b
)
=
q
2
δ (q_1, b) = q_2
δ(q1,b)=q2
我们可以得到
δ
(
q
0
,
a
b
)
=
q
2
δ (q_0, ab) = q2
δ(q0,ab)=q2
过程如下:
δ
(
q
0
,
a
b
)
=
δ
(
δ
∗
(
q
0
,
a
)
,
b
)
δ (q_0, ab) = δ ( δ^*(q_0, a),b)
δ(q0,ab)=δ(δ∗(q0,a),b)
δ
(
q
0
,
a
)
=
δ
(
q
0
,
a
)
=
q
1
δ (q0, a) = δ (q0, a) = q_1
δ(q0,a)=δ(q0,a)=q1
δ
(
q
0
,
a
b
)
=
δ
(
q
1
,
b
)
=
q
2
δ (q_0, ab) = δ (q1, b) = q2
δ(q0,ab)=δ(q1,b)=q2
2.2 Partial function
让我们先看一个例子。
我们根据这个transition graph(转移图)可以得出它的转移函数,我这里用transition table(状态转移表)表示。
我们发现这个字符串它会接受以1开头的所有字符,因此如果它的第一个输入是0的话,他一定会被拒绝。因此我们可以将transition function(转移函数)改写成一个partial function(部分函数)从而减少我们定义的状态和字符。使用partial function后,某些输入组合在转移函数中可能没有定义,在这种情况下DFA会直接拒绝它的输入。
因此我们可以将转移函数用partial function写成这样。
2.3 Design DFA
虽然DFA没有记录功能,他没法告诉你两个字符串谁更长,而且我们也不可能有一个无限状态的DFA(状态集是一个有穷集合),但我们依然可以解决以下这样的问题:我们现在的字母表是
{
0
,
1
}
\{0, 1\}
{0,1},我们判断一个字符串是否含有奇数个1。对于这个问题我们只需要两种状态,有偶数个1的状态、有奇数个1的状态,而我们刚开始的初始状态刚好是空字符串,这个状态刚好也是偶数个1的状态,当输入的符号是0的时候它继续停留在当前状态,如果是输入的符号是1的时候我们就让它切换到另一个状态,其中奇数个1的状态是我们的接收状态,因此我们有了DFA的五个部分,我们便设计好了一个DFA,这里用transition graph展现。
现在我们尝试设计一个DFA,它可以识别含有001作为子串的所有字符串。它的思路其实和字符串匹配类似,我们在寻找001作为子串的时候先尝试寻找到第一个0,然后如果下一个字符还是0,那就继续看下一个字符是否还是1,如果这些都满足那就一直接受,如果中间任何一步不满足就回到最原始的状态。因此我们有了DFA的五个部分,结果如下。
我们再设计一个更具挑战性的DFA,它接受所有b开头的字符串,然后这个字符串会以d结尾,b和d都是一个,但是中间会有至少一个a,例如bad,baad,baaad等。
所以我们这个初始状态后需要先判断下一个字符是否是b,否则我们可以让它待在一个状态,这个状态就代表拒绝的字符串,然后前一个判断后面判断是否下一个字符是a,如果是就继续判断下一个字符是不是d,如果是就到最后的接受状态,如果还是a就继续停留在当前状态,如果是b那么就去拒绝状态,后两个状态同理。因此我们得到的DFA如图。
但其实我们可以使用前面学习的partial function从而直接拒绝一些不符合的结果,如果第一个字符串是b就继续进行判断,否则直接拒绝,后面几步同理,从而我们得到的DFA如图。
2.4 Languages defined by DFA
DFA
M
M
M接受的所有字符串的集合被称为语言,记作
L
(
M
)
L(M)
L(M),因此
L
(
M
)
=
w
∈
Σ
∗
:
δ
∗
(
q
0
,
w
)
∈
F
L(M) = {w ∈ Σ^* : δ^*(q_0, w) ∈ F}
L(M)=w∈Σ∗:δ∗(q0,w)∈F,我们称
M
M
M识别
L
(
M
)
L(M)
L(M)或接受
L
(
M
)
L(M)
L(M)。
因此我们有以下几个结论:1.任何有限语言都可以被某个DFA所接受。
2.DFA可能接受若干个字符串,但是它永远只能识别一种语言。
3.如果DFA不接受任何字符串,那么它也识别一种语言,即空语言。
Reugular language(正则语言):如果存在一个确定的有限自动机 M M M,使得 M M M接受的语言正好是语言 A A A那么语言(表示为 A = L ( M ) A = L(M) A=L(M)) A A A就是regular language(正则语言)。
所以比如我们前文中第一个例子,如图,这个DFA
M
M
M接受的语言是至少含有一个1并且在最后的1后面有偶数个0的字符串。
而在我们的2.3节中设计DFA中第一个例子,如图,这个DFA
M
M
M接受的语言是有奇数个1的字符串。
因此如果我们想要证明一个语言是正则的,那就是要我们展现出这个语言可以被一个DFA所接受。
2.4 Regular operations(正则计算)
我们前文主要介绍了两个概念:DFA和正则语言,现在我们介绍它们的性质。
在平时的数学计算中我们计算的对象是数,而运算符是数学运算符,而在这门课中我们计算的对象是语言,现在我们定义语言的三种计算,称为regular operations(正则计算)。
1.Union(并)
A
∪
B
=
{
w
:
w
∈
A
o
r
w
∈
B
}
A ∪ B = \{w : w ∈ A\ or \ w ∈ B\}
A∪B={w:w∈A or w∈B}
2.Concatenation(连接)
A
B
=
{
w
w
′
:
w
∈
A
a
n
d
w
′
∈
B
}
AB = \{ww^′ : w ∈ A \ and \ w^′ ∈ B\}
AB={ww′:w∈A and w′∈B}
3.Star(星号)
A
∗
=
{
u
1
u
2
.
.
.
u
k
:
k
≥
0
a
n
d
u
i
∈
A
f
o
r
a
l
l
i
=
1
,
2
,
.
.
.
,
k
}
A* = \{u_1u_2. . . u_k: k ≥ 0 \ and \ u_i ∈ A \ for \ all \ i = 1, 2, . . . , k\}
A∗={u1u2...uk:k≥0 and ui∈A for all i=1,2,...,k}
这些在前文中已经介绍过,这里还是通过一个例子帮助理解:
现在我们有两个语言
A
=
{
0
,
01
}
A = \{0, 01\}
A={0,01}和
B
=
{
1
,
10
}
B = \{1, 10\}
B={1,10},那我们的
A
∪
B
=
{
0
,
01
,
1
,
10
}
A ∪ B = \{0, 01, 1, 10\}
A∪B={0,01,1,10},
A
B
=
{
01
,
010
,
011
,
0110
}
AB = \{01, 010, 011, 0110\}
AB={01,010,011,0110},
A
∗
=
{
ϵ
,
0
,
01
,
00
,
001
,
010
,
0101
,
000
,
0001
,
00101
,
.
.
.
}
A^* = \{ϵ, 0, 01, 00, 001, 010, 0101, 000, 0001, 00101, . . .\}
A∗={ϵ,0,01,00,001,010,0101,000,0001,00101,...}.
2.4.1 正则运算下封闭
数学中我们有closed(封闭)这个概念,设
N
=
{
1
,
2
,
3
,
.
.
.
}
N = \{1, 2, 3, ...\}
N={1,2,3,...}是自然数集,那
N
N
N在乘法运算下封闭的意思是,对
N
N
N中任意的
x
x
x和
y
y
y,乘积
x
×
y
x × y
x×y也在
N
N
N中。相反地,
N
N
N在除法下不是封闭的,比如
1
/
2
1/2
1/2不在
N
N
N中。
因此我们现在在对象是一个集合的情况下,我们如果在某个运算应用与一个对象集合的成员的得到的对象仍在这个集合中,则称这个对象集合在该运算下is closed(封闭)。我们下面会介绍正则语言类在三种正则运算下封闭。
定理1:正则语言类在并运算下封闭。换言之,如果 A 1 A_1 A1和 A 2 A_2 A2是正则语言,则 A 1 ∪ A 2 A_1∪ A_2 A1∪A2也是正则语言。
证明思路:由于
A
1
A_1
A1和
A
2
A_2
A2是正则语言,所以存在DFA
M
1
M_1
M1识别
A
1
A_1
A1和DFA
M
2
M_2
M2识别
A
2
A_2
A2,为了证明
A
1
∪
A
2
A_1∪ A_2
A1∪A2也是正则语言,我们需要证明存在一个DFA可以识别
A
1
∪
A
2
A_1∪ A_2
A1∪A2,称之为
M
M
M。
这是一个构造性证明,我们要利用DFA
M
1
M_1
M1和
M
2
M_2
M2去构造
M
M
M。这个DFA必须要在
M
1
M_1
M1或
M
2
M_2
M2接受的时候接受它的输入串。因此
M
M
M必须模拟
M
1
M_1
M1和
M
2
M_2
M2,当其中一个接受时,
M
M
M也接受。因为DFA不能记录,我们不能将它先判断一遍是否被
M
1
M_1
M1接受,如果不接受再判断是否被
M
2
M_2
M2,所以我们必须同时进行同时判断,因此我们要同时模拟
M
1
M_1
M1和
M
2
M_2
M2,然后当一个字符串输入时,它的状态将时一对状态。如果
M
1
M_1
M1有
k
1
k_1
k1个状态,
M
2
M_2
M2有
k
2
k_2
k2个状态,那么一共有
k
1
×
k
2
k_1 × k_2
k1×k2对状态,其中一个状态来自
M
1
M_1
M1,而另一个状态来自
M
2
M_2
M2,这个数目就是M的状态数。由于
M
1
M_1
M1和
M
2
M_2
M2的一对状态是
M
M
M的一个状态,所以当需要转移的时候,我们同时修改
M
1
M_1
M1和
M
2
M_2
M2的状态,当
M
1
M_1
M1或
M
2
M_2
M2是接收状态时,
M
M
M是接受状态。
证明:设
M
1
M_1
M1识别
A
1
A_1
A1,
M
2
M_2
M2识别
A
2
A_2
A2,其中
M
1
=
(
Q
1
,
Σ
,
δ
1
,
q
1
,
F
1
)
M_1 = (Q_1, Σ, δ_1,q_1, F_1)
M1=(Q1,Σ,δ1,q1,F1),
M
2
=
(
Q
2
,
Σ
,
δ
2
,
q
2
,
F
2
)
M_2 = (Q_2, Σ, δ_2,q_2, F_2)
M2=(Q2,Σ,δ2,q2,F2),构造识别
A
1
∪
A
2
A_1∪ A_2
A1∪A2的
M
=
(
Q
,
Σ
,
δ
,
q
,
F
)
M = (Q, Σ, δ,q, F)
M=(Q,Σ,δ,q,F)。
1.
Q
=
Q
1
×
Q
2
=
{
(
q
1
,
q
2
)
:
q
1
∈
Q
1
a
n
d
q
2
∈
Q
2
}
Q = Q_1 × Q_2 = \{(q_1, q_2): q_1 ∈ Q_1 \ and \ q_2 ∈ Q_2\}
Q=Q1×Q2={(q1,q2):q1∈Q1 and q2∈Q2}
2.
Σ
Σ
Σ与
M
1
M_1
M1和
M
2
M_2
M2的字母表相同,在这个定理以及随后相似的定理中,为了简单起见,我们假设
M
1
M_1
M1和
M
2
M_2
M2有相同的字母表。如果它们有不相同的字母表,该定理仍然成立,但是需要令
Σ
=
Σ
1
∪
Σ
2
Σ = Σ_1∪ Σ_2
Σ=Σ1∪Σ2
3.
δ
:
Q
×
Σ
→
Q
δ : Q × Σ → Q
δ:Q×Σ→Q 即
δ
(
(
q
1
,
q
2
)
,
a
)
=
(
δ
(
q
1
,
a
)
,
δ
(
q
2
,
a
)
)
,
a
∈
Σ
δ((q_1, q_2), a) = (δ(q_1, a) , δ(q_2, a)), a ∈ Σ
δ((q1,q2),a)=(δ(q1,a),δ(q2,a)),a∈Σ
4.
q
0
=
(
q
1
,
q
2
)
q_0 = (q_1, q_2)
q0=(q1,q2)
5.
F
=
{
(
q
1
,
q
2
)
:
q
1
∈
F
1
o
r
q
2
∈
F
2
}
F = \{(q1, q2): q_1 ∈ F_1 \ or \ q_2 ∈ F_2\}
F={(q1,q2):q1∈F1 or q2∈F2}这个式子等价于
F
=
(
F
1
×
Q
2
)
∪
(
Q
1
×
F
2
)
F = (F_1 × Q_2)∪(Q_1 × F_2)
F=(F1×Q2)∪(Q1×F2)
下面我们验证这个
M
M
M的正确性,下面是简单证明:
δ
∗
(
(
q
1
,
q
2
)
,
w
)
=
(
δ
∗
(
q
1
,
w
)
,
δ
∗
(
q
2
,
w
)
)
δ^*((q_1, q_2), w) = (δ^*(q_1, w) , δ^*(q_2, w))
δ∗((q1,q2),w)=(δ∗(q1,w),δ∗(q2,w))
δ
∗
(
(
q
1
,
q
2
)
,
w
)
∈
F
⇔
δ
∗
(
q
1
,
w
)
∈
F
1
o
r
δ
∗
(
q
2
,
w
)
∈
F
2
δ^*((q_1, q_2), w) ∈ F ⇔ δ^*(q_1, w) ∈ F_1 or δ^*(q_2, w) ∈ F_2
δ∗((q1,q2),w)∈F⇔δ∗(q1,w)∈F1orδ∗(q2,w)∈F2
M
a
c
c
e
p
t
s
w
⇔
δ
∗
(
q
1
,
w
)
∈
F
1
o
r
δ
∗
(
q
2
,
w
)
∈
F
2
M \ accepts \ w ⇔ δ^*(q_1, w) ∈ F_1 \ or \ δ^*(q_2, w) ∈ F_2
M accepts w⇔δ∗(q1,w)∈F1 or δ∗(q2,w)∈F2
M
a
c
c
e
p
t
s
w
⇔
M
1
a
c
c
e
p
t
s
w
o
r
M
2
a
c
c
e
p
t
s
w
M \ accepts \ w ⇔ M_1 \ accepts \ w \ or \ M_2 \ accepts \ w
M accepts w⇔M1 accepts w or M2 accepts w
定理2:正则语言类在连接运算下封闭。换言之,如果 A 1 A_1 A1和 A 2 A_2 A2是正则语言,则 A 1 A 2 A_1A_2 A1A2也是正则语言。
证明思路:和上一个证明相似,我们需要设计一个DFA M M M,其中模拟 M 1 M_1 M1和 M 2 M_2 M2,当 M 1 M_1 M1接受第一段且 M 2 M_2 M2接受第二段的时候, M M M才接受输入。现在的问题是M不知道在什么地方将输入分开,因此我们需要非确定性。
3. NFA(Nondeterministic Finite Automata,非确定性有限自动机)
对于确定性有限状态自动机(DFA),在给定任何输入符号的情况下,机器下一个状态的转移是唯一确定的。而在非确定性有限状态自动机(NFA)中,在任何一点,下一个状态可能存在若干个选择。
非确定性是确定性的推广,因此每一台DFA都是NFA。
如图所示,这是一个NFA,它有一些DFA没有的特征。
1.DFA的每一个状态对于字母表中的每一个符号总是恰好有一个转移箭头射出。而图中
q
1
q_1
q1对于1有两个射出的箭头。在NFA中一个状态对于字母表中的每一个符号可能有0个、1个或多个射出的箭头(partial function的DFA可能有的符号没有定义,但是每一个符号只会有一个箭头而不会有多个)。
2.DFA中的转移箭头上的标号都是来自于字母表的符号。而图中
q
2
q_2
q2中的转移箭头上是
ε
ε
ε。NFA中的转移箭头可以标记字母表中的符号或
ε
ε
ε。因此一个状态可能射出0个、1个或多个
ε
ε
ε的箭头。
ε
ε
ε是什么意思呢?
它表示空转移或空输入,在不消耗任何输入的情况下,从一个状态转移到另一个状态。
那NFA如何进行计算呢?
在NFA中,当自动机处于一个状态并且有多种选择时,它会分裂成多个copies(备份)或threads(线程),每个备份独立地进行计算,遵循各自的路径。这些备份以同时处于一组状态,而不仅仅是单个状态,并且NFA会沿着所有可能的计算路径并行地进行计算。在这种情况下,如果某个备份处于一个状态,并且下一个输入符号在该状态的任何传出边上都没有出现,那么该线程就会"死亡"或"崩溃",也就是说,它无法继续进行计算。当任何一个备份的末端状态是接受状态时,这台NFA接受输入字符串。当任何一个
以前图的NFA为例,我们输入字符串010110。
从起始状态
q
1
q_1
q1开始,读第一个符号0,只有一种选择,即回到
q
1
q_1
q1,所以状态还是
q
1
q_1
q1。
下一个符号1,对于
q
1
q_1
q1来说有两种选择,所以此处我们会分裂成两个,分别跟踪一种选择,一个可能时停留在
q
1
q_1
q1,一个可能是转移到
q
2
q_2
q2,由于
q
2
q_2
q2状态上有一个带有
ε
ε
ε的转移箭头离开
q
2
q_2
q2,所以机器再次分裂,一个可能是留在
q
2
q_2
q2,一种可能是转移到
q
3
q_3
q3。
第三个符号0,第一个备份的
q
1
q_1
q1保持不变,而第二个备份的
q
2
q_2
q2备份转移到
q
3
q_3
q3,而第三个备份的
q
3
q_3
q3没有关于0的箭头可以转移,所以这个备份就会“死亡”。
第四个符号1,第一个备份的
q
1
q_1
q1会像第二个符号1一样分裂成两个备份
q
1
q_1
q1和
q
2
q_2
q2,然后
q
2
q_2
q2分裂成
q
2
q_2
q2和
q
3
q_3
q3,而原来的那个
q
3
q_3
q3状态在这一步转移到了
q
4
q_4
q4。
第五个符号1,和上一个类似,先是备份的
q
1
q_1
q1会分裂成
q
2
q_2
q2、
q
3
q_3
q3,而备份的
q
2
q_2
q2直接“死亡”,备份的
q
3
q_3
q3转移到
q
4
q_4
q4,备份的
q
4
q_4
q4依然在
q
4
q_4
q4,这里有两个
q
4
q_4
q4的备份,我们只需要记住机器最后可能会出现的状态,而不需要计算它的概率,因此我们可以手动去掉一个
q
4
q_4
q4的备份。
第六个符号0,备份的
q
1
q_1
q1不变,备份的
q
2
q_2
q2转移到
q
3
q_3
q3,备份的
q
3
q_3
q3“死亡”,备份的
q
4
q_4
q4不变。因为机器可能是接受状态
q
4
q_4
q4,所以机器会接受这个字符串。
具体也可以参考该图。
其实我们最后可以发现这个机器接受所有含101或11作为子串的字符串。
3.1 Formal Definition of NFA(非确定性有穷自动机的形式化定义)
为了书写这个定义,我们需要引入一些额外的符号。
对任意的集合
Q
Q
Q,记
P
(
Q
)
P(Q)
P(Q)
Q
Q
Q的所有子集组成的集合,记作
P
(
Q
)
=
{
R
:
R
⊆
Q
}
P(Q) = \{R : R ⊆ Q\}
P(Q)={R:R⊆Q},称
P
(
Q
)
P(Q)
P(Q)是
Q
Q
Q的power set(幂集)。
对任意的字母表,我们定义
Σ
ϵ
Σ_ϵ
Σϵ,其满足
Σ
ϵ
=
Σ
∪
{
ϵ
}
Σ_ϵ = Σ ∪\{ϵ\}
Σϵ=Σ∪{ϵ}。
因此现在转换函数
δ
:
Q
×
Σ
ϵ
→
P
(
Q
)
δ : Q × Σ_ϵ→ P(Q)
δ:Q×Σϵ→P(Q)。
所以我们现在给出NFA的形式化定义:A nondeterministic finite automaton(非确定性有穷自动机,NFA)是一个5元组
M
=
(
Q
,
Σ
,
δ
,
q
,
F
)
M = (Q, Σ, δ, q, F)
M=(Q,Σ,δ,q,F),其中
1.
Q
Q
Q是有穷的状态集;
2.
Σ
Σ
Σ是有穷的字母表;
3.
δ
:
Q
×
Σ
ϵ
→
P
(
Q
)
δ : Q × Σ_ϵ→ P(Q)
δ:Q×Σϵ→P(Q)是转移函数。
4.
q
∈
Q
q ∈ Q
q∈Q是起始状态;
5.
F
⊆
Q
F ⊆ Q
F⊆Q是接受状态。
因此对于前文的例子,如图。
它的形式化定义是
M
=
(
Q
,
Σ
,
δ
,
q
,
F
)
M = (Q, Σ, δ, q, F)
M=(Q,Σ,δ,q,F)。
1.
Q
=
q
1
,
q
2
,
q
3
,
q
4
Q = {q_1, q_2, q_3, q_4}
Q=q1,q2,q3,q4。
2.
Σ
=
0
,
1
Σ = {0, 1}
Σ=0,1。
3.
δ
δ
δ如下表。
4.
q
1
q_1
q1是起始状态。
5.
F
=
q
4
F = {q_4}
F=q4是接收状态的集合。
NFA计算的形式化定义也和DFA计算的形式化定义类似。设
M
=
(
Q
,
Σ
,
δ
,
q
,
F
)
M = (Q, Σ, δ, q, F)
M=(Q,Σ,δ,q,F)是一台NFA,
w
∈
Σ
∗
w ∈ Σ^∗
w∈Σ∗,如果我们能把
w
w
w写成
w
=
y
1
y
2
.
.
.
y
m
w = y_1y_2...y_m
w=y1y2...ym,这里的
y
i
∈
Σ
ϵ
y_i ∈ Σ_ϵ
yi∈Σϵ其中
1
≤
i
≤
m
1 ≤ i ≤ m
1≤i≤m,并且存在
Q
Q
Q中的状态序列
r
1
,
r
2
,
…
,
r
m
r_1, r_2,… , r_m
r1,r2,…,rm满足下述3个条件:
1.
r
0
=
q
r_0 = q
r0=q
2.
r
i
+
1
∈
δ
(
r
i
,
y
i
+
1
)
)
,
f
o
r
i
=
0
,
1
,
.
.
.
,
m
−
1
r_i+1 ∈ δ(r_i, y_{i+1})), for \ i = 0, 1, . . . , m − 1
ri+1∈δ(ri,yi+1)),for i=0,1,...,m−1
3.
r
m
∈
F
r_m ∈ F
rm∈F
则称
M
M
M接受
w
w
w。
3.2 Difference between DFA and NFA(DFA 和 NFA 的不同)
1.转移函数的不同。
2.NFA返回的是状态的集合而非DFA的单个状态。
3.NFA的字母表允许
ϵ
ϵ
ϵ。
4.每一个DFA都是一个NFA。
3.3 Equivalence of DFA and NFA(DFA 与 NFA的等价性)
首先我们需要知道NFA接受的语言,被NFA接受的语言定义如下:对于 M = ( Q , Σ , δ , q , F ) M = (Q, Σ, δ, q, F) M=(Q,Σ,δ,q,F),它接受的语言 L ( M ) = { w ∈ Σ ∗ : M a c c e p t s w } L(M) = \{w ∈ Σ^∗: M \ accepts \ w \} L(M)={w∈Σ∗:M accepts w}.
然后DFA和NFA其实识别相同的语言类,这个等价性有点出人意料,因为似乎NFA比DFA更加强大,但是实际上只是在描述语言的时候NFA可能比DFA更容易些。
如果两台机器识别同样的语言,则称它们是等价的。
我们只需要知道DFA是NFA的一种特殊情况,每一个NFA有一个等价的DFA,因此我们可以将任意的NFA转化为与之等价的DFA,因此DFA和NFA是由相同能力的,因为它们可以接受相同的语言。
因此我们现在先尝试一下将前文中的例子中的NFA转化为DFA。
其实这个转化有两种方法,我们既可以通过NFA来确定DFA的定义,也可以通过NFA的计算来将NFA转化为DFA。
比如这里我们先用后一种方式来完成这里的转化。
我们需要知道,因为NFA在输入的时候可能会有多个选择,从而造成备份,因此产生多个状态的情况,一次输入造成的多个状态,我们现在变成一个集合,从而保证满足DFA的要求。
我们这里可以再强调一下遇到
ϵ
ϵ
ϵ怎么解决,我们遇到
ϵ
ϵ
ϵ就产生一个备份,然后转移,将这几个备份结果作为一个集合记作一个状态。
这里我们可以先看一下状态集合
R
R
R的
ϵ
ϵ
ϵ闭包的定义,
E
(
R
)
E(R)
E(R)表示从
R
R
R出发通过0个或多个
ϵ
ϵ
ϵ可达到的所有状态的集合。
下面给出一个例子。
这里
E
(
q
1
)
=
{
q
1
,
q
2
,
q
3
,
q
4
}
E(q_1) = \{q_1,q_2, q_3, q_4\}
E(q1)={q1,q2,q3,q4},
E
(
q
2
)
=
{
q
2
,
q
4
}
E(q_2) = \{q_2, q_4\}
E(q2)={q2,q4}。
现在我们回到前面的问题。我们先确定初始状态,是
{
q
1
}
\{q_1\}
{q1},然后确定转移函数,如果输入的是0,那下一个状态还是
{
q
1
}
\{q_1\}
{q1},如果输入的是1,那下一个状态是
{
q
1
,
q
2
,
q
3
}
\{q_1,q_2,q_3\}
{q1,q2,q3}(因为
q
2
q_2
q2可以直接不输入就转移到
q
3
q_3
q3)。
下一步同理,我们看状态
{
q
1
,
q
2
,
q
3
}
\{q_1,q_2,q_3\}
{q1,q2,q3},确定它的转移函数,如果输入的是0,因为里面包含
q
1
q_1
q1所以还可以是
q
1
q_1
q1,对于
q
2
q_2
q2来说它会转移到
q
3
q_3
q3,但是对于
q
3
q_3
q3来说它会直接“死亡”,因此最后的结果是
{
q
1
,
q
3
}
\{q_1,q_3\}
{q1,q3}。同理,如果输入的是1,因为里面包含
q
1
q_1
q1,所以会转移到
q
1
,
q
2
,
q
3
q_1,q_2,q_3
q1,q2,q3,对于
q
2
q_2
q2来说它会直接“死亡”,对于
q
3
q_3
q3来说它会转移到
q
4
q_4
q4,因此下一个状态是
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}.
其实到这一步你会发现,由于我们前面的一些计算,后面的转移函数并不复杂,比如如果现在的集合里有
q
1
q_1
q1,输入的是1,那结果一定有
q
1
,
q
2
,
q
3
q_1,q_2,q_3
q1,q2,q3,同理如果现在的集合里有
q
4
q_4
q4输入的是0或1,那最后的结果里还是会有
q
4
q_4
q4。
我们重复上面的步骤,对于状态
{
q
1
,
q
3
}
\{q_1,q_3\}
{q1,q3},如果输入0,它会转移到
{
q
1
}
\{q_1\}
{q1},如果输入1,它会转移到
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}。
对于
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}来说,如果输入0,它会转移到
{
q
1
,
q
3
,
q
4
}
\{q_1,q_3,q_4\}
{q1,q3,q4},如果输入1,它会转移到
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}。
对于
{
q
1
,
q
3
,
q
4
}
\{q_1,q_3,q_4\}
{q1,q3,q4}来说,如果输入0,它会转移到
{
q
1
,
q
4
}
\{q_1,q_4\}
{q1,q4},如果输入1,它会转移到
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}。
对于
{
q
1
,
q
4
}
\{q_1,q_4\}
{q1,q4}来说,如果输入0,他会转移到
{
q
1
,
q
4
}
\{q_1,q_4\}
{q1,q4},如果输入1,它会转移到
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}。
至此为止所有的状态我们都分析过了,然后由于原来的NFA的接收状态是
{
q
4
}
\{q_4\}
{q4},所以按照定义,我们现在的状态中的集合中如果有
{
q
4
}
\{q_4\}
{q4}那就应该被接受,所以我们的
{
q
1
,
q
2
,
q
3
,
q
4
}
\{q_1,q_2,q_3,q_4\}
{q1,q2,q3,q4}、
{
q
1
,
q
3
,
q
4
}
\{q_1,q_3,q_4\}
{q1,q3,q4}、
{
q
1
,
q
4
}
\{q_1,q_4\}
{q1,q4}三个状态都是接受状态,最后的结果如下。
下面我们再看一个例子:
我们可以根据定义去完成NFA到DFA的转化。
1.状态集
Q
′
=
P
(
Q
)
=
{
∅
,
{
1
}
,
{
2
}
,
{
3
}
,
{
1
,
2
}
,
{
1
,
3
}
,
{
2
,
3
}
,
{
1
,
2
,
3
}
}
Q^{'} = P(Q) = \{∅,\{1\},\{2\},\{3\},\{1,2\},\{1,3\},\{2,3\},\{1,2,3\} \}
Q′=P(Q)={∅,{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}}
2.字母表
Σ
′
=
{
a
,
b
}
Σ^{'} = \{a,b\}
Σ′={a,b}
3.转移函数
δ
′
δ^{'}
δ′,将上面状态集和字母表的所有情况写出来,这里便不在展示。
4.起始状态
q
′
=
E
(
{
1
}
)
=
{
1
,
2
}
q^{'} = E(\{1\}) = \{1, 2\}
q′=E({1})={1,2}
5.接受状态
F
′
=
{
{
2
}
,
{
1
,
2
}
,
{
2
,
3
}
,
{
1
,
2
,
3
}
}
F^{'} = \{\{2\},\{1,2\},\{2,3\},\{1,2,3\} \}
F′={{2},{1,2},{2,3},{1,2,3}}
其实这样就已经完成转化了,毕竟这里我们给出了DFA的形式化定义,我们当然也可以把这个DFA的状态图画出来,当然如果按照这个画我们会得到一个含有8个状态的状态图,但实际上我们可以把这张图进行简化,这里展示最后简化的结果。
4.Regular language(正则语言)
4.1 Closed under operation(在正则运算下的封闭性)
我们在前文定理1的地方就开始介绍正则语言类在正则运算下的封闭性。
正则语言在经过Union(并)、Concatenation(连接)以及Kleene star(星号)的运算后仍是正则语言。
我们再复习一下正则语言的定义:如果一个语言可以被某个DFA所接受,那么这个语言就是一个正则语言。
由于我们前面学习了DFA和NFA的等价性,所以我们现在可以得出一个结论:一个语言是正则的,当且仅当有一台NFA识别它。
首先如果一个语言可以被某个DFA所接受,那么这个语言就是一个正则语言,而DFA可以转化成NFA,所以一个语言可以被某个NFA接受,那也一定有某个DFA接受这个语言,因此这个语言是一个正则语言。另一个方向是说如果一个语言是正则的,则一定有一台NFA识别它,这显然成立,因为每一个正则语言都有一台识别它的DFA,而DFA也是NFA。
上次我们用DFA证明了定理1:正则语言类在并运算下封闭。换言之,如果
A
1
A_1
A1和
A
2
A_2
A2是正则语言,则
A
1
∪
A
2
A_1∪ A_2
A1∪A2也是正则语言。
我们用一个例子来再复习一遍。
我们现在设计一个DFA
M
1
∪
M
2
M_1∪ M_2
M1∪M2的
M
M
M
原来的两个DFA的状态组合在一起形成的状态对构成了我们的状态集。我们的起始状态是两个DFA的起始状态组成的状态对,然后同时根据输入同时改变这状态对的结果,只要是状态对中的一个状态是接受状态是接受状态,这个状态对就是接受状态。
所以我们的起始状态是
x
1
,
y
1
x_1,y_1
x1,y1,如果输入的符号是a那它会转移到
x
1
,
y
2
x_1,y_2
x1,y2,如果输入b,它会转移到
x
2
,
y
3
x_2,y_3
x2,y3。
对于
x
1
,
y
2
x_1,y_2
x1,y2,如果输入a,它会转移到
x
1
,
y
1
x_1,y_1
x1,y1,如果输入b,它会转移到
x
2
,
y
1
x_2,y_1
x2,y1。
对于
x
1
,
y
3
x_1,y_3
x1,y3,如果输入a,它会转移到
x
1
,
y
3
x_1,y_3
x1,y3,如果输入b,它会转移到
x
2
,
y
3
x_2,y_3
x2,y3
对于
x
2
,
y
1
x_2,y_1
x2,y1,如果输入a,它会转移到
x
1
,
y
2
x_1,y_2
x1,y2,如果输入b,它会转移到
x
2
,
y
3
x_2,y_3
x2,y3。
对于
x
2
,
y
2
x_2,y_2
x2,y2,如果输入a,它会转移到
x
1
,
y
1
x_1,y_1
x1,y1,如果输入b,它会转移到
x
2
,
y
1
x_2,y_1
x2,y1。
对于
x
2
,
y
3
x_2,y_3
x2,y3,如果输入a,它会转移到
x
1
,
y
3
x_1,y_3
x1,y3,如果输入b,它会转移到
x
2
,
y
3
x_2,y_3
x2,y3。
因此最后的结果如图
其中
x
2
,
y
2
x_2,y_2
x2,y2没有箭头输入我们可以去掉以简化,也可以保留。
现在我们试着从NFA这个定理。
证明思路:有两个正则语言
A
1
A_1
A1和
A
2
A_2
A2,要证明
A
1
∪
A
2
A_1∪ A_2
A1∪A2也是正则的,想法是对
A
1
A_1
A1和
A
2
A_2
A2取两台NFA
N
1
N_1
N1和
N
2
N_2
N2,并把它们合并成一台新的NFA
N
N
N。
当
N
1
N_1
N1和
N
2
N_2
N2接受输入时,机器
N
N
N应该接受这个输入。机器
N
N
N有新的起始状态,它用
ϵ
ϵ
ϵ箭头分支到原机器的起始状态。从而通过这种备份的方法判断两台机器中是否有一台机器接受这个输入。如果两台机器中有一台接受,那么
N
N
N就接受。
下图给出了这个NFA的构造,左边是原来的两台NFA,右边是构造出来的新NFA,图中的圆圈是起始状态和接收状态,小圆圈代表其他状态。
证明:设NFA
N
1
=
(
Q
1
,
Σ
,
δ
1
,
q
1
,
F
1
)
N_1 = (Q_1, Σ, δ_1, q_1, F_1)
N1=(Q1,Σ,δ1,q1,F1)识别
A
1
A_1
A1和
N
2
=
(
Q
2
,
Σ
,
δ
2
,
q
2
,
F
2
)
N_2 = (Q_2, Σ, δ_2, q_2, F_2)
N2=(Q2,Σ,δ2,q2,F2)识别
A
2
A_2
A2,构造识别
A
1
∪
A
2
A_1∪ A_2
A1∪A2的
N
=
(
Q
,
Σ
,
δ
,
q
0
,
F
)
N = (Q, Σ, δ, q_0, F)
N=(Q,Σ,δ,q0,F)。
1.
Q
=
{
q
0
}
∪
Q
1
∪
Q
2
Q = \{q_0\}∪Q_1∪Q_2
Q={q0}∪Q1∪Q2,
N
N
N的状态集是
N
1
N_1
N1和
N
2
N_2
N2的所有状态加上新的起始状态
q
0
q_0
q0。
2.
q
0
q_0
q0是
N
N
N的起始状态。
3.
F
=
F
1
∪
F
2
F = F_1∪F_2
F=F1∪F2原来的两台机器的接受状态都是接收状态。
4.
δ
δ
δ的定义如下:对每一个
q
∈
Q
q∈Q
q∈Q和每一个
a
∈
Σ
ϵ
a ∈ Σ_ϵ
a∈Σϵ,
定理2:正则语言类在连接运算下封闭。
前面我们说过用DFA证明这个是困难的,现在我们这里用NFA证明它。
证明思路:有两个正则语言
A
1
A_1
A1和
A
2
A_2
A2,要证明
A
1
A
2
A_1A_2
A1A2也是正则的,想法是对
A
1
A_1
A1和
A
2
A_2
A2取两台NFA
N
1
N_1
N1和
N
2
N_2
N2,并把它们合并成一台新的NFA
N
N
N,但是和前文的合并不同。
取
N
1
N_1
N1的起始状态是
N
N
N的起始状态。将
N
1
N_1
N1的每一个接收状态增加一个
ϵ
ϵ
ϵ箭头指向
N
2
N_2
N2的起始状态,这意味着它已经找到了输入的前一段,这一段构成了
A
1
A_1
A1中的一个字符串,因此我们需要将第二段放入
N
2
N_2
N2里开始计算,如果第二段也被
N
2
N_2
N2接受,那么
N
N
N接受,所以
N
N
N的接收状态就是
N
2
N_2
N2的接收状态。
ϵ
ϵ
ϵ的使用可以被认为是
N
N
N非确定地猜想在什么地方把输入串分开。
N
N
N地构造如图。
证明:设NFA
N
1
=
(
Q
1
,
Σ
,
δ
1
,
q
1
,
F
1
)
N_1 = (Q_1, Σ, δ_1, q_1, F_1)
N1=(Q1,Σ,δ1,q1,F1)识别
A
1
A_1
A1和
N
2
=
(
Q
2
,
Σ
,
δ
2
,
q
2
,
F
2
)
N_2 = (Q_2, Σ, δ_2, q_2, F_2)
N2=(Q2,Σ,δ2,q2,F2)识别
A
2
A_2
A2,构造识别
A
1
A
2
A_1A_2
A1A2的
N
=
(
Q
,
Σ
,
δ
,
q
1
,
F
2
)
N = (Q, Σ, δ, q_1, F_2)
N=(Q,Σ,δ,q1,F2)。
1.
Q
=
Q
1
∪
Q
2
Q = Q_1∪Q_2
Q=Q1∪Q2,
N
N
N的状态集是
N
1
N_1
N1和
N
2
N_2
N2的所有状态。
2.
N
N
N的起始状态是
N
1
N_1
N1的起始状态
q
1
q_1
q1。
3.
N
N
N的接收状态是
N
2
N_2
N2的接收状态集
F
2
F_2
F2。
4.
δ
δ
δ的定义如下:对每一个
q
∈
Q
q∈Q
q∈Q和每一个
a
∈
Σ
ϵ
a ∈ Σ_ϵ
a∈Σϵ,
定理3:正则语言类在星号运算下封闭。
证明思路:有一个正则语言
A
1
A_1
A1,要证明
A
1
∗
A_1^*
A1∗也是正则的。取一台识别
A
1
A_1
A1的NFA
N
1
N_1
N1,如下图这样修改它,使其识别
A
1
∗
A_1^*
A1∗。
可以这样构造
N
N
N,在
N
1
N_1
N1上添加从每一个接收状态返回起始状态的
ϵ
ϵ
ϵ箭头。这样,当进行到
N
1
N_1
N1能接受的一段结束时,机器
N
N
N可以选择条回到起始状态从而试图读
N
1
N_1
N1能接受的下一段。此外还必须修改
N
N
N使其接受
ϵ
ϵ
ϵ,
ϵ
ϵ
ϵ也是
A
1
∗
A_1^*
A1∗的一部分。为了解决这个问题,我们可以添加一个新的起始状态,同时这个状态也是一个接受状态,然后用
ϵ
ϵ
ϵ将它连接指向原来的起始状态。
证明:设NFA
N
1
=
(
Q
1
,
Σ
,
δ
1
,
q
1
,
F
1
)
N_1 = (Q_1, Σ, δ_1, q_1, F_1)
N1=(Q1,Σ,δ1,q1,F1)识别
A
1
A_1
A1,构造识别
A
1
∗
A_1^*
A1∗的
N
=
(
Q
,
Σ
,
δ
,
q
0
,
F
)
N = (Q, Σ, δ, q_0, F)
N=(Q,Σ,δ,q0,F)。
1.
Q
=
{
q
0
}
∪
Q
1
Q = \{q_0\}∪Q_1
Q={q0}∪Q1,
N
N
N的状态是
N
1
N_1
N1的所有状态加上一个新的起始状态
q
0
q_0
q0。
2.
q
0
q_0
q0是新的起始状态。
3.
F
=
{
q
0
}
∪
F
1
F = \{q_0\}∪F_1
F={q0}∪F1,接收状态是原有的接收状态加上新的起始状态。
4.
δ
δ
δ的定义如下:对每一个
q
∈
Q
q∈Q
q∈Q和每一个
a
∈
Σ
ϵ
a ∈ Σ_ϵ
a∈Σϵ,
定理4:正则语言类在补集运算下封闭。表示为
A
‾
=
{
w
∈
Σ
∗
:
w
∉
A
}
\overline{A} = \{w ∈ Σ^∗:w ∉A\}
A={w∈Σ∗:w∈/A}。
定理5:正则语言类在交集运算下封闭。表示为
A
1
∩
A
2
=
{
w
∈
Σ
∗
:
w
∈
A
1
a
n
d
w
∈
A
2
}
A_1 ∩ A_2 = \{w ∈ Σ^∗: w ∈ A_1 \ and \ w ∈ A_2\}
A1∩A2={w∈Σ∗:w∈A1 and w∈A2}。
4.2 Regular expressions(正则表达式)
在算术中我们可以用数学运算符来构造表达式:如
(
5
+
3
)
×
4
(5+3)×4
(5+3)×4。类似地,我们可以用正则运算符来构造描述语言的表达式,称为正则表达式。例如:
(
0
∪
1
)
0
1
∗
(0∪1)01^*
(0∪1)01∗
这个正则表达式表示的是有0或1开头,第二个字符是0,最后以人一个0结尾的所有字符串组成的语言。
仔细分析这个表达式的各个部分,首先第一部分是
(
0
∪
1
)
(0∪1)
(0∪1)表示这个符号是
0
0
0或者
1
1
1,第二部分是
0
0
0,第三部分是
1
∗
1^*
1∗表示人一个1的字符串,这三个部分由concatenation(连接)运算符连接起来,从而得到整个表达式。
上面的例子充分体现了最基础的三种正则运算符的使用,我们在这里再提供几个例子,下面的例子的字母表
Σ
=
{
0
,
1
}
Σ = \{0, 1\}
Σ={0,1}:
1.含有2个0的所有字符串构成的语言,其正则表达式为
1
∗
0
1
∗
0
1
∗
1^*01^*01^*
1∗01∗01∗。
2.含有至少2个0的所有字符串构成的语言,其正则表达式为
1
∗
0
1
∗
0
1
∗
(
0
∪
1
)
∗
1^*01^*01^*(0∪1)^*
1∗01∗01∗(0∪1)∗或
(
0
∪
1
)
∗
0
(
0
∪
1
)
∗
0
(
0
∪
1
)
∗
(0∪1)^*0(0∪1)^*0(0∪1)^*
(0∪1)∗0(0∪1)∗0(0∪1)∗。
3.含有1011作为子串的所有字符串构成的语言,其正则表达式为
(
0
∪
1
)
∗
1011
(
0
∪
1
)
∗
(0∪1)^*1011(0∪1)^*
(0∪1)∗1011(0∪1)∗。
其实到这里我们可以发现一个正则语言,它会被有限状态机识别,同时它也可以被一个正则表达式表达出来。
4.2.1 Formal Definition of regular expressions(正则表达式的形式化定义)
对于一个非空的字母表
Σ
Σ
Σ,称
R
R
R是一个正则表达式,如果
R
R
R是
1.
ϵ
ϵ
ϵ
2.
∅
∅
∅
3.
a
a
a,这里a是字母表
Σ
Σ
Σ中的一个元素
4.
R
1
∪
R
2
R_1∪R_2
R1∪R2,这里
R
1
R_1
R1和
R
2
R_2
R2是正则表达式
5.
R
1
R
2
R_1R_2
R1R2,这里
R
1
R_1
R1和
R
2
R_2
R2是正则表达式
6.
R
∗
R^*
R∗,这里
R
R
R是正则表达式。
其中
ϵ
ϵ
ϵ表示只包含一个字符串——空串的语言,而
∅
∅
∅表示不包含任何字符串的语言。
因此我们要是想证明对于字母表
Σ
=
{
0
,
1
}
Σ = \{0, 1\}
Σ={0,1}的表达式
(
0
∪
1
)
∗
101
(
0
∪
1
)
∗
(0∪1)^*101(0∪1)^*
(0∪1)∗101(0∪1)∗是正则表达式,其的证明如下:
1.因为
0
,
1
0,1
0,1是字母表的一个元素,所以它们是正则表达式。
2.所以
0
∪
1
0∪1
0∪1是正则表达式。
3.所以
(
0
∪
1
)
∗
(0∪1)^*
(0∪1)∗是正则表达式。
4.因为
0
,
1
0,1
0,1是正则表达式,所以
101
101
101是正则表达式。
5.所以
(
0
∪
1
)
∗
101
(0∪1)^*101
(0∪1)∗101是正则表达式。
6.所以
(
0
∪
1
)
∗
101
(
0
∪
1
)
∗
(0∪1)^*101(0∪1)^*
(0∪1)∗101(0∪1)∗是正则表达式。
我们前文说一个正则语言,它会被有限状态机识别,同时它也可以被一个正则表达式表达出来。这是说正则表达式可以描述语言,同样它可以生成语言。
与前文的形式化定义类似,下文不再叙述,但是正则表达式还有几点强调:
1.把空语言加到任何一种语言上不会改变这个语言,
R
∪
∅
=
R
R ∪ ∅ = R
R∪∅=R。
2.把空串加到任意字符串上不会改变这个字符串,
R
ϵ
=
R
Rϵ = R
Rϵ=R
但是我们稍微交换一下,这两个式子便不成立。
1.
R
∪
ϵ
R ∪ ϵ
R∪ϵ可能不等于
R
R
R,
ex:
L
(
R
)
=
{
0
}
L(R) = \{0\}
L(R)={0},而
L
(
R
∪
ϵ
)
=
{
0
,
ϵ
}
L(R ∪ ϵ) = \{0, ϵ\}
L(R∪ϵ)={0,ϵ}。
2.
R
∅
R∅
R∅可能不等于
R
R
R
ex:
L
(
R
)
=
{
0
}
L(R) = \{0\}
L(R)={0},而
L
(
R
∅
)
=
∅
L(R∅) =∅
L(R∅)=∅。
因为不存在任何来自
∅
∅
∅的字符串可以和
R
R
R的字符串进行连接。即我们将一个正则语言
R
R
R与
∅
∅
∅进行连接,那么结果将是空集。
因此我们看下面的例子:描述出正则表达式
(
0
∪
ϵ
)
1
∗
(0∪ ϵ) 1^*
(0∪ϵ)1∗
结合前面的两点,现在的语言集合中应该有
{
ϵ
}
\{ϵ\}
{ϵ},并且它还能支持0开头后面任意个1的字符串或者任何个1的字符串,即
L
(
R
)
=
{
ϵ
,
0
,
01
,
011
,
.
.
.
,
1
,
11
,
111
,
.
.
.
}
L(R) = \{ϵ,0,01,011,...,1,11,111,...\}
L(R)={ϵ,0,01,011,...,1,11,111,...}.
5. Regular language(2)(正则语言)
5.1 正则表达式与有限自动机的等价性
我们前文说过一个正则语言,它会被有限状态机识别,同时它也可以被一个正则表达式表达出来。
我们现在介绍Kleene’s Theorem(克林定理):一个语言是正则的,当且仅当可以用正则表达式描述它。
这个定理有两个方向,因此我们可以拆分成两个引理并分别证明。
1.如果一个语言可以用正则表达式描述,那么它是正则的。
证明思路:假设正则表达式
R
R
R描述某语言
A
A
A,我们想要证明这个语言是正则的,就是要将正则表达式
R
R
R转换成一台识别
A
A
A的NFA。而正则表达式的形式化定义定义了正则表达式的六种情况,所以这六种情况我们都要进行讨论。
第一种情况:如果
R
=
ϵ
R = ϵ
R=ϵ,那么
L
(
R
)
=
{
ϵ
}
L(R) = \{ϵ\}
L(R)={ϵ}。NFA
N
=
(
{
q
}
,
Σ
,
δ
,
q
,
{
q
}
)
N = (\{q\}, Σ, δ, q, \{q\})
N=({q},Σ,δ,q,{q}),其中对于任意的
a
a
a,都有
a
∈
Σ
ϵ
a ∈ Σ_ϵ
a∈Σϵ都有
δ
(
q
,
a
)
=
∅
δ(q, a) = ∅
δ(q,a)=∅。
第二种情况:如果
R
=
∅
R = ∅
R=∅,那么
L
(
R
)
=
{
∅
}
L(R) = \{∅\}
L(R)={∅}。NFA
N
=
(
{
q
}
,
Σ
,
δ
,
q
,
∅
)
N = (\{q\}, Σ, δ, q, ∅)
N=({q},Σ,δ,q,∅),其中对于任意的
a
a
a,都有
a
∈
Σ
ϵ
a ∈ Σ_ϵ
a∈Σϵ都有
δ
(
q
,
a
)
=
∅
δ(q, a) = ∅
δ(q,a)=∅。
第三种情况:如果
R
=
a
R = a
R=a,这里
a
∈
Σ
a ∈ Σ
a∈Σ。NFA
N
=
(
{
q
1
,
q
2
}
,
Σ
,
δ
,
q
1
,
{
q
2
}
)
N = (\{q_1, q_2\}, Σ, δ, q1, \{q_2\})
N=({q1,q2},Σ,δ,q1,{q2}),其中
δ
δ
δ的定义如下:若
r
≠
q
1
r≠q_1
r=q1或
b
≠
a
b≠a
b=a,则
δ
(
q
1
,
a
)
=
{
q
2
}
δ(q_1, a) = \{q_2\}
δ(q1,a)={q2},$δ(r, b) = ∅。
第四种情况:
R
=
R
1
∪
R
2
R = R_1∪R_2
R=R1∪R2
第五种情况:
R
=
R
1
R
2
R = R_1R_2
R=R1R2
第六种情况:
R
=
R
1
∗
R = R_1^*
R=R1∗
对于这三种情况,使用正则语言类在正则运算下封闭的证明中所给出的构造。换句话说,由识别
R
1
R_1
R1和
R
2
R_2
R2的NFA构造出关于
R
R
R的NFA。
我们这里可以看一个例子,用上述学习的知识尝试将正则表达式转换成NFA。
ex:将正则表达式
(
a
b
∪
a
)
∗
(ab∪a)^*
(ab∪a)∗转换成一台NFA。
我们的思路是从最小的子表达式开始,一步步到大一点的子表达式。
解题过程如下:
2.如果一个语言是正则的,则可以用正则表达式描述它。
证明思路:由于语言
A
A
A是正则的,故它被一台DFA接受。然后我们要用一种新型有限自动机Generalized NFA(GNFA,广义非确定性有限自动机),先将DFA转换成GNFA,再将GNFA转换成正则表达式。
GNFA是NFA,但是它的转移箭头可以用任何正则表达式作标号,而不是只能用字母表的成员或
ϵ
ϵ
ϵ作标号。GNFA读一段输入符号,沿着连接两个状态的箭头移动,这段输入符号正好是那个箭头上的正则表达式描述的一个字符串。GNFA是NFA,所以它是非确定性的,从而可能有好几种不同的方式处理同一个输入串。如果它的处理能够是的GNFA在输入结束时进入一个接收状态,则它接受它的输入。
为方便起见,要求GNFA具有符合下述条件的特殊形式:
• 起始状态有射到其他每一个状态的箭头,但是没有从任何状态射入的箭头。
• 有唯一的一个接受状态,并且它有从其他每一个状态射入的箭头,但是没有射到任何其他状态的箭头。此外,这个接受状态与起始状态不同。
• 除起始状态和接收状态外,每一个状态到自身和其他每一个状态都有一个箭头。
我们能够很容易地把DFA转换成这种特殊形式的GNFA。添加一个新的起始状态和一个新的接受状态,从新起始状态到原起始状态有一个
ϵ
ϵ
ϵ箭头,从每一个原接收状态到新接收状态有一个
ϵ
ϵ
ϵ箭头。如果一个箭头有多个标记(或者在两个状态之间有多个方向相同的箭头),则把它替换成一个标记着原先标记的并集的箭头。
下面说明如何把GNFA转换成正则表达式。设这台GNFA有
k
k
k个状态。由于GNFA必须有一个起始状态和一个接收状态,并且这两个状态不是同一个状态,因此
k
≥
2
k≥2
k≥2。如果
k
>
2
k>2
k>2,则构造一台有
k
−
1
k-1
k−1个状态的等价GNFA。对新的GNFA重复这一步骤直至把它化简到只有2个状态。当
k
=
2
k=2
k=2时,这台GNFA只有一个从起始状态到接收状态的箭头。这个箭头的标记就是等价的正则表达式。下图给出了如何将一台有3个状态的DFA转换成等价的正则表达式的步骤。
关键的步骤是当
k
>
2
k>2
k>2时构造等价的少一个状态的GNFA。为此我们需要挑选一个状态删去,并修改留下的部分从而不改变机器所能识别的语言。其实我们挑选这个状态是除了起始状态和接收状态外的任意状态,当
k
>
2
k>2
k>2时,一定存在一个这样的状态。我们把这个要删去的状态称为
q
r
i
p
q_{rip}
qrip。下图中的例子就是按照上文所说的方法实现了将原来从
q
i
q_i
qi直接到
q
j
q_j
qj或者通过
q
r
i
p
q_{rip}
qrip到
q
j
q_j
qj改成了从
q
i
q_i
qi到
q
j
q_j
qj的正则表达式。
我们现在给出GNFA的形式化定义,GNFA
M
=
(
Q
,
Σ
,
δ
,
s
,
t
)
M = (Q,Σ,δ,s,t)
M=(Q,Σ,δ,s,t)是一个5元组,其中:
1.
Q
Q
Q是有穷的状态集。
2.
Σ
Σ
Σ是输入的字母表。
3.
δ
:
(
Q
−
{
t
}
×
(
Q
−
{
s
}
)
→
R
δ:(Q - \{t\} × (Q - \{s\}) → R
δ:(Q−{t}×(Q−{s})→R是转移函数。符号
R
R
R是字母表
Σ
Σ
Σ上的全体正则表达式的集合,
s
s
s和
t
t
t分别是起始状态和接收状态。如果
δ
(
q
i
,
q
j
)
=
R
δ(q_i,q_j)=R
δ(qi,qj)=R,则从状态
q
i
q_i
qi到状态
q
j
q_j
qj的箭头以正则表达式
R
R
R作为它的标记。转移函数的定义域为
(
Q
−
{
t
}
×
(
Q
−
{
s
}
)
(Q - \{t\} × (Q - \{s\})
(Q−{t}×(Q−{s}),这是因为除了
t
t
t没有射出的箭头和
s
s
s没有射入的箭头,每一个状态到另一状态都有一个箭头连接。
4.
s
s
s是起始状态。
5.
t
t
t是接收状态。
我们不需要掌握这里的证明,我们直接看下图的例子,尝试将下图3-状态的自动机转换成正则表达式:
我们第一步先将添加一个起始状态和一个接收状态,将它改成一个5-状态的GNFA。
然后我们将这个5-状态的GNFA不断删去一个状态,直到它变成一个2-状态的GNFA,这样我们就得到了最终的正则表达式,后续步骤如下。
现在我们看一个更难的例子,尝试将下面的这个DFA改成正则表达式。
我们还是按照上面的步骤进行操作,但我们可能到这一步卡住。
现在
q
0
q_0
q0有一个指向自己的箭头,而且到
q
2
q_2
q2有一个箭头,而
q
2
q_2
q2有两个箭头指向了
q
0
q_0
q0,看上去我们很难删去其中一个状态,我们可以尝试将这个拆成两个路径去看。
当然我们也可以直接因为
q
2
q_2
q2有两个箭头指向了
q
0
q_0
q0,所以两者和从
q
0
q_0
q0到
q
2
q_2
q2的箭头连接在一起然后再并集在一起。
最后我们可以得到结果。
5.2 Nonregular language(非正则语言)
我们了解了有限状态自动机的能力,我们也知道了它们的局限性。这一届将重点讲述哪些语言不可能用有限状态自动机识别。
设语言
B
=
{
0
n
1
n
∣
n
≥
0
}
B = \{0^n1^n|n≥0\}
B={0n1n∣n≥0}。如果想要识别B,这个机器需要输入了多少个0,由于输入的0可能是无数个,这是有限状态机不可能做到的。
我们可能会觉得有些语言如果需要无限存储那它就一定是非正则的,语言
B
B
B的确是非正则的,但是下面一个语言是正则的。设语言
C
=
{
w
∣
w
中
01
和
10
作
为
子
串
出
现
的
次
数
相
同
}
C=\{w|w中01和10作为子串出现的次数相同\}
C={w∣w中01和10作为子串出现的次数相同},看上去D需要无数个状态去记录,但D其实是正则的。因此如果我们想要去判断一个语言是正则的还是非正则的,我们需要用严谨的数学证明。
5.2.1 Pumping Lemma(泵引理)
证明非正则性的技术源于一个关于正则语言的定理——pumping lemma(泵引理)。该定理指出所有的正则语言都有一种特殊的性质。如果能证明一个语言没有这种性质,则可以保证它不是正则的。这条性质是:语言中的所有字符串只要它的长度不小于某个特定的值——pumping length(泵长度),就可以被“抽取”。它的意思是指每一个这样的字符串都包括一段子串,把这段子串重复任意次,得到的字符串仍在这个语言中。
注意:1.如果一个语言是正则的,那么它一定满足pumping lemma(泵引理)。如果对于某个语言
L
L
L,至少存在一个由pumping(泵动作)产生的字符串不属于
L
L
L,那么可以确定
L
L
L不是正则语言。
2.这个定理反过来不一定成立。如果一个语言满足pumping lemma(泵引理)的条件,并不能直接得出该语言一定是正则语言。比如上下文无关发对应的自动机能够识别的语言。
下面我们介绍pumping lemma(泵引理):若
A
A
A是一个正则语言,那存在一个数
p
,
p
≥
1
p,p≥1
p,p≥1,我们叫这个数pumping length(泵长度),它使得,如果
s
s
s是
A
A
A中任意长度不小于
p
p
p的字符串,那么
s
s
s可以被分成3段,
s
=
x
y
z
s=xyz
s=xyz,满足下述条件:
1.
y
≠
ϵ
y≠ϵ
y=ϵ,或者
∣
y
∣
≥
1
|y|≥1
∣y∣≥1或者
∣
y
∣
>
0
|y|>0
∣y∣>0。
2.
∣
x
y
∣
≤
p
|xy|≤p
∣xy∣≤p。
3.对每一个
i
≥
0
,
x
y
i
z
∈
A
i ≥ 0, xy^iz ∈ A
i≥0,xyiz∈A。
注意:当
s
s
s可以划分成
x
y
z
xyz
xyz的时候,
x
x
x和
z
z
z可以是
ϵ
ϵ
ϵ。
证明思路:设
M
=
(
Q
,
Σ
,
δ
,
q
1
,
F
)
M = (Q,Σ,δ,q_1,F)
M=(Q,Σ,δ,q1,F)是识别
A
A
A的DFA。令pumping length(泵长度)
p
p
p等于
M
M
M的状态数。要证明
A
A
A中任意长度不小于
p
p
p的字符串
s
s
s可以划分成3段
x
y
z
xyz
xyz,且满足定理中的3个条件。如果
A
A
A中没有长度不小于
p
p
p的字符串,显然这个定理成立。若
A
A
A中存在长度不小于
p
p
p的字符串,比如下图例子中的DFA的状态数
M
=
5
M = 5
M=5
我们的字符串
s
=
0011011
s = 0011011
s=0011011是它能接受的,那它接受的路径如下。
我们可以发现
q
2
q_2
q2是第一个被访问两次的状态。
因此我们就可以把这个
s
s
s分成3部分
x
,
y
,
z
x,y,z
x,y,z:
•
x
=
0
x = 0
x=0是
s
s
s在
q
2
q_2
q2之前的部分。
•
y
=
0110
y = 0110
y=0110是
s
s
s两个
q
2
之
间
的
部
分
q_2之间的部分
q2之间的部分。
•
z
=
11
z = 11
z=11是
s
s
s的剩余部分。
三部分的结果如图。
这样被访问两次的状态一定存在吗?若 s s s的长度为 n n n,则状态序列的长度为 n + 1 n+1 n+1,由于你不小于 p p p,故 n + 1 n+1 n+1大于 M M M的状态数 p p p。因此在这个序列中一定有重复出现的状态,这个结论是pigeonhole principle(鸽巢原理)的一个例子。
现在我们来看看为什么这样划分
s
s
s可以满足规定的3个条件。
假设我们输入
x
y
y
z
xyyz
xyyz运行
M
M
M,我们运行完
x
x
x后,状态在
r
r
r,我们运行
y
y
y后,状态在
r
r
r,我们运行第二个
y
y
y后,状态依然在
r
r
r,运行完
z
z
z后状态到了接收状态,故
M
M
M接受
x
y
y
z
xyyz
xyyz。同理包括
x
z
xz
xz,
M
M
M也会接受,因此我们证明了条件3。
由于
y
y
y是
s
s
s在状态
r
r
r出现的两个不同地点之间的部分,故
∣
y
∣
>
0
|y|>0
∣y∣>0,因此我们证明了条件1。
为了确保状态
r
r
r是序列中第一个重复的状态,根据鸽巢原理,在序列的前
p
+
1
p+1
p+1个状态中必定有重复,因此我们证明了条件2。
我们现在想要用pumping lemma(泵引理)证明某个语言 B B B不是正则的,首先假设语言 B B B是正则的,因此根据pumping lemma(泵引理),存在pumping length(泵长度) p p p使得 B B B中所有长度为 p p p或大于 p p p的字符串可以被抽取。其次,在 B B B中寻找一个字符串 s s s,它的长度为 p p p或大于 p p p,但不能被抽取。如果存在这样的 s s s那就会与pumping lemma(泵引理)矛盾,从而证明语言 B B B不是正则的。
我们现在回到这一节开头的那个例子
B
=
{
0
n
1
n
∣
n
≥
0
}
B = \{0^n1^n|n≥0\}
B={0n1n∣n≥0},我们证明它不是正则的。
我们采用反证法,所以我们先假设
B
B
B是正则的。令
p
p
p是由pumping lemma(泵引理)给出的pumping length(泵长度)。选择
s
s
s为字符串
0
p
1
p
{0^p}{1^p}
0p1p。因为
s
s
s是
B
B
B的一个成员且
s
s
s的长度大于
p
p
p,所以pumping lemma(泵引理)保证
s
s
s可以分成3段
s
=
x
y
z
s=xyz
s=xyz,使得对每一个
i
≥
0
,
x
y
i
z
∈
B
i ≥ 0, xy^iz ∈ B
i≥0,xyiz∈B下面考虑3种情况,说明这个结论是不可能的。
1.字符串y只包含0。在这种情况下,字符串xyyz中的0比1多,从而不是B的成员,因此矛盾。
2.字符串y只包含1,与第一种情况同理,因此矛盾。
3.字符串y包含0和1,能保证xyyz中的0和1的个数是相等,但是它们的顺序是0和1混在一起了,因此矛盾。
因为矛盾,所以B不是正则的。
下面稍微展示一下英文解答的过程: