离散数学实验求真值表.docx
《离散数学实验求真值表.docx》由会员分享,可在线阅读,更多相关《离散数学实验求真值表.docx(19页珍藏版)》请在冰豆网上搜索。
![离散数学实验求真值表.docx](https://file1.bdocx.com/fileroot1/2023-2/9/29803707-d39f-4111-9aca-b5b167b22add/29803707-d39f-4111-9aca-b5b167b22add1.gif)
离散数学实验求真值表
一实验目的
熟悉掌握命题逻辑中的联接词、真值表、主范式等,进一步能用它们来解决实际问题。
二实验内容
1.从键盘输入两个命题变元P和Q的真值,求它们的合取、析取、条件和双条件的真值。
(A)
2.求任意一个命题公式的真值表(B,并根据真值表求主范式(C))
三实验环境
C或C++语言编程环境实现。
四实验原理和实现过程(算法描述)
A:
首先提示用户输入真值指派,然后判断用户输入的是否是0或者1,如果不是则利用while语句提示错误,然后提示重新输入直至输入正确,再根据用户输入的真值给代表合取,析取,蕴含,双条件的变量赋值,再以两行表格形式输出所得结果。
最后提示按#键退出,否则继续循环求真值。
B:
主要思路:
首先提示用户输入表达式,然后编写并调用一个函数将表达式转换为逆波兰式,在转换的同时,插入部分语句将表达式中的变量名存储到数组bianl[N]中,然后输出存好的各变量名及用户输入的表达式(建立表头),将每次的真值指派存在数组zhi[]中,编写函数zzhi()每次调用zzhi()时都使数组zhi[]中的真值加1,(利用递推实现加一时可能的进位,)然后编写并调用一函数qiuzhi()计算每次真值指派下的逆波兰表达式的值,再输出各真值指派和求出的表达式的真值,然后调用函数zzhi()将真值指派的数组加1,最后外围利用while语句循环输出每个不同的真值指派和该指派下表达式的值。
将表达式转换成逆波兰式并将变量提取的算法:
首先需要分配2个栈,一个作为临时存储运算符的栈fu[],一个作为输入逆波兰式的栈nibol[],从中缀式的左端开始取字符,逐序进行如下步骤:
(1)若取出的字符是字母,则该字母直接送入nibol[]栈。
同时为了找出所有变量,将该变量名与数组bianl[]中已有的元素比较,如果bianl[]中还没有该字母,则该字母是新出现的变量,将其录入数组bianl[]中。
(2)若取出的字符是“(”,则直接送入fu[]栈栈顶。
(3)若取出的字符是“)”,则将距离fu[]栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入nibol[]栈,此时抛弃“(”。
(4)若取出的字符是运算符,则将该运算符与fu[]栈栈顶元素比较,如果该运算符优先级大于fu[]栈栈顶运算符优先级,(此处是编写程序时自己判断好优先级了再按不同情况处理)则将该运算符进fu[]栈,否者,将fu[]栈的栈顶运算符弹出,送入nibol[]栈中,直至fu[]栈栈顶运算符低于(不包括等于)该运算符优先级,则将该运算符送入fu[]栈。
(5)重复上面的1~4步,直至处理完所有的输入字符
(6)最后将残留在符号栈fu[]中的运算符依次出栈。
由于是用数组做的栈,所以,使用逆波兰式求值时只需直接从数组nibol[]的第一个元素nibol[0]开始依次读取就行了。
计算逆波兰式的值的算法:
把转换好的逆波兰式从nibol[0]开始依次读取,如果是字母,则先判断字母是第几个变量,将数组zhi[](存放了真值指派)中对应的真值zhi[i]放入数组result[](作为一个栈)中,遇到双目运算符就将result[]中栈顶的两个元素出栈,执行运算,得到的结果再入栈,如果是单目运算符!
,则只将一个元素出栈并计算和入栈。
增值:
用一个数组存放每一次的真值指派,并用递推实现真值的加1进位:
zzhi(intn)
//数组zhi存每次的真值指派,调用zzhi(bl+1);时给n赋值为变量数加1,以防止最后一个输出完时再增值时溢出
{
if(zhi[n]==0)
zhi[n]=1;
elseif(zhi[n]==1)
{
zhi[n]=0;
zzhi(n-1);//递推,实现进位
}
}
C:
在B的基础上加了两个二维数组biao[],biao2[],将真值为1和0的真值指派分别存在数组biao[][]和biao2[][]中,然后按合取和析取主范式的不同定义,用循环将对应真值的变量以原或反变量形式输出期间也输出合取、析取符和括号,使之成为主范式
再编写好主要的功能之后,我又在源程序基础上加了一些判断输入错是的提示和处理。
具体见实验数据分析和源程序的注释
五实验数据及结果分析;
A:
当输入的真值不为0或者1时提示错误并要求重新输入
可循环输入直到输入#才退出,如图所示,以表格的形式输出结果,并且真值都正确
B和C:
首先提示输入表达式(合取、析取、蕴含、双条件分别用&,|,>,=表示),然后如果输入的是正确的表达式则以表格形式输出该表达式的真值表、主析取范式和主合取范式,如下图所示,所得真值表和主范式是正确的
当输入错误的表达式格式时,提示错误,并让用户选择是否继续输入表达式(按#退出)然后返回原界面,继续求真值表和主范式。
如下图所示:
当输入p&Q!
R(!
R前漏写运算符)时,提示表达式不合规范,要求重新输入,也可选择按#退出。
括号不匹配:
输入错误的符号:
六源程序清单;
A:
#include
main()
{
charflag;
inthe,xi,yunh,dengj,p,q;
printf("***************欢迎进入***************\n");
printf("\t(本次实验计算二元表达式的值)\n\n");
do{
printf("请输入P,Q的真值指派(0或1)\n");
printf("p=");
scanf("%d",&p);
while(p!
=0&&p!
=1)
{
printf("输入有误!
请重新输入!
\np=");
fflush(stdin);
scanf("%d",&p);
}
printf("Q=");
scanf("%d",&q);
while(q!
=0&&q!
=1)
{
printf("输入有误!
请重新输入!
\nQ=");
fflush(stdin);
scanf("%d",&q);
}
he=(p==1&&q==1)?
1:
0;
xi=(p==1||q==1)?
1:
0;
yunh=(p==0||q==1)?
1:
0;
dengj=(p==q)?
1:
0;
printf("\tP\tQ\tP∧Q\tP∨Q\tP→Q\tP←→Q\n");
printf("\t%d\t%d\t%d\t%d\t%d\t%d",p,q,he,xi,yunh,dengj);
printf("\n\t(按#退出,其它任意键继续)\n");
fflush(stdin);
scanf("%c",&flag);
}while(flag!
='#');
}
B和C:
#include
#include
#include
#include
#defineN20
charyuan[N*N]={'\0'},fu[N]={'\0'},nibol[N*N]={'\0'},bianl[N];
intzhi[N]={0};
//全局变量分别存储原表达式yuan,求逆波兰式时的符号fu,
//存储逆波兰式nibol,存储变量bianl,存储真值指派zhi,
intnitop=-1,ftop=-1,bl=-1;
//存储逆波兰式和求逆波兰式时要用到的存符号的数组的栈顶指针以及变量下标
interror0=0;
intbiao[N*N][N],geshu=0;//存储使真值为一的真值指派,用来求主析取范式
intbiao2[N*N][N],geshu2=0;
voidmain0();//函数声明
nibolan()//将输入的表达式转换为逆波兰式
{
inti,j,k,flag,fg=-1;
//flag记录某变量是否已出现,fg记录上个变量是字母1,运算符号2,左括号0,右括号3
j=strlen(yuan);
for(i=0;i{
//以下判断表达式中不同的符号分别采取不同的措施
if((yuan[i]>='A'&&yuan[i]<='Z')||(yuan[i]>='a'&&yuan[i]<='z'))
//字母作为运算数直接入栈
{
if(fg==1||fg==3)//字母前不能是字母或右括号
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=1;
nibol[++nitop]=yuan[i];
flag=1;
for(k=0;k<=bl;k++)
{
if(yuan[i]==bianl[k])
{flag=0;break;}
}
if(flag==1)
bianl[++bl]=yuan[i];
}
elseif(yuan[i]=='(')//左括号直接入栈
{
fu[++ftop]=yuan[i];
if(fg==1||fg==3)
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=0;
}
elseif(yuan[i]==')')
//右括号将栈中已存的符号依次出栈,直到将最近的左括号出栈
{
if(fg!
=1)//右括号前只能是字母
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=3;
while(fu[ftop]!
='(')
{
if(ftop==0)
{printf("输入错误,括号不匹配!
请重新输入!
");error0=1;return;}
nibol[++nitop]=fu[ftop--];
}
fu[ftop--];
}
//以下直接判断各不同的符号,据优先级采取不同的措施
//优先级高的直接入符号栈fu[],优先级低则将符号栈fu[]中符号
//依次出栈送入逆波兰式nibol[]中,直至fu中没有优先级更高的再将本次的符号入栈
elseif(yuan[i]=='!
')
{
if(fg==1||fg==3)//前不能是字母或右括号
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=2;
while(fu[ftop]=='!
')
{
nibol[++nitop]=fu[ftop--];
}
fu[++ftop]=yuan[i];
}
elseif(yuan[i]=='&')
{
if(fg==0||fg==2)//双目运算符前不能是左括号或运算符
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=2;
while(fu[ftop]=='!
'||fu[ftop]=='&')
nibol[++nitop]=fu[ftop--];
fu[++ftop]=yuan[i];
}
elseif(yuan[i]=='|')
{
if(fg==0||fg==2)
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=2;
while(fu[ftop]=='!
'||fu[ftop]=='&'||fu[ftop]=='|')
nibol[++nitop]=fu[ftop--];
fu[++ftop]=yuan[i];
}
elseif(yuan[i]=='>')
{
if(fg==0||fg==2)
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=2;
while(fu[ftop]=='!
'||fu[ftop]=='&'||fu[ftop]=='|'||fu[ftop]=='>')
nibol[++nitop]=fu[ftop--];
fu[++ftop]=yuan[i];
}
elseif(yuan[i]=='=')
{
if(fg==0||fg==2)
{printf("输入错误!
表达式不规范请重新输入!
");error0=1;return;}
fg=2;
while(fu[ftop]=='!
'||fu[ftop]=='&'||fu[ftop]=='|'||fu[ftop]=='>'||fu[ftop]=='=')
nibol[++nitop]=fu[ftop--];
fu[++ftop]=yuan[i];
}
else{printf("输入了错误的符号!
请重新输入!
");error0=1;return;}
//如果输入错误的符号,则提示错误,
}
while(ftop>=0)
nibol[++nitop]=fu[ftop--];
}
zzhi(intn)//用数组zhi存每次的真值指派,调用时给n赋值为变量数加1,即zzhi(bl+1);
{
if(zhi[n]==0)
zhi[n]=1;
elseif(zhi[n]==1)
{
zhi[n]=0;
zzhi(n-1);//递推
}
}
intqiuzhi()
{
inti,j,result[N],res=-1,a,b;
for(i=0;i<=nitop;i++)
{
if((nibol[i]>='A'&&nibol[i]<='Z')||(nibol[i]>='a'&&nibol[i]<='z'))
{
for(j=0;j<=bl;j++)
//用循环找到逆波兰式中的变量在数组bianl[N]中的位置,
//并将存放真值的数组中相应的真值直接代替该变量存入用来计算的数组中
{
if(nibol[i]==bianl[j])
{
result[++res]=zhi[j+1];
break;
}
}
}
//下面根据逆波兰式中各不同的运算符分别计算
elseif(nibol[i]=='&'||nibol[i]=='|'||nibol[i]=='>'||nibol[i]=='=')
{
a=result[res--];
b=result[res--];
if(nibol[i]=='&')
result[++res]=(a==1&&b==1)?
1:
0;
elseif(nibol[i]=='|')
result[++res]=(a==1||b==1)?
1:
0;
elseif(nibol[i]=='>')
result[++res]=(b==0||a==1)?
1:
0;
elseif(nibol[i]=='=')
result[++res]=(a==b)?
1:
0;
}
elseif(nibol[i]=='!
')
{a=result[res--];
result[++res]=!
a;
}
}
returnresult[res];//返回计算完毕时最终的结果
}
voidmain0()//为实现循环输入将main函数提取成模块main0
{
inti,j,m=0,n1,result0;
printf("************欢迎进入************\n");
printf("(本次实验输出表达式的真值表)\n请输入表达式:
");
printf("合取、析取、蕴含、双条件分别用&,|,>,=表示\n");
memset(yuan,0,sizeof(yuan));
memset(fu,0,sizeof(fu));
memset(nibol,0,sizeof(nibol));
memset(bianl,0,sizeof(bianl));
memset(zhi,0,sizeof(zhi));
nitop=-1;ftop=-1;bl=-1;error0=0;geshu=0;geshu2=0;
memset(biao,0,sizeof(biao));
memset(biao2,0,sizeof(biao2));
scanf("%s",yuan);
nibolan();
//system("cls");
if(error0)return;
for(i=0;i<=bl;i++)
printf("\t%c",bianl[i]);
printf("\t%s\n",yuan);
n1=pow(2,bl+1);
do
{
result0=qiuzhi();
if(result0==0)
{
for(i=1;i<=bl+1;i++)
{
printf("\t%d",zhi[i]);
biao2[geshu2][i]=zhi[i];
}
geshu2++;
}
else
{
for(i=1;i<=bl+1;i++)
{
printf("\t%d",zhi[i]);
biao[geshu][i]=zhi[i];
}
geshu++;
}
printf("\t%d\n",result0);
zzhi(bl+1);
}while(++m<(int)n1);
printf("主析取范式为:
\n");
for(i=0;i{
for(j=1;j<=bl;j++)
{
if(biao[i][j]==1)
printf("%c∧",bianl[j-1]);
else
printf("┐%c∧",bianl[j-1]);
}
if(biao[i][j]==1)
printf("%c",bianl[j-1]);
else
printf("┐%c",bianl[j-1]);
if(iprintf("∨");
elseprintf("\n");
}
printf("主合取范式为:
\n");
for(i=0;i{
printf("(");
for(j=1;j<=bl;j++)
{
if(biao2[i][j]==0)
printf("%c∨",bianl[j-1]);
else
printf("┐%c∨",bianl[j-1]);
}
if(biao2[i][j]==0)
printf("%c",bianl[j-1]);
else
printf("┐%c",bianl[j-1]);
if(iprintf(")∧");
elseprintf(")\n");
}
}
main()
{
charch;
while
(1)
{
main0();
printf("任意键继续,按#退出");
fflush(stdin);
scanf("%c",&ch);
system("CLS");
if(ch=='#')break;
}
}
七其他收获和体会。
本次试验收获颇丰,不仅加深了对离散数学的知识的理解,而且积累了很多编写程序的经验,锻炼了动手能力和调试程序的能力。
写实验A的时候,为了实现提示错误和循环输入用了while语句,但是,最初运行的时候却出现了死循环,后来找了很久,才知道是要清空缓存,调用了函数fflush(stdin)。
实验B,刚开始看到题目不知道怎么下手,想起上课时老师说到过逆波兰式,然后去找了一些资料学习了逆波兰式的原理,再花了很多时间将所学的理论写成程序,期间遇到了很多问题,比如:
如何给变量赋真值,怎样让真值以二进制的形式加一,怎样判断优先级……后来都想出了解决的办法:
将真值存在数组中,求值时先用for循环找出该变量的次序再直接替换了成对应的真值,用递推的办法实现二进制进位的问题,编程的时候手动判断优先级(可能是比较笨的办法)然后采取相应的措施……
实验C:
B编出来之后,C就在B的基础上加了一些数组,将真值为1和0的真值指派分别存在不同的数组中,然后用循环输出主范式。
总而言之,这次实验尽管花费了很多时间,但是收获真的很大,尤其是经验的积累,对c语言又进一步地熟练了。