计算理论 (一):有穷状态机与正则语言
自动机理论是计算理论中的一个重要的分支,其致力于建立抽象的计算模型来阐释计算,用抽象刻画出来的 “计算机” 来解决一些问题,在不同的领域都有作用,比如形式语言或者可判定性,可计算性等。而 有穷状态机 是一类最基础,最简单的计算模型。
语言与短语结构文法
定义 字母表 为任意一个非空有穷集合,用 表示,其中的元素为字母表中的符号,而 字母表上的字符串 为该字母表中符号的有穷序列,字符串的集合则称为 语言。
例如设 ,则 为 上的一个字符串,而 为 上的一个语言。其中 表示空串,即长度为 的字符串。
文法 用来描述语言,在一个短语结构文法的字母表上有 终结符 和 变元 两种符号,包含变元的一个字符串可以用另一些符号来替换,而文法的 产生式 则用来描述这种替换规则,指明 可以替换为 的产生式记为 。一个文法中有一个 起始变元 ,我们总是从起始变元开始定义其它字符串。
一个文法可以生成若干字符串,其方式是通过起始变元,根据产生式将字符串中的某部分替换为产生式右边的字符串,直到字符串中没有变元为止。获取一个字符串的替换序列称为 派生。
如令终结符为 ,有两个产生式
它产生 的派生过程为
下面我们形式化地定义短语结构文法以及它产生的语言:
一个 「短语结构文法」 是一个四元组 ,其中
是一个有穷集合,称为 变元集。
是一个与 不相交的有穷集合,称为 终结符集合。
是一个有穷 产生式集,每一个产生式左边必须至少包含一个变元。
是 起始变元。
设 是由变元和终结符组成的字符串, 是文法的一条规则,那么称 生成 ,记作 。如果 或存在序列 使得
则称 派生 ,记作 。
由 生成的语言 是起始变元能派生的所有仅由终结符组成的字符串,即
根据产生式的类型,短语结构文法被分为四类:
型 文法:产生式没有限制。
型 文法:有两种产生式,一种是 ,其中 ,, 是一个变元, 是由变元或终结符组成的串, 是变元或终结符构成的非空串。一种是 。 不能出现在产生式的右边。
型 文法:只有一种产生式 ,其中 是一个变元, 是由变元或终结符构成的串。
型 文法:产生式为 或 或 其中 是终结符, 是变元。
其中 型文法称为 上下文有关文法, 型文法称为 上下文无关文法, 型文法称为 正则文法。
有穷状态机
有穷状态机也可以叫做有穷自动机,因为习惯下文中将用 “自动机” 来代指这类对象,具体分类依据上下文而定。
下图描述了一个有穷状态机 :
该图称为 的 状态图,它有 个状态 ,其中中 起始状态 用一个指向它的无出发点的箭头表示,接受状态 用双圈表示。状态之间用称为 转移 的箭头联系起来。
自动机以字符串为输入,它处理输入字符串并产生一个输出,表示为 接受 或 拒绝。它从左往右逐个接受输入字符串的所有符号,当输入一个符号时,它将沿着标有该符号的转移从一个状态移动到另一个状态,当输入最后一个符号时产生输出,如果自动机处于一个接受状态,输出为接受,否则为拒绝。
顺便一提,上图中的自动机接受所有至少含有一个 且在最后一个 后有偶数个 的字符串。
接下来我们将形式化地定义有穷自动机和在它上面的计算:
「有穷状态机」 是一个五元组 ,其中
是一个有穷集合,称为 状态集。
是一个有穷集合,称为 字母表。
是 转移函数。
是 起始状态。
是 接受状态集。
设 是一个字符串,如果存在 中的状态序列 满足下述条件:
对于
则称 接受 。
称自动机 接受的全部字符串集合 为 自动机 的语言,或者说 识别 ,记为 。对于一个自动机 ,这个 是唯一的。
例如前面图中的 可以形式地写成 ,其中 ,,起始状态为 ,, 描述为
的语言 为
非确定性
非确定型 计算是 确定型 计算的推广,在确定型计算中,机器每一步的计算都是按照唯一的转移法则,根据下一个输入的符号确定下一个状态。但是在非确定型计算中,转移法则不是唯一的。
有穷自动机也存在这两个种类,分别为 确定型有穷自动机 和 非确定型有穷自动机 。上面的例子是一个 。对于在 上的某个状态上的某个输入字符,我们会指定若干个转移的方式,甚至不指定对应的转移,机器会并行地执行所有分支,即使某一个分支由于不存在对应的转移而直接结束,整个计算也不会结束。最后,若存在一个分支在最后到达了一个接受状态,那么输出接受,否则输出拒绝。
另外,在 中我们新增一种转移为 ,当我们转移到一个状态 且 有射出的 转移,无需输入任何字符,机器自动新建一个线程转移到 指向的状态。
接下来我们形式化地定义非确定型有穷自动机和在它上面的计算:
「非确定型有穷自动机」 是一个五元组 ,其中
是有穷的状态集。
是有穷的字母表。
是转移函数,其中 表示 的所有子集组成的集合。
是起始状态。
是接受状态集。
设 是一个字符串,如果存在 中的状态序列 满足下述条件:
对于
则称 接受 。
与 的计算能力
在自动机上进行的计算即为识别语言,那么它们识别语言的能力就代表了它们的计算能力。后面我们会讨论有穷自动机识别的语言类,即正则语言,为此这里我们先研究一下 和 的计算能力有何区别。
直觉上来说, 的计算能力似乎比 更强,因为它可以并行计算,用更简单的状态来识别同样的语言。对于两台自动机识别同样的语言,那么称它们是 等价 的,令人惊奇地是, 和 是等价的,即 每一台非确定型有穷自动机都等价于某一台确定型有穷自动机。
证明 对于一台有 个状态的 ,我们考虑用 模拟它的所有并行线程,即维护在原机器中的当前状态子集。为此,新 的每个状态都应该对应原机器的一个状态子集,共有 个状态,若最后状态中包含一个在原机器中的结束状态,那么输出接受,否则输出拒绝。
形式化地,设 为一台识别语言 的 ,下面考虑构造一台识别语言 的 。
设 为所有从任意一个 开始经过 个或多个 转移可以到达的状态集合。
。
。
。
。
经过这样构造,我们在 和 上同时输入一个字符串,对于每个输入字符, 所处的状态恰好是 此时可能处于的状态集合,且若 中此时的状态集合中有接受状态,那么对应的 的状态也是一个接受状态。于是 识别的语言与 相同,两者等价。
正则语言
接下来我们将讨论有穷自动机的计算能力和它的局限性。如我们之前所言,有穷自动机识别一类特别的语言。
如果一个语言可以被一台有穷自动机识别,则称它是 「正则语言」 。
语言的 「正则运算」 是下面三种运算:
设 是两个语言,那么有
- 并:
- 连接:
- 星号:
正则语言在正则运算下封闭。
证明大概是利用识别原语言的自动机构造出识别新语言的自动机,这里我们大致描述一下证明思路而不形式化说明了,正确性比较显然。
设 分别是识别 和 的非确定型有穷自动机。
并:并行处理两个自动机。
新建一个起始状态并射出两个 转移分别指向 , 的起始状态。
连接:先运行 ,但凡找到了一段 中的串就新建一个线程将后面一段输入给 识别。
起始状态设为 的起始状态,对于每个 中的结束状态都射出一个 转移指向 的起始状态,置结束状态集合为 的结束状态集合。
星号:先运行 ,但凡找到了一段 中的串就新建一个线程将后面一段返回到 的起始状态继续识别。
新建一个状态作为起始状态,并且它也是一个接受状态,这是为了接受星号中的空串。从它射出一个 转移指向 的起始状态,从所有 的接受状态上射出一个 转移指向起始状态。
接下来介绍语言的 正则表达式,它以另外一个形式等价地定义了正则语言。
称 是一个 「正则表达式」,如果 是
,这里 是字母表 上的一个符号。
,这里 是空串。
,这里 是空语言。
,这里 和 是正则表达式。
,这里 和 是正则表达式。
,这里 是正则表达式。
每个正则表达式都如逐个按照规则运算描述一个语言。例如设 , 为长度为偶数的字符串组成的语言, 表示每一个 后都至少跟有一个 的字符串组成的语言。其中 表示 ,即至少有 个 连接构成的语言。
正则表达式与有穷状态机具有等价性:一个语言是正则语言,当且仅当可以用正则表达式描述它。
证明 先证充分性。
设正则表达式 描述了一个语言 ,考虑构造一个识别 的 。对于正则表达式的 条定义,前 条是容易的,后 条可以直接用证明正则语言在并,连接,星号运算下封闭中的构造。
再证必要性。
设语言 是正则语言,需要证明有一个正则表达式描述它。由于 是正则语言,故存在一台 接受它,接下来我们将说明如何将 转化为一个正则表达式。
首先将 转化为一个等价的特殊形式的 广义非确定性有穷自动机 ,这里 与 的区别是 转移上的标号可以是一个正则表达式而非必须是符号,其余定义如 一样。特殊形式是指 的起始状态到其余每一个状态都射出一个转移,接收状态仅有一个,且从其余每一个状态都射入一个转移,除了这两个状态,每一个状态到自身和其余状态都射出一个转移。
为此,首先新建一个起始状态,射出一个 转移到原起始状态,新建一个接受状态,从所有原接受状态射入一个 转移。如果一个转移上有多个标号,将其替换为一个标号为原先标号的并集的转移。最后在缺少转移的地方加入标号为 的转移。
接下来我们将说明一种方法来构造比 的状态数少 的等价 ,运用这种方法直到 的状态数为 ,此时 仅有一个起始状态,一个接受状态和一个转移,这个转移上的正则表达式即为与 等价的正则表达式。
首先任意选择一个非起始或接受的状态 ,将它和关联它的转移从 中删除,对于剩余的所有状态对 ,由于 的特殊形式,在原自动机中 到 , 到 , 到 和 到 间都存在转移,设这四个转移上的正则表达式分别为 ,那么 中从 到 的转移上的标号为
现在证明 和 是等价的,假设 接受字符串 ,那么存在一个接受 的状态序列 ,设这个序列中存在若干段形如 的状态序列,由于新添加的转移描述了从 到 到 的所有字符串,所以将其中的 删除后的状态序列是 中一个接受 的状态序列。另外一个方向也能这样直接推得。
故这个方法是正确有效的,我们将这个过程进行直到仅剩两个状态,此时可以得到与 等价的正则语言 。
再来回顾我们在前面定义的正则文法,一个正则文法 中的产生式的形式为下面三种
其中 是终结符, 是变元。奇妙的是它也像前面两个方式一样定义了正则语言:一个语言是正则语言,当且仅当它可以由一个正则文法生成。
证明 先证充分性。
设 是一个正则文法,考虑构造识别 的 。令 ;对于每个 的产生式,建立一个从 到 的标号为 的转移,对于每个 的产生式,建立一个从 到 的标号为 的转移;如果 中没有 的产生式那么 ,否则 。
下面我们说明 能识别 。如果 ,观察产生式的形式,我们发现每次生成后的串中的变元只可能在最右边且数量为 或 ,假设没有使用 的产生式,那么派生过程就为 ,采用的产生式序列为 ,根据 的构造,上面的产生式对应转移 ,故输入 后 到达一个接受状态。如果采用了 的产生式,那么派生过程为 ,相当于上面的最后转移到了 ,而由于此时 , 仍然接受 。反向亦然。
再证必要性。
设 是一个正则语言, 是识别它的 ,考虑根据它构造生成 的文法正则文法 。令 ,,,对于每个转移 ,其中 ,添加产生式 ,对于每个转移 ,添加产生式 ,如果 ,那么添加产生式 。
正确性也能类比上面的那个,挺显然的 ,不想写了。
于是我们得到正则语言类的三个等价定义方式:有穷状态机,正则表达式,正则文法。
是正则语言吗?
有穷状态机的计算能力也是有限的,现在我们来讨论一些有穷状态机无力识别的语言和判断它们的方法。
没有存储空间是有穷状态机的一大硬伤,它将把所有当前需要用到的信息都放在一个状态里。比如语言 ,如果要识别它,那么机器需要记住当前输入了多少个 ,而 的个数是没有限制的,这意味着我们要准备无限个状态来记住当前输入了多少个 ,但是它是 “有穷” 状态机。
下面说明一个判定正则语言的定理:「泵引理」 。它给出了一个语言是正则语言的必要条件。
若 是一个正则语言,则存在一个称之为泵长度的数 ,使得如果 是 中任一长度不小于 的字符串,那么 可以被分成三段 满足:
对于每一个 ,。
。
。
通俗地理解这个定理,大致就是对于一个正则语言中所有长度不小于泵长度的的字符串,都可以从其中 “抽取” 一段非空子串,把它重复任意次后得到的字符串仍然在这个语言中。对于泵长度,我们知道它是存在的,但是不知道具体值是多少,也可能这个语言中根本没有长度不小于泵长度的字符串,这样的情况也是会出现的。
证明 设 是一个正则语言,那么存在 为一台识别 的 ,令泵长度 为 的状态数。
设 ,且 ,设 为 接受 时的状态序列,根据抽屉原理,在这个序列的前 个状态中必存在两个状态是重复的,设前一个为 ,后一个为 ,令 ,,。输入 后, 由 转移到 ;输入 后, 由 转移到 ,故接着可以输入任意次 ,仍然转移到 ;输入 后, 由 转移到 ,而 是一个接受状态,故满足条件 。由于 ,故 ,满足条件 。又由于 ,故 ,满足条件 。
下面我们考虑用泵引理来证明开头的语言 不是正则语言。采用反证法,假设 是正则语言,令 为泵引理给出的泵长度,,它应该满足泵引理的三个条件。考虑从其中抽取一段 并重复它。由于条件 , 在前半段,即 中只有 ,这样的话重复一次后 中的 数量比 多,不是 的成员,矛盾。故假设不成立, 不是正则语言。
再举一个应用泵引理的有趣例子:。继续采用反证法:设 是正则语言, 为泵引理给出的泵长度。取 ,它一定可以被抽取,寻找矛盾。考虑 和 ,我们有 ,故 ,又由于 ,有 ,并且 说明 ,这就得到 ,它的长度不是一个完全平方数,即 ,假设不成立, 不是正则语言。
后记
这是可爱的企鹅看计算理论导引 以及离散数学及其应用 的笔记,其中大部分(或者全部)的例子,配图和证明都来自于本书中,当然后续有时间会更新后面习题的记录之类,如果期中考试能够考的比较好来给我创造更多空余时间...
习题
来自计算理论导引第三版第一章习题的问题部分。皆为本人口胡www
找到识别 和 的 和 。在它们的基础上构造识别 的自动机 。
大致思路是将输入串在 和 上并行计算,并且轮流将输入的字符给两边进行计算,比如输入 时,在 中输入 ,在 中输入 ,若两边都接受则接受,否则拒绝。
对于一个状态 将它拆成两个状态 和 以做到输入一个符号后 “等待” 一个字符再进行输入的目的。原本射入 的转移现在射入到 ,原本从 射出的转移现在从 射出,然后设置一个由 到 ,标号为所有符号的转移。若 是接受状态则 和 都是接受状态。最后将 的起始状态拆成的两个 和 中的 删掉,改起始状态为 。
对 做完上面的修改后,开始构造 。令 , 是 的起始状态,, 为
在上一题的基础上,我们在每个状态上 “等待” 的符号个数不确定了。摒弃上一题拆状态的构造,直接令每个状态上往自己转移一个 然后再组合成 。
用非确定性枚举该从哪里断开。把识别 的 复制一份为 ,把 中所有的结束状态去掉后作为上层, 作为下层。对于 中的每个转移 都新建一个从上层到下层的 转移 。最后设起始状态为 的起始状态。
找到识别 和 的 和 。然后把 中的所有 转移改成 转移,这样在 中输入一个串 后的状态集合为所有和 含有同样个数的 的串输入原自动机后到达的状态集合。
并行地计算 和修改后的 ,当且仅当 和 都接受的时候输出接受。
找到识别 的 ,然后反向它的所有转移,设起始状态为接受状态,接受状态取消,然后用一个新起始状态对所有原接受状态射出 转移。
首先因为二进制串从低位往高位输入是比较自然的,转化为证明 是正则的。
我们维护两个值,一个是前两行的和的当前位和进位,一个是第三行的当前位,若前两行没有产生进位且前两位的和的当前位和第三行的当前位不同,那么一定是不同的,直接拒绝。到最后如果对应位全部相同且没有多的进位,那么接受,否则拒绝。
类似上一题,只要认为第一行的 等价为 个 。
类似上一题,只要认为当前位第一行为 而第二行为 那么接受。
如果 是正则的,令 是泵长度, 为
由于 我们只能抽取前半段,而上一行是下一行的翻转,它们的 数量首先要相同,但是前半段的上一行只有 ,下一行只有 ,无论如何抽取都会使得 数量不等,从而抽取后的 ,给出矛盾。
做一个长度为 ,转移标号为 ,起始状态是结束状态的圈!
只需证 是正则的。
根据欧拉定理, 是以 来循环的。我们设状态为二元组 ,其中 表示当前输入的值 的结果, 表示这个输入在循环节的第 位,那么按照在循环的某一位对应的 值能在它们之间建立转移。结束状态是所有的 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话