【C++】模拟图灵机实现输入十进制数转化成XN(收缩)形式并实现*2操作

           阅读此博客之前需要您对图灵机模型有一定的了解~比如我就是在程序设计方法学中学到的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又改。总的来说弄出来还是很有成就感的,以后可以做注重逻辑方面的训练。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值