南京工程学院实 验 报 告
课程名称 离散数学
项目名称 命题逻辑
实验学生班级 K多媒体111
实验学生姓名 朱在吉
学 号 240111338
同组学生姓名
实验时间 2012.10.25
实验地点
实验成绩评定
指导教师签字 年 月 日
一、实验目的和要求
真值表是命题逻辑中的一个十分重要的概念,利用它几乎可以解决命题逻辑中的所有问题。例如,利用命题公式的真值表,可以判断命题公式的类型、求命题公式的主范式、判断两命题公式是否等价,还可以进行推理等。
本实验通过编写一个程序,让计算机给出命题公式的真值表,并在此基础上进行命题公式类型的判定、求命题公式的主范式等。目的是让学生更加深刻地理解真值表的概念,并掌握真值表的求解方法及其在解决命题逻辑中其他问题中的应用。
二、实验主要仪器和设备
计算机
使用visual C++6.0为编程软件,采用C语言为编程语言实现三、实验方法与步骤(需求分析、算法设计思路、流程图等)
. 求任意一个命题公式的真值表(B,并根据真值表求主范式(C))
详细说明:
1. 逻辑联接词的运算
本实验要求大家利用C/C++语言,实现二元合取、析取、条件和双条件表达式的计算。充分利用联接词和逻辑运算符之间的相似性实现程序功能。
2. 求任意一个命题公式的真值表
本实验要求大家利用C/C++语言,实现任意输入公式的真值表计算。一般我们将公式中的命题变元放在真值表的左边,将公式的结果放在真值表的右边。命题变元可用数值变量表示,合适公式的表示及求真值表转化为逻辑运算结果;可用一维数表示合式公式中所出现的n个命题变元,同时它也是一个二进制加法器的模拟器,每当在这个模拟器中产生一个二进制数时,就相当于给各个命题变元产生了一组真值指派。算法逻辑如下:
(1)将二进制加法模拟器赋初值0
(2)计算模拟器中所对应的一组真值指派下合式公式的真值。
(3)输出真值表中对应于模拟器所给出的一组真值指派及这组真值指派所对应的一行真值。
(4)产生下一个二进制数值,若该数值等于2n-1,则结束,否则转(2)。
2.实验过程
B,C题部分,首先是输入一个合理的式子,然后从式子中查找出变量的个数,开辟一个二进制函数,用来生成真值表,然后用函数运算,输出结果,并根据结果归类给范式,最后输出范式。
函数部分,主要是3个函数,一个为真值表递加函数,通过二进制的加法原理递进产生,一个为分级运算函数,这个函数是通过判断括号,选出最内级括号的内容执行运算函数,这样一级一级向外运算,最后得出最终结果,剩下一个为主运算函数,按照运算符号的优先级按顺序进行运算,如先将所有非运算运算完,再执行与运算。如此运算。
四、实验原始纪录(源程序、数据结构等)
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "conio.h"
#include "math.h"
#define N 50
void panduan(int b[N],int f);//赋值函数
int tkh (char sz[N], char ccu[N], int icu[N], int h0);//分级运算函数
int fkh (char sz[N], char ccu[N], int icu[N], int h0);//主运算函数
main()
{
int i1,i2,d=1,icu[N],kh=0,jg,j=0,h0;//icu[N]用于存放变量值,kh括号计数,jg存放结果
int bj=0,hq[N],h=0,x=0,xq[N];//hq[N]存放合取结果xq[N]存放析取结果
char sz[N],ccu[N],sz0[N],s;//sz[N]存放式子,ccu[N]存放变量,sz0[N]也是用于存放式子
hq[0]=-1;
xq[0]=-1;
printf("***************************************\n");//标语
printf("** **\n");
printf("** 欢迎进入逻辑运算软件 **\n");
printf("** (可运算真值表,主范式,支持括号) **\n");
printf("** **\n");
printf("** 用!表示非 **\n");
printf("** 用&表示与 **\n");
printf("** 用|表示或 **\n");
printf("** 用^表示蕴含 **\n");
printf("** 用~表示等值 **\n");
printf("** **\n");
printf("***************************************\n\n");
printf("请输入一个合法的命题公式:\n");//输入式子
gets(sz);//读取式子
strcpy(sz0,sz);//复制式子
for(i1=0;i1<strlen(sz);i1++)
{
if(sz[i1]==')' || sz[i1]=='(')//存储括号数量
kh++;
if(sz[i1]>='a' && sz[i1]<='z' || sz[i1]>='A' && sz[i1]<='Z')
{
for(i2=0;i2<j;i2++) //判断并储存变量。
if(ccu[i2]==sz[i1])//去除重复变量
d=0;
if(d==1)
{
ccu[j]=sz[i1];
j++;
}
d=1;
}
}
printf("\nd该式子中的变量个数为:%d\n",j);//输出变量个数
h0=j;
printf("\n输出真值表如下:\n \n"); //输出真值表表头
for(i1=0;i1<h0;i1++)
printf(" %c ",ccu[i1]);
printf(" ");
puts(sz);
printf("\n");
for(i1=0;i1<j;i1++) ///先将所有的变量赋值为零。
icu[i1]=0;
for(i2=0;i2<j;i2++)//输出真值表前项
printf(" %d ",icu[i2]);
jg=tkh(sz,ccu,icu,h0); //用函数求结果
if(jg==0)//结果为0,合取加1
hq[h++]=bj;
else //否则,析取加1
xq[x++]=bj;
printf(" %d\n",jg);//输出运算结果
strcpy(sz,sz0);
for(i1=0;i1<(int)pow(2,j)-1;i1++)
{
++bj;
panduan(icu,j-1); //赋值变量
jg=tkh(sz,ccu,icu,h0);
if(jg==0)//结果为0,合取加1
hq[h++]=bj;
else //否则,析取加1
xq[x++]=bj;
strcpy(sz,sz0); //恢复被修改的数组。
for(i2=0;i2<j;i2++)
printf(" %d ",icu[i2]);//输出真值表前项
printf(" %d\n",jg);//输出运算结果
}
if(hq[0]==-1)//不存在合取范式时
printf("\n该命题公式不存在主合取范式。\n");
else
{
printf("\n该命题公式的主合取范式:\n\t");
for(i1=0;i1<h;i1++)
{
if (i1>0)//判断并添加符号
printf("\\/");
printf("M(%d)",hq[i1]); //输出主合取范式
}
}
if(xq[0]==-1)//不存在析取范式时
printf("\n该命题公式不存在主析取范式。\n");
else
{
printf("\n\n该命题公式的主析取范式:\n\t");
for(i1=0;i1<x;i1++)
{
if (i1>0)//判断并添加符号
printf("/\\");
printf("m(%d)",xq[i1]);//输出主析取范式
}
}
printf("\n");
printf("\n欢迎下次再次使用!\n ");//结束
getch();
}
void panduan(int b[N],int f) // 二进制赋值。
{
int i;
i=f;
if(b[f]==0)//加1
b[f]=1;
else//进位
{
b[f]=0;
panduan(b,--i);
}
}
int tkh (char sz[N],char ccu[N],int icu[N],int h0)//分级运算函数
{
int i,j,h,s,kh=0,wz[N],a;
char xs1[N],ckh[N]; //xs1用来保存括号内的字符 ckh用来保存括号。
s=strlen(sz);
for(i=0;i<s;i++)
if(sz[i]=='(' || sz[i]==')')//判断括号
{
wz[kh]=i;//存储括号位置
ckh[kh]=sz[i];//存储括号类型
kh++;
}
if(kh==0)
return fkh(sz,ccu,icu,h0);//如果无括号,直接运行
else
{
for(i=0;i<kh;i++)
if(ckh[i]==')')//找到第一个)
break;
for(j=wz[i-1]+1,h=0;j<wz[i];j++,h++) //存储最内级括号中的内容
xs1[h]=sz[j];
xs1[h]='\0';
a=fkh(xs1,ccu,icu,h0);//运行最内级括号的式子,得到结果
if(a==1)//判断并存储结果
sz[wz[i-1]]=1;
else
sz[wz[i-1]]=-2;
for(j=wz[i-1]+1;j<s+wz[i-1]-wz[i];j++)//将括号后内容前移
sz[j]=sz[j+wz[i]-wz[i-1]];
sz[j]='\0';
return tkh(sz,ccu,icu,h0);//循环执行
}
}
int fkh(char sz[N],char ccu[N],int icu[N],int h0)//主运算函数
{
int i,h=0,j=0,j1=0,j2=0,j3=0,j4=0,j5=0,i1,i2,p1=-1,p2=-1,s;
char dt[N];
s=strlen(sz);
if(s==1)
if(sz[0]==-2)//判断是否是最后一项
return 0;
else
return 1; //1 就是sz[0]的值、
else
{
for(i=0;i<s-j;i++) //先处理非
if(sz[i]=='!')
{
for(i1=0;i1<h0;i1++)
if(sz[i+1]==ccu[i1])//将变量赋值并给P1
p1=icu[i1];
if(sz[i+1]==-2)//如果是前运算结果的0,则P1等于0
p1=0;
if(p1==-1)//如果是数字,直接给P1
p1=sz[i+1];
dt[j+2]=!p1;//非运算
sz[i]=j+2;
j++;
p1=0;
for(i1=i+1;i1<s-j;i1++)
sz[i1]=sz[i1+1];//将后续式子前移一项
}
p1=-1;
j1=j;
for(i=0;i<s-j1-2*j2;i++) // 处理与
if(sz[i]=='&')
{
for(i1=0;i1<h0;i1++)
{
if(sz[i-1]==ccu[i1])//将变量赋值并给P1
p1=icu[i1];
if(sz[i+1]==ccu[i1])//将变量赋值并给P2
p2=icu[i1];
}
for(i2=2;i2<j+2;i2++)
{
if(sz[i-1]==i2) //如果为前计算结果,将结果赋值并给P1
p1=dt[i2];
if(sz[i+1]==i2) //如果为前计算结果,将结果赋值并给P2
p2=dt[i2];
}
if(sz[i-1]==-2)//如果是前运算结果的0,则P1等于0
p1=0;
if(sz[i+1]==-2)//如果是前运算结果的0,则P2等于0
p2=0;
if(p1==-1) //如果是数字,直接给P1
p1=(int)(sz[i-1]);
if(p2==-1)//如果是数字,直接给P2
p2=(int)(sz[i+1]);
dt[j+2]=p1 && p2;//与运算
sz[i-1]=j+2;
j++;
j2++;
p1=-1;
p2=-1;
for(i1=i;i1<s-j1-2*j2;i1++)//将后续式子前移两项
sz[i1]=sz[i1+2];
i=i-1;
}
for(i=0;i<s-j1-2*j2-2*j3;i++) // 处理或。
if(sz[i]=='|')
{
for(i1=0;i1<h0;i1++)
{
if(sz[i-1]==ccu[i1])//将变量赋值并给P1
p1=icu[i1];
if(sz[i+1]==ccu[i1])//将变量赋值并给P2
p2=icu[i1];
}
for(i2=2;i2<j+2;i2++)
{
if(sz[i-1]==i2) //如果为前计算结果,将结果赋值并给P1
p1=dt[i2];
if(sz[i+1]==i2)//如果为前计算结果,将结果赋值并给P2
p2=dt[i2];
}
if(sz[i-1]==-2)//如果是前运算结果的0,则P1等于0
p1=0;
if(sz[i+1]==-2)//如果是前运算结果的0,则P2等于0
p2=0;
if(p1==-1)//如果是数字,直接给P1
p1=sz[i-1];
if(p2==-1)//如果是数字,直接给P2
p2=sz[i+1];
dt[j+2]=p1 || p2;//或运算
sz[i-1]=j+2;
j++;
j3++;
p1=-1;
p2=-1;
for(i1=i;i1<s-j1-2*j2-2*j3;i1++)//将后续式子前移两项
sz[i1]=sz[i1+2];
i--;
}
for(i=0;i<s-j1-2*j2-2*j3-2*j4;i++) // 处理蕴含。
if(sz[i]=='^')
{
for(i1=0;i1<h0;i1++)
{
if(sz[i-1]==ccu[i1])//将变量赋值并给P1
p1=icu[i1];
if(sz[i+1]==ccu[i1])//将变量赋值并给P2
p2=icu[i1];
}
for(i2=2;i2<j+2;i2++)
{
if(sz[i-1]==i2) //如果为前计算结果,将结果赋值并给P1
p1=dt[i2];
if(sz[i+1]==i2) //如果为前计算结果,将结果赋值并给P2
p2=dt[i2];
}
if(sz[i-1]==-2)//如果是前运算结果的0,则P1等于0
p1=0;
if(sz[i+1]==-2)//如果是前运算结果的0,则P2等于0
p2=0;
if(p1==-1)//如果是数字,直接给P1
p1=sz[i-1];
if(p2==-1)//如果是数字,直接给P2
p2=sz[i+1];
dt[j+2]=!p1 || p2;//蕴含运算
sz[i-1]=j+2;
j++;
j4++;
p1=-1;
p2=-1;
for(i1=i;i1<s-j1-2*j2-2*j3-2*j4;i1++)//将后续式子前移两项
sz[i1]=sz[i1+2];
i--;
}
for(i=0;i<s-j1-2*j2-2*j3-2*j4-2*j5;i++) // 处理等值。
if(sz[i]=='~')
{
for(i1=0;i1<h0;i1++)
{
if(sz[i-1]==ccu[i1])//将变量赋值并给P1
p1=icu[i1];
if(sz[i+1]==ccu[i1])//将变量赋值并给P2
p2=icu[i1];
}
for(i2=2;i2<j+2;i2++)
{
if(sz[i-1]==i2) //如果为前计算结果,将结果赋值并给P1
p1=dt[i2];
if(sz[i+1]==i2) //如果为前计算结果,将结果赋值并给P2
p2=dt[i2];
}
if(sz[i-1]==-2)//如果是前运算结果的0,则P1等于0
p1=0;
if(sz[i+1]==-2)//如果是前运算结果的0,则P2等于0
p2=0;
if(p1==-1)//如果是数字,直接给P1
p1=sz[i-1];
if(p2==-1)//如果是数字,直接给P2
p2=sz[i+1];
dt[j+2]=(!p1 || p2)&&(!p2 || p1);//等值运算
sz[i-1]=j+2;
j++;
j5++;
p1=-1;
p2=-1;
for(i1=i;i1<s-j1-2*j2-2*j3-2*j4-2*j5;i1++)//将后续式子前移两项
sz[i1]=sz[i1+2];
i--;
}
return dt[j+1];//返回结果
}
}
五.实验数据及结果分析;
欢迎界面
非运算
与运算
或运算
蕴含运算
等值运算
综合运算
带括号的综合运算
结果分析:
由于本身关系比较密切,故我直接将两个做在了一起,这个程序从功能上来说,达到了题目要求的各个功能,可以运算与,或,非,蕴含,等值条件组成的表达式,并且支持括号运算,所以,总体上来说,虽然还有一些不完善的部分,但是整体是比较可以的。
六、实验总结与思考
C类题如果是在B类题的基础上构建,相对来说也是一个不困难的题,所以主要在B类题上花费了大量时间,一开始想用中序表达式转变为逆波兰式的方法来解决,但是总有一些零零散散的问题困扰,况且对于栈和二叉树之类的东西在学习C语言时接触很少,所以困难更大,经过多次失败后,还是最后用最简单同时也是最麻烦的方法来解决这个问题。利用函数一步步来解决,所以在个别功能上来说有一些不完美,比如纠错,因为时间有限,有个别地方没有设置出错提示。
也许是很久没看C语言了,导致很多基本的语句都已经并不熟练了,通过这次实验,让我从一定程度上找回了一点以前做课程设计时的感觉。在编程过程中,最重要的其实是对细节方面的把握。
B类题,在设计上主要有以下几个问题:
1.优先级的问题,要如何解决优先级的问题,最后我直接用先后执行来解决。
2.括号问题,对于运算来说,解决括号问题很重要,而我是从最内级括号向外层层运算,最后得出结果。
3.真值表问题,真值表利用二进制的原理,递加得到。
4.赋值问题,一开始这个问题困扰了我很久,后来发现直接用一个赋值函数,每次运行都直接运行一次就可以很好的解决了。
这些是在做B类题时主要遇到的问题。
C类题,其实这个题在B类题得出结果之后可以比较简单的解决,因为已经有真值表,只要将最后结果分类,并将其分类列出就可以了。
总的来说,本次实验总体上是比较成功的,也让我学到了很多东西。