一、题目分析
所谓的图灵机就是指一个抽象的机器,它有一条无限长的纸带,纸带分成了一个一个的小方格,每个方格有不同的颜色。有一个机器头在纸带上移来移去。机器头有一组内部状态,还有一些固定的程序。在每个时刻,机器头都要从当前纸带上读入一个方格信息,然后结合自己的内部状态查找程序表,根据程序输出信息到纸带方格上,并转换自己的内部状态,然后进行移动。
通过编程的方式模拟实现图灵机的运行过程,我们需要先对图灵机的操作步骤进行抽象概括。在本次实验中我将通过C语言编程的方式模拟图灵机(XN*2)的运行过程实现求某个整数的倍数运算。
分析:
根据图灵机(XN*2)的运行过程,可将其操作步骤概括为以下五点:
- 将输入的十进制数转换成二进制
- 将二进制数转换成计算机能够直接识别的扩展二进制数
- 根据指令实现对扩展二进制数按指令操作
- 因为操作结果仍为扩展二进制数,则需要将其收缩为普通二进制数
- 将收缩完成的二进制数转化为十进制数输出
结论:
可以根据这五个步骤设计五个函数,通过main()函数依次调用它们,从而实现对图灵机的编程模拟。
二、算法构造
● 设计transform()函数实现将输入的十进制数转换为二进制
● 设计extend()函数实现扩展二进制
● 设计realize()函数实现对扩展二进制的按指令操作
● 设计shrink()函数实现将扩展二进制收缩为普通二进制
● 设计reverse()函数实现将二进制转换为十进制
已知图灵机(XN*2)的指令如下:
0 0 → 0 0 R,
0 1 → 1 0 R,
1 0 → 0 1 R,
10 0 → 11 1 R,
11 0 → 0 1 STOP。
这五条指令是设计realize()函数的理论依据。
三、算法实现(源代码)
#include <stdio.h>
#include <math.h>
int size1 = 0; // size1存储二进制数的长度
int size2 = 0; // size2存储扩展二进制的长度
int size3 = 0; // size3存储按指令操作后的二进制长度
int size4 = 0; // size4存储收缩二进制的长度
// 通过transform()函数实现十进制转二进制
int* transform(int x)
{
int rem; // 存储x对2取余后的余数
int i = 0, j = 0;
int r;
int a[100]; // 数组a[]倒序存储十进制转换后的二进制
static int b[100]; // 数组b[]正序存储十进制转换后的二进制
while(x!=0) // 十进制转二进制
{
rem = x%2;
a[i] = rem;
i++;
x/=2;
}
for(r=i-1; r>=0; r--) // 通过for循环将二进制数正序存入数组b[]中
{
b[j] = a[r];
j++;
}
size1 = j;
return b;
}
// 通过extend()函数实现二进制的扩展
int* extend(int p[])
{
int i = 0, j, k;
static int c[100]; // 数组c[]存储扩展二进制
c[i] = 0; // 数组首位存入0
i++;
for(j=0; j<size1; j++)
{
k = p[j];
if(k==0) // 若从二进制数组中扫描到0,则将一个0存入扩展二进制数组c[]中
{
c[i] = 0;
i++;
}
else if(k==1) // 若从二进制数组中扫描到1,则将一个1、一个0依次存入扩展二进制数组c[]中
{
c[i] = 1;
i++;
c[i] = 0;
i++;
}
}
c[i] = 1;
i++;
c[i] = 1;
i++;
c[i] = 0;
size2 = i+1;
return c;
}
// 通过realize()函数实现对扩展二进制的按指令操作
int* realize(int q[])
{
int i, j = 0, k = 0;
int inner = 0; // inner为内态
for(i=0; i<size2+2; i++)
{
k = q[i];
if(k==0 && inner==0) // 输入为0,内态为0
{
q[i] = 0;
inner = 0;
}
else if(k==0 && inner==1) // 输入为0,内态为1
{
q[i] = 1;
inner = 0;
}
else if(k==0 && inner==10) // 输入为0,内态为10
{
q[i] = 1;
inner = 11;
}
else if(k==0 && inner==11) // 输入为0,内态为11
{
q[i] = 1;
inner = 0;
}
else if(k==1 && inner==0) // 输入为1,内态为0
{
q[i] = 0;
inner = 1;
}
else if(k==1 && inner==1) // 输入为1,内态为1
{
q[i] = 0;
inner = 10;
}
else if(k==1 && inner==10) // 输入为1,内态为10
{
q[i] = 0;
inner = 0;
}
}
size3 = i;
printf("%d", size3);
return q;
}
// 通过shrink()函数实现对操作结果的收缩
int* shrink(int r[])
{
int i, j = 0;
static int d[100];
for(i=1; i<size3-3; i++)
{
if(r[i]==0 && r[i+1]==0 )
{
d[j] = 0;
j++;
continue;
}
if(r[i]==0 && r[i+1]==1 && r[i+2]==0)
{
d[j] = 1;
j++;
continue;
}
}
size4 = j;
return d;
}
// 通过reverse()函数实现二进制转十进制
int reverse(int s[])
{
int sum = 0;
int i;
for(i=0; i<size4; i++)
{
sum += s[i] * (int)pow(2,(size4-1-i));
}
return sum;
}
void main()
{
int x; // 存放用户键入的整数
int i = 0;
printf("请输入一个十进制整数:\n");
scanf("%d", &x);
int*p;
p = transform(x);
printf("十进制数转换成二进制数为:\n");
for(i=0; i<size1; i++)
printf("%d", p[i]);
printf("\n");
int*q;
q = extend(p);
printf("扩展二进制数为:\n");
for(i=0; i<size2; i++)
printf("%d", q[i]);
printf("\n");
int*r;
r = realize(q);
printf("指令执行结束生成的二进制数:\n");
for(i=0; i<size3; i++)
printf("%d", r[i]);
printf("\n");
int*s;
s = shrink(r);
printf("图灵机计算结果的二进制数:\n");
for(i=0; i<size4; i++)
printf("%d", s[i]);
printf("\n");
int y;
y = reverse(s);
printf("图灵机计算结果的十进制数:\n");
printf("%d", y);
printf("\n");
}
四、调试、测试及运行结果
测试结果:
输入3,输出8
经调试找出错误原因,错将extend()函数中数组[ ]中的i++写成++i导致程序运行出错。
运行结果:
输入9, 输出18
输入43, 输出86
五、总结
与之前几次作业相比本次实验应该是我写起来最费力的一次了。因为我是通过C语言编程来模拟实现图灵机(XN*2)求解输入值的倍数问题,所以有一些函数不能通过直接调用,而是需要自定义,这对于算法底子不牢的我来说真的是很困难了。就比如说对扩展二进制进行收缩操作,C++中就可以通过调用库函数来实现而C语言就不行,最终我还是通过查阅资料自定义了收缩函数解决了问题。实验中还有一些因为粗心犯下的错误,就比如我把i++写成了++i导致程序出错等等。虽然完成这次实验的过程很是艰难但我还是从中学到了很多,就比如这次实验需要通过定义许多数组来实现一些功能,使我巩固了数组的用法。ps:由于自身能力原因,代码实现的过程中可能会出现一些错误,希望大家能够批评指正,不吝赐教。