阅读此博客之前需要您对图灵机模型有一定的了解~比如我就是在程序设计方法学中学到的0 0
代码中有很全的注释,如果不想看内容建议直接看二、算法分析(4)整体过程+代码即可~
一、题目要求
对于任意给定的一台Turing机,输入一个十进制大于等于零的整数,编程模拟此图灵机的运行过程。
要求:(1)将输入的十进制数转化成XN形式,并实现XN*2过程
(2)输出从开始运行起的每一步骤的结果
(3)转化否的结果以十进制表示
二、算法分析
ps:以下为了便于理解将不采用专业术语,而仅为个人理解,如果有误,敬请谅解quq
(1)什么是UN形式?什么是XN形式?
【UN形式】原始的图灵机是在纸带上表示的,有数字就在纸带上加上记号,没有数字就空着;用“0”来表示空白方格,用“1”来代表记号方格。而每个数全由“1”表示,5:“11111”;7:“1111111” 例如
表示 4 3 7 2 (空格随便打的,不影响)
这种表示方法的优点就是简单易懂;缺点也很明显,如果数字很大,那就要有很多个1,很浪费资源。
【XN形式(收缩)】
(介绍省略)
转换规则:(1)0110表示逗号,用于隔开数与数(如果只有一个数则作为结尾)
(2)10表示1(位于结尾的话表示10):
(3)0就是0,2个0收缩是1个0,3个0收缩是2个0以此类推。
实例可见代码
(2)XN*2的转换规则
00->00R 01->10R 10->01R 11->100R 100->111R 110->01STOP
左边加粗的表示纸带上此时的数,右边加粗的表示将现在的数改成这个数。
左边未加粗的数表示图灵机此时的内态,右边未加粗的表示图灵机要改变到的内态。
R表示向右移动,STOP表示终止操作。
例如01->10R情况为:此时图灵机内态为0,并且读到纸带上的数为1;那么久把这个1改成0,并将内态变为1。01相当于是发生改变的条件,只要满足这个条件就进行右边的改变,不满足就不改变。
(3)如何实现二进制数转化成XN形式(麻烦的地方)
我的思路是重新创建一个数组,从后往前一一读取二进制数从而进行转化,这里可以一开始把这个数组全部初始化为-1,后面只要将从第一个不为-1开始的数组取出来就可以了。ps:后面与室友聊天时想到其实也可以不从后往前,直接取第一个不为零的一的位置+逗号在的位置就可以了,这样也可以开稍微小一点的数组。
(4)总体过程
(1)输入十进制数
(2)将十进制数转化成二进制数
(3)将二进制数转化成XN形式并加上逗号
(4)将XN形式的数复制进另一个数组里,目的是*2操作后的数可能会大于16位
(5)对XN形式进行*2操作并输出每一步操作
(6)去掉XN*2数的逗号和第一个1前面的0,方便后续转化成二进制数
(7)将进行了第六步操作的数存入一个更小的数组
(8)将XN*2转化成二进制数
(9)将二进制数转化成十进制数
(10)输出十进制数
三、代码实现
/****
Anthor:Innocence
IDE:dev c++
OS:win 10
Edition:1.0
Time:2019/3/28
Description:输入一个十进制大于等于零的整数,编程模拟此图灵机的运行过程。
具体过程:(1)输入十进制数
(2)将十进制数转化成二进制数
(3)将二进制数转化成XN形式并加上逗号
(4)将XN形式的数复制进另一个数组里,目的是*2操作后的数可能会大于16位
(5)对XN形式进行*2操作并输出每一步操作
(6)去掉XN*2数的逗号和第一个1前面的0,方便后续转化成二进制数
(7)将进行了第六步操作的数存入一个更小的数组
(8)将XN*2转化成二进制数
(9)将二进制数转化成十进制数
(10)输出十进制数
****/
#include<iostream>
#include<bitset>
#include<math.h>
using namespace std;
int main()
{
int n;
cout<<"请输入一个十进制整数:"<<endl;
cin >> n;
bitset <16> bin(n); //将输入的十进制以二进制的形式输出 16是显示的长度,便于存放很大的数
int zeroOne; //存放bitset里的二进制数的第一个不为0的1的位置
int state=0; //图灵机的内态
int final[100]; //存放进行了XN*2操作后的数
int end; //进行了XN*2操作后的数中逗号在的前一位,目的是去掉逗号简化XN形式转化成十进制数的操作
int num=0; //存放*2后的二进制转化成的十进制
int last[20]; //存放XN转化为二进制后的数
int mark; //第行了XN*2操作后的数中一个不为0的1开始的位置 ,目的是简化XN形式转化成十进制数的操作
//输出十进制数转化成的二进制形式
int time=0; //记录进行*2操作的次数
cout<<endl<<"bin:"<<bin<<endl; //输出二进制
//对于bitset从左到右坐标依次减小,和数组相反
for(int i=15;i>=0;i--){
if(bin[i]==0){
continue;
}
else {
zeroOne=i; //第一个1的位置
break;
}
}
//这个数组存放将二进制转化为xn表示的形式后的数
int transform[(zeroOne+1)*2+4]; //加4是加上逗号(0110)的大小
//初始化transform数组
for(int i=0;i<(zeroOne+1)*2+4;i++){
transform[i]=-1;
}
//初始化final数组
for(int i=0;i<100;i++){
final[i]=0;
}
//初始化last数组
for(int i=0;i<20;i++){
last[i]=0;
}
//加逗号(0110)在末尾
transform[(zeroOne+1)*2+2]=transform[(zeroOne+1)*2+1]=1;
transform[(zeroOne+1)*2+3]=transform[(zeroOne+1)*2]=0;
以下是将二进制转化成XN形式
int j=0;
for(int i=(zeroOne+1)*2-1;i>=0;i--){
//含有00的情况:包括1001,10001,100001等 ,每次只存入一个0
if(bin[j]==0&&transform[i+1]==0){
transform[i]=0;
j++; //比如此时指向1001的第一个零,则令转化数组对应的为0,向后移动一位
continue;
}
//例如101的情况,则存入00
if(bin[j]==0&&transform[i+1]!=0){
transform[i]=0;
i-=1;
transform[i]=0;
j++;
continue;
}
// 10的情况 直接存入1
if(bin[j]==1&&transform[i+1]==0){
transform[i]=1;
j++;
continue;
}
//11的情况 存入01(从左往右)
if(bin[j]==1&&transform[i+1]==1){
transform[i]=0;
i-=1;
transform[i]=1;
j++;
continue;
}
}
cout<<endl<<"转化后的XN:形式:"<<endl;
for(int i=0;i<(zeroOne+1)*2+4;i++)
cout<<transform[i];
//XN形式的数复制进另一个数组里,目的是*2操作后的数可能会大于16位
for(int i=0;i<(zeroOne+1)*2+4;i++)
final[i]=transform[i];
cout<<endl<<endl<<"复制后的数:"<<endl;
for(int i=0;i<100;i++){
cout<<final[i];
}
/进行*2操作
for(int i=0;i<100;i++){
//00->00R
if(state==0&&final[i]==0){
time+=1;
continue;
}
//01->10R
if(state==0&&final[i]==1){
state=1;
final[i]=0;
time+=1;
continue;
}
//10->01R
if(state==1&&final[i]==0){
state=0;
final[i]=1;
time+=1;
continue;
}
//11->100R
if(state==1&&final[i]==1){
state=10;
final[i]=0;
time+=1;
continue;
}
//100->111R
if(state==10&&final[i]==0){
state=11;
final[i]=1;
time+=1;
continue;
}
//110->01STOP
if(state==11&&final[i]==0){
state=0;
final[i]=1;
time+=1;
break;
}
}
cout<<endl<<endl<<"经过了转化之后:"<<endl;
for(int i=0;i<100;i++)
cout<<final[i];
cout<<endl<<endl<<"一共进行了:"<<time<<"次操作"<<endl;
///以下是删除逗号部分
for(int i=0;i<100;i++)
if(final[i]==0&&final[i+1]==1&&final[i+2]==1&&final[i+3]==0){
//这是我一开始分的情况,来来回回改了很多次,最后才发现其实哪种情况的end都是等于i的,具体可以自己试一下
// if(final[i-1]==0&&final[i-2]==1){
// end=i;
// break;
// }
// if(final[i-1]==0){
// end=i+1;
// break;
// }
//
// if(final[i-1]==1){
// end=i;
// break;
// }
end=i;
}
cout<<endl<<"去除了逗号之后的数之后:"<<endl;
for(int i=0;i<end;i++){
cout<<final[i];
}
//删除第一个1前面的0之后
for(int i=0;i<end;i++){
if(final[i]==0&&final[i+1]==1){
mark=i+1; //mark为第一个不为0的位置,+1是为了下一个循环从1开始
break;
}
}
//输出删除了逗号以及第一个1前面的0之后的数字
cout<<endl<<endl<<"删除第一个1前面的0和逗号之后:"<<endl;
for(int i=mark;i<end;i++){
cout<<final[i];
}
cout<<endl<<endl;
int z=mark;
int t[end-mark];//这个数组存放去掉第一个1前面的0以及逗号的后的剩余数
for(int i=0;i<end-mark;i++){
t[i]=final[z];
z+=1;
}
/将XN*2后的数转化成二进制数
cout<<"存放*2操作后的二进制数的大小为:"<<end-mark<<endl;
int y=0;
for(int i=0;i<end-mark;i++){
if(t[i]==0&&t[i+1]==1&&t[i-1]==1){
continue;
}
if(t[i]==0&&t[i+1]==1){
//这种是例如10001的到了01的时候情况
continue;
}
if(t[i]==0&&t[i+1]==0){
//这种是例如10001中00的情况,100001,10000001同样如此
last[y]=0;
y+=1;
continue;
}
if(t[i]==1&&i<end-mark-2){
last[y]=1;
y+=1;
continue;
}
if(t[end-mark-1]==0&&t[end-mark-2]==1){
//这种是以10结尾的特殊情况,此时要输出10而不是1
last[y]=1;
y+=1;
last[y]=0;
y+=1;
break;
}
if(t[end-mark-1]==0&&t[end-mark-2]==0){
//这种是以00结尾的情况,例如10000,如果不加的话只能输出1000
last[y]=0;
y+=1;
break;
}
}
//输出转化后的二进制数
cout<<endl<<"转化后的二进制数为:"<<endl;
for(int i=0;i<y;i++){
cout<<last[i];
}
//将二进制数转化成十进制数
for(int i=y;i>=0;i--){
num+=pow(2,i-1)*last[y-i];
}
//cout<<endl<<endl<<"原本的数为:"<<n<<endl;
cout<<endl<<"进行*2操作后的数为:"<<num<<endl;
return 0;
}
四、运行结果
输入1
输入9999
五、总结
前前后后一共弄了大概四天吧,最难的部分就是将二进制转化成XN形式以及XN形式转化成二进制,这部分主要涉及到的逻辑部分很多,在纸上演算了很多才弄清楚。后面好不容易做出来了输入999的时候还错了quq又改。总的来说弄出来还是很有成就感的,以后可以做注重逻辑方面的训练。