【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又改。總的來說弄出來還是很有成就感的,以後可以做注重邏輯方面的訓練。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章