构造FA#
构造接收是3倍数的二进制串,高位先读。
等价类:模3余数为 i 的状态 qi,初始状态为 qs,接受状态为 q0
q0 代表的等价类:3k
- 下一位为 0 时:2(3k)+0=6k,模3余数为0,转移到 q0
- 下一位为 1 时:2(3k)+1=6k+1,模3余数为1,转移到 q1
q1 代表的等价类:3k+1
- 下一位为 0 时:2(3k+1)+0=6k+2,模3余数为2,转移到 q2
- 下一位为 1 时:2(3k+1)+1=3(2k+1),模3余数为0,转移到 q0
q2 代表的等价类:3k+2
- 下一位为 0 时:2(3k+2)+0=6k+4,模3余数为1,转移到 q1
- 下一位为 1 时:2(3k+2)+1=6k+5,模3余数为2,转移到 q2
flowchart LR
Begin((S)) --> qs(($$q_s$$)) -->|1| q1(($$q_1$$))
qs -->|0| q0((($$q_0$$)))
q0 -->|0| q0
q0 -->|1| q1
q1 -->|0| q2(($$q_2$$))
q1 -->|1| q0
q2 -->|0| q1
q2 -->|1| q2
style Begin fill:none,stroke:none
改:接收模3余1且模5余2的二进制串,高位先读。
只要设计等价类:qij(模3余i且模5余j)。
左线性文法到NFA#
S→a∣Bc
关键在于:
- 将 “a” 视为 “q0a”(加入了一个初始状态)
- 用“规约”的思想,反着看
flowchart LR
Begin(( )) --> Start(($$q_0$$)) --a--> S(((S)))
B((B)) --c--> S
style Begin fill:none,stroke:none
NFA到DFA#
构建DFA:{x∣x∈{0,1}+且x中含形如110的子串}
1. 构建NFA#
flowchart LR
q0(($$q_0$$))
q1(($$q_1$$))
q2(($$q_2$$))
q3((($$q_3$$)))
S((S)) --> q0 --"0,1"--> q0 --"1"--> q1 --"1"--> q2 --"0"--> q3 --"0,1"--> q3
style S fill:none,stroke:none
这个题目有些简单,自己稍微改改就成DFA了。下面是机械的方式:
2. NFA转DFA#
原理是NFA的状态组合得到DFA的状态,从头到尾模拟着走一遍NFA即可。
下表是逐行写的,右侧出现新状态就添加新行:
| 状态 | 输入0 | 输入1 | 新名称 |
|---|
| [q0] | [q0] | [q0,q1] 新状态 | 0 |
| [q0,q1] | [q0] | [q0,q1,q2] | 1 |
| [q0,q1,q2] | [q0,q3] | [q0,q1,q2] | 2 |
| [q0,q3] | [q0,q3] | [q0,q1,q3] | 3 |
| [q0,q1,q3] | [q0,q3] | [q0,q1,q2,q3] | 4 |
| [q0,q1,q2,q3] | [q0,q3] | [q0,q1,q2,q3] | 5 |
- 初始状态:[q0]
- 接受状态:包含 q3 的状态
如果发现当前所有 NFA 状态都无法继续转移(没有定义),对应转移后 DFA 状态 [∅]。该 DFA 状态无论输入什么都转移到自己,对应“陷阱状态”。
例如,不接收包含 00 的 NFA 中,设“已经读取到第一个0”的状态为 q1,只要不定义 q1 在输入0时的转移即可。此时转化为 DFA 会自动增加陷阱状态 [∅]。
3. 最小化DFA#
原理:把不可区分的状态合并。
可区分的:∃x∈Σ∗ 使得 δ(q,x)∈F 和 δ(p,x)∈F 有且只有一个成立,则 q 和 p 是可区分的。
过程:
- “终态”和“非终态”一定可以区分,先标记上(用
√标记表示可区分)
- 对没有标记的状态对正推,如果关联到的状态对已经标记了,那么就传染
- 最终剩下的就是等价状态对
下表中,打勾的为可区分,打叉的为等价,写数对的为下一步可转移;终态用粗体表示;对称只写上三角。
| 0 | 1 | 2 | 3 | 4 | 5 |
|---|
| 0 | X | (1,2) | (0,3) | √ | √ | √ |
| 1 | | X | (0,3) | √ | √ | √ |
| 2 | | | X | √ | √ | √ |
| 3 | | | | X | (3,3) | (3,3) |
| 4 | | | | | X | (3,3) |
| 5 | | | | | | X |
注意:(0, 1) 可以转移到 (0, 0) 和 (1, 2),虽然 (0, 0) 不可区分,但是 (1, 2) 可区分((1, 2) 可转移到 (0, 3),被传染了可区分性),只要染上可区分就会被传染。
发现 3≡4≡5,合并为一个状态。
4. 得到DFA#
flowchart LR
q0((0))
q1((1))
q2((2))
q345(((3,4,5)))
S((S)) --> q0 --0--> q0
q0 --1--> q1
q1 --0--> q0
q1 --1--> q2
q2 --0--> q345
q2 --1--> q2
q345 --0,1--> q345
style S fill:none,stroke:none
正则文法和右线性文法的gap#
正则文法的定义:只准有
- A→aB
- A→a
其中 A,B∈V,a∈T+。容易证明其实可以限制 a∈T。
右线性文法的定义:只准有
- A→aB
- A→a
其中 A,B∈V,a∈T∗。
两者的区别在于正则文法要求 a 至少是一个终结符,而右线性文法允许 a 是空串 ϵ。显然,正则文法的变量不能“总是”派生出空串,而右线性文法可以;正则文法是右线性文法的一个子集。
但这不影响两者的等价性。等价性的证明为:给出任意一方,可以构造出接收同一语言的另一方。要弥补双方的差异,只需要找到可以接收空串正则文法。关键:将 ϵ 作为构造的正则文法的一个终结符。
所以此时构建出的正则文法的形式描述是和右线性文法不一样的。子集是语法层面的子集,但等价是语言层面的等价。
证明不是RL#
pumping lemma 泵引理#
证明 L={0m1n∣m=n} 不是RL:
- 假设它是RL,那么存在一个 pumping length N。
- 取 m=N,则 z=0N1n.
- 任意划分:∀j∈[0,N),∀k∈[1,N−j](j 是为了满足 ∣uv∣≤N,k 是为了满足 ∣v∣≥1,两个一起实现了任意划分),进行如下划分:
- u=oN−j−k
- v=ok
- w=0j1n
此时 uviw=0N+k(i−1)1n,i≥0
- 要证明不是RL,只要找到 i 使得 uviw∈/L 即可。此时 i=kn−N+1 存在,即要求 n−N 是 k 的倍数,问题变为找到这样的 n。考虑到 k≤N,取 n−N=N! 即可。
利用封闭性#
仍然对上一题:
- 已知 A={0n1n} 不是RL、 C={0∗1∗} 是RL。
- 若 L 是 RL,根据运算封闭性,C−L 也是RL,但这实际上得到了非RL的 A,矛盾,所以 L 不是RL。
转换为CNF#
构造与下列文法等价的CNF文法:
- S→aBB∣bAA
- A→bbA∣ϵ
- B→aBa∣aa∣ϵ
机械做法为:先化简CFG,然后转变为CNF。
1. 消除 ε-产生式#
观察到 A 和 B 都有 ϵ-产生式,所以需要消除它们。只要将这两个变量出现的地方分裂为“X非空”和“X为空”两种情况,代入即可。
- S→aBB∣aB∣a∣bAA∣bA∣b
- A→bbA∣bb
- B→aBa∣aa
2. 消除单变量产生式#
没有单变量产生式。
3. 去无用符号#
3.1 从后向前:从终极符串出发逆推#
终极符串有a b aa 和 bb,所以都是有用的。
3.2 从前向后:从开始符号出发正推#
从 S 出发可以推导出 A 和 B,所以它们都是可达的。
补充一个例题#
删除下列文法中的无用符号:
- S→AB∣CA∣a
- A→a
- B→BC∣AB∣DE∣d
- C→aB∣b
先从后向前:终极符串有 a b 和 d,所以 SABC 都有用;剩下 D 和 E 没有派生,删除,得到:
- S→AB∣CA∣a
- A→a
- B→BC∣AB∣d
- C→aB∣b
然后从前向后:从 S 出发可以推导出 A、B 和 C,所以它们都是可达的。
4. 调整为 CNF 的形式#
只允许右侧要么是两个变量、要么是一个终极符。
先把终极符串解决:原本的 A、B 名称变为 X 和 Y,功能变为派生单个终极符:
- S→AYY∣AY∣a∣BXX∣BX∣b
- X→BBX∣BB
- Y→AYA∣AA
- A→a
- B→b
再把多变量串解决——引入新的变量:
- AY→AY
- BX→BX
- S→AYY∣AY∣a∣BXX∣BX∣b
- X→BBX∣BB
- Y→AYA∣AA
- A→a
- B→b
上下文无关语言的构造#
构造产生0和1数目相同、且非空串的文法(上下文无关语言):
- S→0B∣1A: B 表示1要多一个,A 表示0要多一个
- B→1∣1S∣0BB
- A→0∣0S∣1AA
一个错误解法:
S→1X0∣0X1∣01X∣10X∣X01∣X10
因为产生不了 000111111000
构造接收 L={1n02n∣n≥1} 的PDA:
- 状态:q0(记忆状态),q1(读了第一个0),q2(读了第二个0),qf(接受状态)
- 输入字母表:{0,1}
- 栈字母表:{Z,A}(Z 是初始栈符号,A 用来记忆读了多少个1)
- 转移函数:
- δ(q0,1,Z)={(q0,AZ)}
- δ(q0,1,A)={(q0,AA)}
- δ(q0,0,A)={(q1,A)} 读了第一个0
- δ(q1,0,A)={(q2,ϵ)}
- δ(q2,0,A)={(q1,A)} 读了奇数个0
- δ(q2,ϵ,Z)={(qf,ϵ)} 空栈和终态同时接收
图灵机#
计算阶乘:输入为 n 个 0,输出 为 n! 个 0。
基本思路:
n!result0resultt=[n×(n−1)×...×i]×i×...×2=[result+result×(i−1)]×...×2=1=resultt−1×(n−t+1),1≤t<n=resultt−1+resultt−1×(n−t)
- 先在末尾加上
10,回到开头。
- 消去一个输入的
0,在遇到 1 之前,每次读取到一个 0,就把后面的内容复制到最后
- 遇到
1 开始返回。
- 如果没有
0 可以消去(遇到的是 1)则直接把 1 改为空,进入终止状态
追加的时候有些小技巧,比如暂时用 X 标记已经读取的,Y 标记已经追加的,最后统一变为 0。
| δ | 0 | 1 | B | X | Y | 解释 |
|---|
| q0 | (q0,0,R) | (q0,0,R) | (q1,1,R) | | | 到末尾加1 |
| q1 | | | (qb,0,L) | | | 加0后回头 |
| qb | (qb,0,L) | (qb,1,L) | (q2,B,R) | (qb,0,L) | (qb,0,L) | 到开头(back) 顺别负责将X和Y置为0 |
| q2 | (q3,B,R) | (qf,B,R) | | | | 消去第一个0,开始乘法 遇到1说明没0了,结束 |
| q3 | (q4,X,R) | (qe,1,R) | | | | 已读取,准备追加 遇到1说明本轮乘法结束 先把X和Y置为0再开启下一轮 |
| qe | (qe,0,R) | (qe,1,R) | (qb,B,L) | (qend,X,R) | (qe,Y,R) | 到末尾(end) |
| q4 | (q4,0,R) | (q5,1,R) | | | | 找到第一个输出位置 |
| q5 | (q6,X,R) | | | | (q7,Y,L) | 读取一个输出的0 读到Y说明复制完了 |
| q6 | (q6,0,R) | | (qb2,Y,L) | | (q6,Y,R) | 读一个就尾部复制一个 |
| qb2 | (qb2,0,L) | | | (q5,X,R) | (qb2,Y,L) | 回到输入的第一个0继续复制 |
| q7 | | (q8,1,L) | | (q7,0,L) | | 向左将输出的X变为0 回到输入侧 |
| q8 | (q8,0,L) | | | (q3,X,R) | | 到输入最左侧开始下一轮追加 |
模拟 3!=6:
| 序号 | 状态 | 序号 | 状态 | 序号 | 状态 |
|---|
| 1 | q0000BB | 2 | 000q0BB | 3 | 0001q1B |
| 4 | 000qb10 | | 乘2 | | 乘1 |
| 5 | qbB00010 | 33 | qbB001000 | 56 | qbB01000000 |
| 6 | q200010 | 34 | q2001000 | 57 | q20100000 |
| 7 | Bq30010 | 35 | Bq301000 | 58 | Bq31000000 |
| 8 | Xq4010 | 36 | Xq41000 | 59 | 1qe000000 |
| 9 | X0q410 | 37 | X1q5000 | 60 | 100000qb0 |
| 10 | X01q50 | 38 | X1Xq600 | 61 | q21000000 |
| 11 | X01Xq6B | 39 | X1X00q6B | 62 | Bqf000000 |
| 12 | X01qb2XY | 40 | X1X0qb20Y | 答 | 000000=6 |
| 13 | X01Xq5Y | 41 | X1Xq500Y | | |
| 14 | X01q7XY | 42 | X1XXq60Y | | |
| 15 | X0q710Y | 43 | X1XX0Yq6B | | |
| 16 | Xq8010Y | 44 | X1XX0qb2YY | | |
| 17 | q8X010Y | 45 | X1XXq50YY | | |
| 18 | Xq3010Y | 46 | X1XXXq6YY | | |
| 19 | XXq410Y | 47 | X1XXXYYYq6B | | |
| 20 | XX1q50Y | 48 | X1XXXYqb2YY | | |
| 21 | XX1Xq6Y | 49 | X1XXXq5YYY | | |
| 22 | XX1XYq6B | 50 | X1XXq7XYYY | | |
| 23 | XX1Xqb2YY | 51 | Xq71000YYY | | |
| 24 | XX1qb2XYY | 52 | q8X1000YYY | | |
| 25 | XX1Xq5YY | 53 | Xq31000YYY | | |
| 26 | XX1q7XYY | 54 | X1qe000YYY | | |
| 27 | XXq710YY | 55 | X1000YYqbY | | |
| 28 | Xq8X10YY | | | | |
| 29 | XXq310YY | | | | |
| 30 | XX1qe0YY | | | | |
| 31 | XX10YqbY | | | | |
| 32 | XX10qbY0 | | | | |
可以看到复杂度还是很高的。主要是不能随机跳转。实际上 Y 可以用 1 代替,不过为了过程的清晰还是没有复用。