程序设计作业之模拟图灵机
-
题目分析
对于任意给定的一台Turing机和任意给定的字符串w ( w不含空格),编程模拟此Turing机的运行过程,要求输出从开始运行起的每一步骤的结果。
根据书上的内容,模拟图灵机的方式主要是模拟自然数加1,自然数乘2的操作。因此决定设计一个自然数模拟图灵机乘2和加1的程序,由题目可以知道大概要进行的步骤有输入数据,对数据进行转换,模拟图灵机指令对数据进行操作等。 -
算法构造
1)自然数转化为二进制数
自然数转化为二进制数可以通过自然数对2进行求模运算获得,由于求模得到的结果与实际的结果位置是颠倒的,因此还需要进行位置调换的操作。
2)对二进制数进行扩展
为了与其他的数分隔开,规定了一些分隔符,因此需要对二进制数进行扩展,便于模拟图灵机的操作。利用书上的规则令1=10,0=0,‘,=110’即可转化为便于操作的数。
3)模拟图灵机指令
模拟自然数乘2的图灵机指令主要是对一进位数据进行读写操作,并且需要左右移动读写数据,因此可以通过双向链表实现,利用双向链表的指针可以很方便的向前向后移动,并且修改数据也很方便。所以在自然数进行转化扩展之后应该将其存放到链表中以便于操作。 -
算法实现
1)自然数转化为二进制数
int *transform(int x,int *arr)//传入自然数x和全局变量arr
{
int i,j,t;
arr=(int*)malloc(sizeof(int)*10);
for(i=0;x>0;i++)
{
arr[i]=x%2; //获取余数
x=x/2;
}
for(j=0;j<i/2;j++) //将得到的位置颠倒的二进制数顺序调换变为正常顺序
{
t=arr[j];
arr[j]=arr[i-j-1];
arr[i-j-1]=t;
}
printf("自然数转换为二进制的结果为:");
i=0;
while(arr[i]==0||arr[i]==1)
{
printf("%d",arr[i]);
i++;
}
return arr; //将指针返回
}
2)将二进制数进行扩展
int *extend( int *arry,int *b)
{
int i=0,j=0,s=0;
b=(int*)malloc(sizeof(int)*50);
b[0]=0;
while(arry[i]==1||arry[i]==0)
{
if(arry[i]==1) //令1=10
{
b[s+1]=1;
b[s+1+1]=0;
s++;
}
else
{
b[s+1]=arry[i];
}
i++;
s++;
}
i=0;
while(b[i]==1||b[i]==0)
{
i++;
}
b[i]=1; //为了便于观察得到的结果,在数字串末尾多加几个零
b[i+1]=1;
b[i+2]=0;
b[i+3]=0;
b[i+4]=0;
return b;
}
3)模拟图灵机指令
LinkList creatlist(int *m)
{
LinkList head,end;
LNode *L;
int i,j=0;
printf("\n二进制扩展之后的结果为:");
while(m[j]==1||m[j]==0) //将之前二进制数扩展的结果输出并获取数字的个数
{
printf("%d",m[j]);
j++;
}
head=(LinkList )malloc(sizeof(LNode)); //为头指针申请一个空结点
head->next=NULL; //置空链表
end=head; //让尾指针指向头指针
for( i=0;i<j;i++)
{
L=(LinkList)malloc(sizeof(LNode)); //为新创建的结点申请空间
L->data=m[i];
L->prev=L;
end->next=L;
L->prev=end;
end=L;
}
end->next=NULL;
return head; //返回头结点
}
//模拟图灵机的指令
LinkList instructions(LinkList head)
{
int neitai=0; //初始内态为零
int input;
LinkList L;
L=head->next;
printf("\n请选择模拟图灵机加1(输入1)还是乘2(输入2)运算:");
scanf("%d",&input);
if(input==1)
{
while(L!=NULL)
{
if(neitai==0&&L->data==0) //内态为零并读到的数据为0
{ L->data=0; //将当前的数据改为0,即不变
neitai=0;
L=L->next; //指向下一个结点
}
else if(neitai==0&&L->data==1)
{ L->data=1;
neitai=1;
L=L->next;
}
else if(neitai==1&&L->data==0)
{
L->data=0;
neitai=0;
L=L->next;
}
else if(neitai==1&&L->data==1)
{ L->data=1;
neitai=10;
L=L->next;
}
else if(neitai==10&&L->data==0)
{ L->data=0;
neitai=11;
L=L->prev; //指向前一个结点
}
else if(neitai==10&&L->data==1)
{ L->data=1;
neitai=10;
L=L->next;
}
else if(neitai==11&&L->data==0)
{ L->data=1;
neitai=0;
L=L->next;
break; //停止,跳出循环
}
else if(neitai==11&&L->data==1)
{ L->data=0;
neitai=100;
L=L->prev;
}
else if(neitai==100&&L->data==0)
{ L->data=1;
neitai=101;
L=L->prev;
}
else if(neitai==100&&L->data==1)
{ L->data=1;
neitai=100;
L=L->prev;
}
else if(neitai==101&&L->data==0)
{ L->data=0;
neitai=110;
L=L->next;
}
else if(neitai==101&&L->data==1)
{ L->data=1;
neitai=10;
L=L->next;
}
else if(neitai==110&&L->data==1)
{ L->data=1;
neitai=111;
L=L->next;
}
else if(neitai==111&&L->data==0)
{ L->data=1;
neitai=11;
L=L->next;
}
else
{
L->data=0;
neitai=111;
L=L->next;
}
}
}
else
{
while(L!=NULL)
{
if(neitai==0&&L->data==0)
{ L->data=0;
neitai=0;
L=L->next;
}
else if(neitai==0&&L->data==1)
{ L->data=0;
neitai=1;
L=L->next;
}
else if(neitai==1&&L->data==0)
{
L->data=1;
neitai=0;
L=L->next;
}
else if(neitai==1&&L->data==1)
{ L->data=0;
neitai=10;
L=L->next;
}
else if(neitai==10&&L->data==0)
{ L->data=1;
neitai=11;
L=L->next;
}
else if(neitai==11&&L->data==0)
{ L->data=1;
neitai=0;
L=L->next;
break;
}
}
}
return head;
}
- 调试、测试及运行结果
调试结果
指针arr为全局变量,在函数中使用时要开辟空间以存放数据,返回时直接返回的是数组中第一个数的地址。
模拟图灵机的指令,要注意当前结点指针的移动方向,以及停止位置ZG4ubmV0L3dlaXhpbl80Mzc1OTM0Mg==,size_16,color_FFFFFF,t_70)
最终结果,可以看到一个自然数每一步转化的结果。
测试结果测试主要是对二进制转化和链表创建的测试对二进制的测试,可以看到能够输出相对应的正确的二进制数。
对链表的测试,链表的结果能够正常输出。
运行结果模拟自然数乘2:可以看到输入6,模拟图灵机乘2的指令最后输出的结果为00101000110,将该数按照扩展的逆方式进行操作,得到“01100,”,再转化为十进制数即为12,可见该模拟过程是成功的。
模拟图灵机加1操作:输入126,模拟图灵机加1的指令最后输出的结果为0101010101010101100,将该数按照扩展的逆方式进行操作,得到“01111111,”,再转化为十进制数即为127,可见该模拟过程是成功的。
5.经验归纳
通过此次用程序模拟图灵机指令对自然数乘2以加1的操作,巩固了对链表的操作知识,特别是对双向链表能够进一步的熟练使用。在程序中也碰到了很多问题,比如如何返回一个数组,最后通过全局变量的指针解决了这个问题,还有就是链表的创建有较大的问题,需要多练习。