閱讀此博客之前需要您對圖靈機模型有一定的瞭解~比如我就是在程序設計方法學中學到的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又改。總的來說弄出來還是很有成就感的,以後可以做注重邏輯方面的訓練。