图灵机定义
图灵机(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}.
- 状态集 Q Q Q
- 字母表 Σ \Sigma Σ
- 带子 Γ \Gamma Γ
- 转移函数 δ \delta δ
- 起始状态 q 0 q_0 q0
- 接受状态 q a c c e p t q_{accept} qaccept
- 拒绝状态 q r e j e c t q_{reject} 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使得:
- C 1 C_1 C1为起始格局
- C i C_i Ci可以产生 C i + 1 C_{i + 1} Ci+1
- C k C_k 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,并将读写头向右移动。
给出在具体实现中的一些常用技巧:
- 回到左端的方法:在最左端增加标记符,例如增加空白符 ⊔ \sqcup ⊔。
- 大多 O ( n 2 ) O(n^2) O(n2)的算法,都可以用间隔替换的方法优化到 O ( n log n ) O(n\log n) O(nlogn)(下文会给例子)。
- 对需要排查顺序的问题,例如 { 0 n 1 n } \{0^n 1^n\} {0n1n}, 0 0 0一定是在 1 1 1前面的,描述中应先进行顺序检查。
例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条带子,分别为输入带、模拟带和地址带。
- 输入带只包含输入,且不再变化;
- 模拟带对应 N N N的某个非确定性计算分支,在 N N N的计算树上有特定位置
- 地址带记录 D D D在 N N N分支上的位置。
给出
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.将第二条带子作为输出。”
参考
- 《计算理论导引》第二版,机械工业出版社