最近刚考完可计算理论,考前看习题总有一些题让设计一个图灵机来实现某个算法什么的(≖-≖)(虽然考试题里完全没有考到!然而我还是勤勤恳恳地想了很久)当时看图灵机定义看了无数遍,但依然不是很明白怎么设计啊摔(手动翻桌…)后来在众多友人的帮助下终于想通了!(〃'▽'〃)接下来让本小白用最白的话举几个小例子帮助更好地理解图灵机:
首先简单讲一下图灵机的构成成分(这个定义并不官方,就本小白瞎定义的,不过基本包括了下面例子里会提到的所有概念):M=(Q,A,B,R,t,f,Q0,Qaccept,Qreject) 其中:
Q:有限的状态集合;
A:有限的输入字母表(不包含空白符B);
R:代表无限长度的纸带,纸带上可能出现A里的任意字母,也可能是空白符B(可以把纸带看做是一个磁盘);
t:代表图灵机的读写头,可以在纸带上左右移动并进行读写操作;
f:转移函数(f:Q × A -> Q × A × {L,R} 转移函数实际上是定义在图灵机的有限控制机里的一张状态转换图的表,我理解的是,在根据当前控制机的状态q以及读写头t读取的当前输入a,在表里找到其对应的新的状态q',在当前纸带的格子上写入的a',以及读写头下一步应该往哪边移动{Left,Right});
Q0:Q0∈Q,图灵机的初始状态(读写头t的初始位置一般都是纸带的第一格);
Qaccept:接受状态(有限的状态集合);
Qreject:拒绝状态,且Qreject≠Qaccept.
1. 设计一个能识别aaabbb类似字符串的图灵机:输入类似ab,aabb,aaabbb,aaa……bbb……(a,b的数量相等)的字符串则接受该输入,否则拒绝。
在这个问题里面A = {a,b,*} (PS.*代表其他可能的字符)
首先看一下在我的想象中这大概是个啥情况(B是空白字符啊切记):
这时候最重要的就是定义转移函数,定义转移函数就相当于设计算法(对于一个可计算的问题,我们可以设计很多个图灵机,每个图灵机就对应着不同的算法)。
例如我们的解题思路是:
从字符串aa……bb的左右两边依次交替消除a和b(即用空白符B代替当前字符),如果纸带上的a,b恰好能消除完,那证明a,b的数量相等,否则a,b的数量不相等。那这个算法在图灵机上如何实现呢?
状态图可以帮我们很好地理解图灵机的运作过程:
(PS:【读/写,移动】,例如【a/B,R】表示读写头t读到当前格子上的字符为a,读写头t把当前格子上的字符改写成空白B,读写头t向右移动一格)
Q0:初始状态,读写头在纸带的左端第一格(无限长的纸带只有左端,没有右端),若当前字符为空白B,则写B(即不修改当前字符),然后t向右移动,这个时候状态是不变的,控制机停留在Q0状态。
当t读到第一个a的时候,t将a改写成B(即消除字符串的左边第一个a),t向右移动,控制机切换到Q1状态。那如果没有读到a却读到一个b呢?这代表该字符串b多于a,不满足要求,于是输出拒绝。
下一步我们要到达字符串的最右端,找到最右端的b并消除它。
Q1:在Q1状态时,t继续读取字符,读a写a右移,读b写b右移,直到读到一个空白B,这代表我们读到了字符串的末尾!这时最右端的b就应该在当前t的左边一格,所以这时要读B写B左移找到最右端的b,控制机切换到Q2。
Q2:读写头t现在处在字符串最右端一格,如果当前字符为a则代表a的数量多于b,输出拒绝,如果为b则消除b改写为B,读写头t左移,控制机切换到Q3。这样就完成了一次(a,b)的消除。接下来我们要做的就是回到字符串的最左端,开始新一轮的消除。
Q3:现在t需要往左走,如果往左读到第一个字符是a代表已经没有b了,因此a,b的数量是不对等的,输出拒绝,如果读到字符为空白B,则代表没有a也没有b,那么aa……bb恰好被消除完,那么这个字符是满足要求的,输出接受。如果读到b,则读b写b左移,状态切换到Q4。
Q4:读b写b左移,读a写a左移。读到空白符B则代表到了字符串最左边,读B写B右移找到字符串的第一个字符,此时控制机又回到了状态Q0。
根据状态图我们可以写出一张状态转换表,这就相当于是在控制机内的转移函数了。
当前状态 | 读B | 读a | 读b |
Q0 | (写B,右移,Q0) | (写B,右移,Q1) | (写b,右移,Qreject) |
Q1 | (写B,左移,Q2) | (写a,右移,Q1) | (写b,右移,Q1) |
Q2 | (写B,左移,Qreject) | (写a,左移Qreject) | (写B,左移,Q3) |
Q3 | (写B,左移,Qaccept) | (写a,左移,Qreject) | (写b,左移,Q4) |
Q4 | (写B,右移,Q0) | (写a,左移,Q4) | (写b,左移,Q4) |
2.计算3+5。
在初始图灵机的定义里是没有内存的,也就是我读了第N格上的数字,当我移到N+1的时候,我已经忘记了N上写的是什么了,那我们怎么设计一个图灵机来定义一个用任何编程语言都能轻易写出来的加法运算呢?
在当前问题中A = {0,1,2,3……}
假设输入纸带是长这样:
B | B | B | 3 | 5 | B | B | B | B | B | B | B | B | B |
B |
B | B | B | B | B |
思想其实也很简单,移动t找到第一个数字3, 写入3-1,右移到第二个数字5,写入5+1,然后再左移到第一个数字,递减1,直到第一个数字为0,则第二个数字所写的数字即为两者之和。
3.输入abbacc,输出abbaccccabba(即将输入字符串翻转并与原字符串相连)。
在这个问题里A = {a,b,c,*}
基本思想:引入其他字符分别代表原字符串的字符用来标记已读和还原!
假设输入纸带是长这样:
B | a | b | b | a | c | c | B | B | B | B | B | B | B |
B |
B | B | B | B | B |
既然是要翻转,那最好是从字符串的右端开始,首先读写头先移到最右的c处,将c改写成X,然后t右移到最近的一个空白格,写下c。此处的状态转换为:
(Qi:读c,写X,右移,Qj)->(Qj:读B,写c,左移,Qk)
B | a | b | b | a | c | X | c | B | B | B | B | B | B |
B |
B | B | B | B | B |
然后t左移,移到最近的属于A的字符,如上图即为c,读c写X右移,找到空白格B写c。
B | a | b | b | a | X | X | c | c | B | B | B | B | B |
B |
B | B | B | B | B |
接下来安同样的套路,读a写Y右移,读B写a左移。直到字符串变成:
B | Y | Z | Z | Y | X | X | c | c | a | b | b | a | B |
B |
B | B | B | B | B |
通过上面的几个小例子应该可以理解一个简单粗暴的图灵机是怎么工作的了吧~~虽然很粗糙,但是设计的过程还是挺有意思的ヾ(◍°∇°◍)ノ゙