图灵机(Turing machine)与DFA类似,但相比DFA,图灵机具有无限制的存储空间,且读写头可以左右移动、既能读也能写。并且,与DFA不同,当图灵机进入accept或者reject状态时,图灵机会立即停机。
图灵机可以模拟所有实际计算机的所有计算行为,但仍存在不可解的问题。
一台图灵机可以被形式化描述为一个7元组: { Q , Σ , Γ , δ , q 0 , q a c c e p t , q r e j e c t } \{Q, \Sigma, \Gamma, \delta, q_0, q_{accept}, q_{reject}\} {Q,Σ,Γ,δ,q0,qaccept,qreject}.
为描述图灵机的状态转移,在此引入格局(configuration)的概念:格局由当前状态 q i q_i qi、当前带子内容和读写头位置组成。设当前带子内容为 u v uv uv,读写头位于 v v v的第一个字符处,则该格局可以写为 u q i v uq_iv uqiv。同理,可知下图中图灵机的格局为 1011 q 7 01111 1011q_701111 1011q701111。
在图灵机的计算过程中,如果能够从格局 C 1 C_1 C1合法转移到格局 C 2 C_2 C2,则称 C 1 C_1 C1产生(yield) C 2 C_2 C2;由于图灵机的读写头可以左右移动,还需要在转移函数中标记移动的方向。
设 a , b , c a, b, c a,b,c为 Σ \Sigma Σ中的符号, u , v u, v u,v为 Γ \Gamma Γ中的字符串, q i , q j q_i, q_j qi,qj为状态;若状态转移函数满足 δ ( q i , b ) = ( q j , c , L ) \delta(q_i, b) = (q_j, c, L) δ(qi,b)=(qj,c,L),则记为
u a q i b v 产生 u q j a c v uaq_ibv 产生 uq_jacv uaqibv产生uqjacv
解释一下,图灵机的读写头当前位于 b b b,状态转移将 c c c写入 b b b的位置,并将读写头向左移动到 a a a。
同理,若状态转移函数满足 δ ( q i , b ) = ( q j , c , R ) \delta(q_i, b) = (q_j, c, R) δ(qi,b)=(qj,c,R),则记为
u a q i b v 产生 u a c q j v uaq_ibv产生uacq_jv uaqibv产生uacqjv
图灵机的读写头位于 b b b,状态转移将c写入b的位置,并将读写头向右移动到 v v v的第一个字符。
称图灵机接受输入 w w w,如果存在格局序列 C 1 , C 2 , … , C k C_1, C_2, \dots, C_k C1,C2,…,Ck使得:
则称图灵机接受 w w w, w w w构成的语言集合为可被图灵机识别的语言。
若识别某种语言的图灵机一定停机(不进入循环,只进入 q a c c e p t q_{accept} qaccept或 q r e j e c t q_{reject} qreject),则称该语言是可判定的;能够判定某种语言的图灵机称为判定器。
可判定可归约章节的大部分证明都是要求构造判定器,故应当掌握图灵机的常用描述方法。
由于图灵机的形式化表述非常复杂(需要给出转移矩阵或者绘制状态图),对图灵机的描述通常采用描述性的语言进行表述。
当然,如果考试要求的话,还是得画图或者写转移函数的。
状态图的转移箭头使用形如 0 → x , R 0 \to x, R 0→x,R的格式,代表将读写头的字符从0置换到x,并将读写头向右移动。
给出在具体实现中的一些常用技巧:
例1:给出判定语言 A = { 0 n 1 n ∣ n ≥ 0 } A = \{0^n 1^n | n \ge 0\} A={0n1n∣n≥0}的图灵机描述
解:构造图灵机 M M M:
M = “对输入字符串 w : 1. 从左到右,检查是否有 0 出现在 1 的后面 ; 2. 从左向右,扫描整个带子,将一个 0 置换为 x ;继续向右移动,选择一个 1 置换为 y ; 3. 若 0 没有剩余, 1 还有剩余;或者 1 没有剩余, 0 还有剩余,则拒绝;否则接受。” \begin{array}{l} M = \text{“对输入字符串$w$:}\\ 1.从左到右,检查是否有0出现在1的后面;\\ 2.从左向右,扫描整个带子,将一个0置换为x;继续向右移动,选择一个1置换为y;\\ 3.若0没有剩余,1还有剩余;或者1没有剩余,0还有剩余,则拒绝;否则接受。 \text{”} \end{array} M=“对输入字符串w:1.从左到右,检查是否有0出现在1的后面;2.从左向右,扫描整个带子,将一个0置换为x;继续向右移动,选择一个1置换为y;3.若0没有剩余,1还有剩余;或者1没有剩余,0还有剩余,则拒绝;否则接受。”
另一种构造方法为:
M = “对输入字符串 w : 1. 从左到右,检查是否有 0 出现在 1 的后面 ; 2. 从左向右,扫描整个带子,隔一个字符将一个 0 置换为 x ;继续向右移动,隔一个字符将一个 1 置换为 y ; 3. 在每轮扫描结束后,检查 0 和 1 的数量的奇偶性,若两者的奇偶性不一致,则拒绝;否则接受。” \begin{array}{l} M = \text{“对输入字符串$w$:}\\ 1.从左到右,检查是否有0出现在1的后面;\\ 2.从左向右,扫描整个带子,隔一个字符将一个0置换为x;继续向右移动,隔一个字符将一个1置换为y;\\ 3.在每轮扫描结束后,检查0和1的数量的奇偶性,若两者的奇偶性不一致,则拒绝;否则接受。 \text{”} \end{array} M=“对输入字符串w:1.从左到右,检查是否有0出现在1的后面;2.从左向右,扫描整个带子,隔一个字符将一个0置换为x;继续向右移动,隔一个字符将一个1置换为y;3.在每轮扫描结束后,检查0和1的数量的奇偶性,若两者的奇偶性不一致,则拒绝;否则接受。”
举个例子来说,对 00001111 00001111 00001111,每轮结束后带子为:
00001111 0x0x1y1y 0xxx1yyy xxxxyyyy
而对 000001111 000001111 000001111,则有:
000001111 0x0x01y1y
此时出现01数量不符的情况,拒绝该字符串。
严格证明的话,第二种构造方法实现了折半方法,等价于将01个数转化为二进制表示,并在每轮扫描比较对应的二进制位。
例2:构造判定语言 A = { 0 2 n ∣ n ≥ 0 } A = \{0^{2^n} |n \ge0\} A={02n∣n≥0}的图灵机。
解:构造判定该语言的图灵机 M M M
M = “对输入 w : 1. 从左向右,检查 w 中是否存在 0 以外的字符; 2. 从左向右,间隔一个字符,将 0 置换为 x ; 3. 每轮置换结束后,检查 0 的数目,如果为奇数,且不止 1 个,则拒绝;否则接受。” \begin{array}{l} M = \text{“对输入}w:\\ 1.从左向右,检查w中是否存在0以外的字符;\\ 2.从左向右,间隔一个字符,将0置换为x;\\ 3.每轮置换结束后,检查0的数目,如果为奇数,且不止1个,则拒绝;否则接受。 \text{”} \end{array} M=“对输入w:1.从左向右,检查w中是否存在0以外的字符;2.从左向右,间隔一个字符,将0置换为x;3.每轮置换结束后,检查0的数目,如果为奇数,且不止1个,则拒绝;否则接受。”
检查奇数可以通过置换偶数位0的方法,如果本来在偶数位不是0,而是空白符 ⊔ \sqcup ⊔,则说明0的个数是奇数,拒绝。
举例子, 0 x 0 x 0 x ⊔ ⊔ … 0x0x0x\sqcup \sqcup\dots 0x0x0x⊔⊔…,在扫描第3个0的时候,本应有转移 δ ( q 奇数 0 , 0 ) = ( q 偶数 0 , x , R ) \delta(q_{奇数0}, 0) = (q_{偶数0}, x, R) δ(q奇数0,0)=(q偶数0,x,R),实际读入的却不是 0 0 0,而是 ⊔ \sqcup ⊔,因此拒绝。
尝试写一下转移矩阵
状态 0 x ⊔ q 1 ( q 2 , x , R ) ( q 1 , x , R ) ( q 3 , ⊔ , L ) q 2 ( q 1 , x , R ) ( q 2 , x , R ) ( q 4 , ⊔ , L ) q 3 ( q 3 , 0 , L ) ( q 3 , x , L ) ( q 1 , ⊔ , R ) q 4 ( q a c c e p t , x , L ) ( q 4 , x , L ) / q 5 ( q r e j e c t , x , L ) ( q 5 , x , L ) ( q a c c e p t , ⊔ , L ) \begin{array}{|l|c|c|} \hline 状态 & 0 & x & \sqcup \\\hline q_1 & (q_2, x, R) & (q_1, x, R) & (q_3, \sqcup, L)\\\hline q_2 & (q_1, x, R) & (q_2, x, R) & (q_4, \sqcup, L)\\\hline q_3 & (q_3, 0, L) & (q_3, x, L) & (q_1, \sqcup, R)\\\hline q_4 & (q_{accept}, x, L) & (q_4, x, L) & / \\\hline q_5 & (q_{reject}, x, L) & (q_5, x, L) & (q_{accept}, \sqcup, L)\\\hline \end{array} 状态q1q2q3q4q50(q2,x,R)(q1,x,R)(q3,0,L)(qaccept,x,L)(qreject,x,L)x(q1,x,R)(q2,x,R)(q3,x,L)(q4,x,L)(q5,x,L)⊔(q3,⊔,L)(q4,⊔,L)(q1,⊔,R)/(qaccept,⊔,L)
q 1 q_1 q1用于判断奇数0,当识别到奇数0的时候,进入状态 q 2 q_2 q2,寻找偶数0;
如果状态 q 2 q_2 q2在没有找到偶数0的情况下,就到了最右端的空白符,则说明0的个数为奇数,进入新的状态 q 4 q_4 q4,检查0的个数是否为1个;
q 3 q_3 q3为回到左端的状态;
若状态 q 4 q_4 q4,向左能够读到0,则进入状态 q 5 q_5 q5,检查是否只存在一个0;
若状态 q 5 q_5 q5,向左再次读到0,说明0的个数不为1,拒绝;否则读到空白符,接受。
在尝试写转移矩阵的时候,可以首先写出起始状态,然后沿着转移路径依次添加新的状态,并补充该状态对应的转移。
例3:构造判定语言 A = { a i b j c k ∣ i × j = k ; i , j , k ≥ 1 } A = \{a^ib^jc^k|i\times j = k;\ i, j, k \ge 1\} A={aibjck∣i×j=k; i,j,k≥1}的图灵机。
解:构造图灵机 M M M:
M = “对输入 w : 1. 从左向右,检查输入顺序是否符合 a , b , c ; 2. 从左向右,置换一个 a 为 x ,并向右找到 b 的位置,在 b 与 c 之间移动,成对置换 b 和 c ,直到置换所有的 b ;如果 c 被置换完,而 b 有剩余,则拒绝; 3. 若 a 仍有剩余,恢复所有的 b ,重复步骤 2 ;若 a 消尽, c 仍有剩余,则拒绝;否则接受。” \begin{array}{l} M = \text{“对输入}w:\\ 1.从左向右,检查输入顺序是否符合a,b,c;\\ 2.从左向右,置换一个a为x,并向右找到b的位置,在b与c之间移动,成对置换b和c,直到置换所有的b;如果c被置换完,而b有剩余,则拒绝;\\ 3.若a仍有剩余,恢复所有的b,重复步骤2;若a消尽,c仍有剩余,则拒绝;否则接受。 \text{”} \end{array} M=“对输入w:1.从左向右,检查输入顺序是否符合a,b,c;2.从左向右,置换一个a为x,并向右找到b的位置,在b与c之间移动,成对置换b和c,直到置换所有的b;如果c被置换完,而b有剩余,则拒绝;3.若a仍有剩余,恢复所有的b,重复步骤2;若a消尽,c仍有剩余,则拒绝;否则接受。”
例4:构造判定语言 A = { a n b n + 1 c n + 1 ∣ n ≥ 0 } A = \{a^n b^{n +1} c^{n+1}|n\ge 0\} A={anbn+1cn+1∣n≥0}的图灵机。
大体的实现思路:
- 检查顺序
- 首先消一对b, c
- 成对消除a,b,c
解:构造图灵机 M M M:
M = “对输入 w : 1. 从左向右,检查输入顺序是否满足 a , b , c ; 2. 从左向右,找到 b 的位置,将一个 b 置换为 y ,继续向右,找到一个 c 的位置,将一个 c 置换为 z ;如果没有找到 b 或者 c ,则拒绝; 3. 从左向右,成对消除 a , b , c ,如果存在某个字符消尽,仍有字符有剩余的情况,就拒绝,否则接受。” \begin{array}{l} M = \text{“对输入}w:\\ 1.从左向右,检查输入顺序是否满足a, b, c;\\ 2.从左向右,找到b的位置,将一个b置换为y,继续向右,找到一个c的位置,将一个c置换为z;如果没有找到b或者c,则拒绝;\\ 3.从左向右,成对消除a,b,c,如果存在某个字符消尽,仍有字符有剩余的情况,就拒绝,否则接受。 \text{”} \end{array} M=“对输入w:1.从左向右,检查输入顺序是否满足a,b,c;2.从左向右,找到b的位置,将一个b置换为y,继续向右,找到一个c的位置,将一个c置换为z;如果没有找到b或者c,则拒绝;3.从左向右,成对消除a,b,c,如果存在某个字符消尽,仍有字符有剩余的情况,就拒绝,否则接受。”
具有多条带子的图灵机,转移函数允许多个带子同时进行读、写和移动读写头。
易证明:任何多带图灵机,都可被转化为一台单带图灵机(等价性)。
简单来说,只需要将带子依此拼接,并在拼接处填入一个特殊字符标记开始的位置,多带图灵机就可以转化为一台单带图灵机。
类似非确定状态机和下推自动机,如果图灵机在计算过程中,可以非确定的选择多种可能动作中的一个,就称该图灵机为非确定的。非确定形图灵机的转移函数符合 δ : Q × Γ → P ( Q × Γ × { L , R } ) \delta: Q \times \Gamma \to \mathcal P(Q \times \Gamma \times \{L, R\}) δ:Q×Γ→P(Q×Γ×{L,R})。
定理:每个非确定图灵机都等价于某个确定型图灵机。
证明思路:仍采用构造性证明,只要构造出图灵机 D D D,可以模拟非确定性图灵机 N N N的所有可能分支,如果某个分支到达接受状态,则接受;否则继续模拟。
证明:构造确定性图灵机 D D D, D D D包含3条带子,分别为输入带、模拟带和地址带。
给出 D D D的描述:
D = “对输入 w : 1. 将输入读入第 1 个带子; 2. 将第 1 个带子复制到第 2 个带子上; 3. 用第二个带子模拟 N 在输入 w 上非确定计算的某个分支,在 N 的每步动作之前,查询第 3 个带子,寻找可行的选择; 如果第 3 个带子没有对应的节点,则放弃该分支,转入第 4 步;如果进入拒绝状态,也转入第 4 步;如果进入接受状态,则接受; 4. 在第 3 个带子上按照字典序选择下一个串,并转到第 2 步,继续模拟下一个分支。 \begin{array}{l} D = \text{“对输入}w:\\ 1.将输入读入第1个带子;\\ 2.将第1个带子复制到第2个带子上;\\ 3.用第二个带子模拟N在输入w上非确定计算的某个分支,在N的每步动作之前,查询第3个带子,寻找可行的选择;\\ 如果第3个带子没有对应的节点,则放弃该分支,转入第4步;如果进入拒绝状态,也转入第4步;如果进入接受状态,则接受;\\ 4.在第3个带子上按照字典序选择下一个串,并转到第2步,继续模拟下一个分支。 \end{array} D=“对输入w:1.将输入读入第1个带子;2.将第1个带子复制到第2个带子上;3.用第二个带子模拟N在输入w上非确定计算的某个分支,在N的每步动作之前,查询第3个带子,寻找可行的选择;如果第3个带子没有对应的节点,则放弃该分支,转入第4步;如果进入拒绝状态,也转入第4步;如果进入接受状态,则接受;4.在第3个带子上按照字典序选择下一个串,并转到第2步,继续模拟下一个分支。
图灵机可以用来描述某种运算,例如
输入 0 x 1 0 y , 得到 0 f ( x , y ) s . t . f ( x , y ) = { x − y , x > y 0 , x ≤ y 输入0^x10^y,得到0^{f(x, y)}\\ s.t. f(x, y) = \left\{\begin{array}{l} x - y, & x> y\\ 0, & x \le y \end{array}\right. 输入0x10y,得到0f(x,y)s.t.f(x,y)={x−y,0,x>yx≤y
可构造图灵机:
D = “对输入 w : 1. 从左向右扫描一次,如果不存在 1 ,或者存在不止一个 1 ,则拒绝 ; 2. 从左向右扫描,将位于 1 左侧的一个 0 置换为 x ,将 1 右侧的一个 0 置换为 y ,重复该步 ; 3. 若 1 左侧的 0 有剩余, 1 右侧的 0 读尽,则将 1 右侧剩下的 0 作为输出返回 ; 否则返回单个 0 ; ” \begin{array}{l} D = \text{“对输入}w:\\ 1.从左向右扫描一次,如果不存在1,或者存在不止一个1,则拒绝;\\ 2.从左向右扫描,将位于1左侧的一个0置换为x,将1右侧的一个0置换为y,重复该步;\\ 3.若1左侧的0有剩余,1右侧的0读尽,则将1右侧剩下的0作为输出返回;否则返回单个0;\text{”} \end{array} D=“对输入w:1.从左向右扫描一次,如果不存在1,或者存在不止一个1,则拒绝;2.从左向右扫描,将位于1左侧的一个0置换为x,将1右侧的一个0置换为y,重复该步;3.若1左侧的0有剩余,1右侧的0读尽,则将1右侧剩下的0作为输出返回;否则返回单个0;”
例1:设计能够判定语言 { { 0 { 1 , 2 } ∗ } ∪ { 1 { 0 , 2 } ∗ } ∪ { 2 { 0 , 1 } ∗ } } \{\{0\{1, 2\}^*\} \cup \{1\{0,2\}^*\}\cup \{2\{0,1\}^*\}\} {{0{1,2}∗}∪{1{0,2}∗}∪{2{0,1}∗}}的图灵机
解:构造图灵机,绘制状态图如下:
例2:构造将输入向右移动2格的图灵机
解:
构造图灵机N:
N = “对输入 w : 1. 从左到右遍历到字符串尾,再向右移动两格,并置这两格为 y ; 2. 左移,将碰到的第一个字符(假设为 a )置换为 y ,然后右移,将最右侧的 y 置换为 a ; 3. 重复步骤 2 ,直到左移碰到起始标记符 ⊔ ” \begin{array}{l} N = \text{“对输入}w:\\ 1.从左到右遍历到字符串尾,再向右移动两格,并置这两格为y;\\ 2.左移,将碰到的第一个字符(假设为a)置换为y,然后右移,将最右侧的y置换为a;\\ 3.重复步骤2,直到左移碰到起始标记符\sqcup \text{”} \end{array} N=“对输入w:1.从左到右遍历到字符串尾,再向右移动两格,并置这两格为y;2.左移,将碰到的第一个字符(假设为a)置换为y,然后右移,将最右侧的y置换为a;3.重复步骤2,直到左移碰到起始标记符⊔”
例3:构造识别语言 { a n ∣ n 为完全平方数 } \{a^n|n为完全平方数\} {an∣n为完全平方数}的图灵机
解:
首先考察完全平方数的特点,设有一台多带图灵机,每层读到第 k 2 k^2 k2个a的时候进入下一层,则每层应有的a个数应该为:
1: 1
2: 3
3: 5
4: 7
...
可以观察到,这样排列a的时候,每层都比上一层多2个a。
则可以构造下面的图灵机M
M = “对输入 w : 1. 检查 w 是否只包含 a , 存在其他字符就拒绝 ; 2. 第一条带子读入一个 a ,若 a 还有剩余,则进入下一条带子 ; 3. 对上一条带子的每个 a ,下一条带子对应读入一个 a ,再多读 2 个 a ; 4. 若刚好在某条带子正好读完,就接受,否则拒绝。” \begin{array}{l} M = \text{“对输入}w:\\ 1.检查w是否只包含a, 存在其他字符就拒绝;\\ 2.第一条带子读入一个a,若a还有剩余,则进入下一条带子;\\ 3.对上一条带子的每个a,下一条带子对应读入一个a,再多读2个a;\\ 4.若刚好在某条带子正好读完,就接受,否则拒绝。\text{”} \end{array} M=“对输入w:1.检查w是否只包含a,存在其他字符就拒绝;2.第一条带子读入一个a,若a还有剩余,则进入下一条带子;3.对上一条带子的每个a,下一条带子对应读入一个a,再多读2个a;4.若刚好在某条带子正好读完,就接受,否则拒绝。”
例4:整数乘法 0 m 1 0 n → 0 m n 0^m10^n \to 0^{mn} 0m10n→0mn
解:
构造一台多带图灵机M
M = “对输入 w : 1. 第一个带子读入 w ,检查 w 是否只包含 1 个 1 ; 2. 从左向右,将第一个带子上位于 1 左侧的 1 个 0 置换为 x ,移动到 1 的右侧,依此将 1 右侧的 0 置换为 y ,并在第二条带子上录入等量的 0 ; 3. 如果第一条带子上, 1 左侧的 0 未读尽,则恢复第一条带子 1 右侧的所有 0 ,重复步骤 2 ,直到所有 1 左侧的 0 都读尽; 4. 将第二条带子作为输出。” \begin{array}{l} M = \text{“对输入}w:\\ 1.第一个带子读入w,检查w是否只包含1个1;\\ 2.从左向右,将第一个带子上位于1左侧的1个0置换为x,移动到1的右侧,依此将1右侧的0置换为y,并在第二条带子上录入等量的0;\\ 3.如果第一条带子上,1左侧的0未读尽,则恢复第一条带子1右侧的所有0,重复步骤2,直到所有1左侧的0都读尽;\\ 4.将第二条带子作为输出。 \text{”} \end{array} M=“对输入w:1.第一个带子读入w,检查w是否只包含1个1;2.从左向右,将第一个带子上位于1左侧的1个0置换为x,移动到1的右侧,依此将1右侧的0置换为y,并在第二条带子上录入等量的0;3.如果第一条带子上,1左侧的0未读尽,则恢复第一条带子1右侧的所有0,重复步骤2,直到所有1左侧的0都读尽;4.将第二条带子作为输出。”