离散数学实验——求真值表.docx
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一实验目的 (1)
二实验内容 (1)
三实验环境 (1)
四实验原理和实现过程(算法描述) (1)
五实验数据及结果分析; (3)
六源程序清单; (5)
七其他收获和体会。
(14)
一实验目的
熟悉掌握命题逻辑中的联接词、真值表、主范式等,进一步能用
它们来解决实际问题。
二实验内容
1.从键盘输入两个命题变元P和Q的真值,求它们的合取、析取、条件和双条件的真值。
(A)
2.求任意一个命题公式的真值表(B,并根据真值表求主范式(C))
三实验环境
C或C++语言编程环境实现。
四实验原理和实现过程(算法描述)
A:首先提示用户输入真值指派,然后判断用户输入的是否是0或者1,如果不是则利用while语句提示错误,然后提示重新输入直至输入正确,再根据用户输入的真值给代表合取,析取,蕴含,双条件的变量赋值,再以两行表格形式输出所得结果。
最后提示按#键退出, 否则继续循环求真值。
B:
主要思路:首先提示用户输入表达式,然后编写并调用一个函数将表达式转换为逆波兰式,在转换的同时,插入部分语句将表达式中的变量名存储到数组bianl[N]中,然后输出存好的各变量名及用户输入的表达式(建立表头),将每次的真值指派存在数组zhi[]中,编写函数zzhi ()每次调用zzhi ()时都使数组zhi □中的真值加1, (利用递推实现加一时可能的进位,)然后编写并调用一函数qiuzhi ()计算每次真值指派下的逆波兰表达式的值,再输出各真值指派和求出的表达式的真值,然后调用函数zzhiO将真值指派的数组加1, 最后外围利用while 语句循环输出每个不同的真值指派和该指派下表达式的值。
将表达式转换成逆波兰式并将变量提取的算法:首先需要分配2 个栈,一个作为临时存储运算符的栈fu[], —个作为输入逆波兰式的栈nibol:],从中缀式的左端开始取字符,逐序进行如下步骤:(1)若取出的字符是字母,则该字母直接送入nibol[]栈。
同时为了找出所有变量,将该变量名与数组bianl[]中已有的元素比较, 如果bianl □中还没有该字母,则该字母是新出现的变量,将其录入数组bianl []中。
(2)若取出的字符是“(”,则直接送入fu□栈栈顶。
(3)若取出的字符是“)”,则将距离fu[]栈栈顶最近的“(” 之间的运算符,逐个出栈,依次送入nibol[]栈,此时抛弃“(”。
(4)若取出的字符是运算符,则将该运算符与fu□栈栈顶元素比较,如果该运算符优先级大于fu□栈栈顶运算符优先级,(此处是编写程序时自己判断好优先级了再按不同情况处理)则将该运算符进fu□栈,否者,将fu□栈的栈顶运算符弹岀,送入nibol[]栈中,直至fu □栈栈顶运算符低于(不包括等于)该运算符优先级,则将该运算符送入fu[]栈。
(5)重复上面的广4步,直至处理完所有的输入字符(6)最后将残留在符号栈fu□中的运算符依次出栈。
由于是用数组做的栈,所以,使用逆波兰式求值时只需直接从数组nibol []的笫一个元素nibol[0]开始依次读取就行了。
计算逆波兰式的值的算法:把转换好的逆波兰式从nibol [0]^ 始依次读取,如果是字母,则先判断字母是第几个变量,将数组zhi[] (存放了真值指派)中对应的真值zhi[i]放入数组result[](作为一个栈)中,遇到双目运算符就将result:]中栈顶的两个元素出栈, 执行运算,得到的结果再入栈,如果是单目运算符!,则只将一个元素岀栈并计算和入栈。
增值:用一个数组存放每一次的真值指派,并用递推实现真值的加1进位:
zzhi (int n)
//数组zhi存每次的真值指派,调用zzhi(bl+l);时给n赋值为变量数加1,以防止最后一个输出完时再增值时溢出
if (zhi[n]==O)
zhi[n]=l;
else if (zhi [n]=l)
{
zhi[n]=O;
zzhi (n-1) ; //递推,实现进位
}
}
C:在B的基础上加了两个二维数组biao[],biao2[],将真值为1 和0的真值指派分别存在数组biao [][]和biao2 [][]中,然后按合取和析取主范式的不同定义,用循环将对应真值的变量以原或反变量形式输出期间也输出合取、析取符和括号,使之成为主范式
再编写好主要的功能之后,我又在源程序基础上加了一些判断输
入错是的提示和处理。
具体见实验数据分析和源程序的注释
五实验数据及结果分析;
A:当输入的真值不为0或者1时提示错误并要求重新输入
可循环输入直到输入#才退出,如图所示,以表格的形式输出结果, 并且真值都正确
首先提示输入表达式(合取、析取、蕴含、双条件分别用&,!,>,= 表示),然后如果输入的是止确的表达式则以表格形式输出该表达式的真值表、主析取范式和主合取范式,如下图所示,所得真值表和主范式是正确的
当输入错误的表达式格式时,提示错误,并让用户选择是否继续输入表达式(按#退出)然后返回原界面,继续求真值表和主范式。
如下图所示:当输入P&Q!R(! R前漏写运算符)时,提示表达式不合规范,要求重新输入,也可选择按#退出。
括号不匹配:
输入错误的符号:
六源程序清单;
A
:
#include<stdio. h>
main()
char flag;
int he, xi, yunh, dengj, p,
q;
printf ("\t (本次实验计算二元表达式的值)\n\n");
do{
printf C请输入P, Q的真值指派(0或1) \n");
printf("p二");
scanf ("%d", &p);
wh订e(p!=O&&p!=l)
printf C输入有误!请重新输入!\np=");
fflush(stdin);
scanf ("%d", &p);
}
printfCQ^);
scanf ("%d", &q);
wh订e(q!=O&&q!=l)
{
printf C输入有误!请重新输入!\nQ=");
fflush(stdin);
scanf ("%d", &q);
}
he=(p==l&&q==l)?l:0;
xi=(p==l||q==l)?l:0;
yunh= (p==0 |q==1)?1:0;
dengj=(p==q)?l:O;
printf C\tP\tQ\tPAQ\tPVQ\tP-*Q\tP<—*Q\n/z);
printf (*\t%d\t%d \t%d \t%d \t%d \t %d*> p, q, he, xi, yunh, dengj);
printfC\n\t(按#退出,其它任意键继续)\『);
fflush(stdin);
scanf("%c", &flag);
}while(flag!=, #,);
B和C:
#include<stdio. h>
^include <stdlib. h>
#include<string. h>
#include<math. h>
^define N 20
char yuan[N*N]={* \0'}, fu[N]二{' \0'}, nibol [N*N] = {,\0'}, bianl [N];
int zhi[N] = {0);
//全局变暈分别存储原表达式yuan,求逆波兰式时的符号fu,
//存储逆波兰式nibol,存储变量bianl,存储真值指派zhi,
int nitop二T, ftop二T, bl二T ;
〃存储逆波兰式和求逆波兰式时要用到的存符号的数组的栈顶指针以及变量下标
int error0=0;
int biao[N*N] [N], geshu二0;//存储使真值为一的真值指派,用来求主析取范式int biao2[N*N][N], geshu2二0;
void mainO() ;//函数声明
nibolan () //将输入的表达式转换为逆波兰式
{
int i, j,k, flag, fg=-l;
//flag记录某变量是否已出现,fg记录上个变量是字母1,运算符号2,左括号0,右括号3 j=strlen(yuan);
for(i=0;i<j;i++)
//以下判断表达式中不同的符号分别采取不同的措施
if ((yuan[i]>=,A' &&yuan[i]〈二'Z') | | (yuan[i]>=,a' &&yuan[i]<=,z')) 〃字母作为运算数直接入栈
{
if(fg==l | fg==3) //字母前不能是字母或右括号
{printf (,z输入错误!表达式不规范请重新输入!z,); errorO=l; return;} fg=l;
nibol[++nitop]=yuan[i];
flag=l;
for(k=0;k<=bl;k++)
{
if(yuan[i]==bianl[k])
{flag=0;break;}
)
if(flag==l)
bianl[++bl]=yuan[i];
)
else if (yuan [i]==,C )//左括号直接入栈
{
f u [++f top]=yuan[i];
if(fg==l||fg==3)
{printf (,z输入错误!表达式不规范请重新输入!z,); errorO=l; return;} fg=0;
}
else if(yuan[i]==,)')
〃右括号将栈中已存的符号依次出栈,直到将最近的左括号出栈
{
if(fg!=l)//右括号前只能是字母
{printf (,z输入错误!表达式不规范请重新输入!z,); errorO=l; return;} fg=3;
while(fu[ftop] !=,(')
if(ftop==0)
{printfC输入错误,括号不匹配!请重新输入!
”);error0=1;return;}
nibol[++nitop]=fu[ftop--];
}
fu[ftop--];
}
〃以下直接判断各不同的符号,据优先级采取不同的措施
//优先级髙的直接入符号栈fu[],优先级低则将符号栈fu[]中符号
〃依次出栈送入逆波兰式nibol[]中,直至fu屮没有优先级更髙的再将本次的符号入栈
else if (yuan[i]==,!J)
{
if(fg==l |fg==3)//前不能是字母或右括号
{printf (/z输入错误!表达式不规范请重新输入! *); error0=l:return;}
fg=2;
while(fu[ftop]==,!,)
{
nibol[++nitop]=fu[ftop--];
)
fu[++ftop]=yuan[i ];
}
else if(yuan[i]二二'&')
{
if (fg==0 | fg==2) //双目运算符前不能是左括号或运算符
{printf (/z输入错误!表达式不规范请重新输入!;error0=l;return;}
fg=2;
while(fu[ftop]==,!* ||fu[ftop]==,&')
nibol[++nitop]=fu[ftop--];
f u[++ftop]=yuan[i];
}
else if(yuan[i]==, T)
{
if(fg==0||fg==2)
{printf (/z输入错误!表达式不规范请重新输入!;error0=l;return;} fg
二2;
wh订e(fu[ftop]===' !' | |fu[ftop]='&' I |fu[ftop]=T')
nibol[++nitop]=fu[ftop--];
f u[++ftop]=yuan[i];
}
else if(yuan[i]==,>*)
{
if(fg==0||fg==2)
{printf (,z输入错误!表达式不规范请重新输入!z,) ;errorO=l; return;}
fg=2;
wh 订e(fu[ftop]二二'!'| |fu[ftop]二二'&T |fu[ftop]二二'IT |fu[ftop]二二'>')
nibol[++nitop]=fu[ftop--];
f u[++ftop]=yuan[i];
)
else if (yuan[i]==,=,)
{
if(fg==0||fg==2)
{printf C输入错误!表达式不规范请重新输入! ,z) ;errorO=l;return;}
fg=2;
while(fu[ftop]==,!* ||fu[ftop]==,||fu[ftop]==,T ||fu[ftop]==,>* ||fu[ftop] = 二'=)
nibol[++nitop]=fu[ftop--];
f u[++ftop]=yuan[i];
}
else {printf (z/输入了错误的符号!请重新输入!*);errorO=l;return;}
〃如果输入错误的符号,则提示错误,
}
while(ftop>=0)
nibol[++nitop]=fu[ftop--];
}
zzhi(int n)//用数组zhi存每次的真值指派,调用时给n赋值为变量数加1,即zzhi(bl+1);
if(zhi[n]==0)
zhi[n]=l;
else if(zhi[n]==l)
{
zhi[n]二0;
zzhi (n-1): //递推
}
}
int qiuzhi ()
{
int i, j, result [N], res=-l, 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;
//下面根据逆波兰式屮各不同的运算符分别计算
else if(nibol[i]二二'&' ||nibol[i]二二'IT |nibol[i]二二'>'||nibol[i]二二'二
') {
a=result[res--];
b=result[res--];
if (nibol [i]==,&')
result[++res]=(a==l&&b==l)?l:0;
else if(nibol[i]二二T')
result[++res]二(a==l||b==l)?l:0;
else if(nibol[i]二二'>')
result[++res]二(b==0||a==l)?l:0;
else if(nibol[i]==,=,)
result[++res]=(a==b)?l:0;
}
else if(nibol[i]二二'!')
{ a=result[res--];
result[++res]=!a;
}
}
return result [res];//返回计算完毕时最终的结果
void mainOO 〃为实现循环输入将main函数提取成模块mainO
int i, j> m=0, nl, resultO;
printfC(本次实验输出表达式的真值表)\n请输入表达式:〃);
printfC合取、析取、蕴含、双条件分别用&,丨, >,=表示\『);
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二T;ftop二-1;bl=-l;error0=0;geshu=0;geshu2=0;
memset(biao, 0, sizeof(biao));
memset(biao2, 0, sizeof(biao2));
scanf ("%s", yuan); nibolanO ;
// system("cls");
if(errorO) return;
for(i=0;i<=bl;i++)
printf("\t%c", bianl[i]);
printf (,?\t%s\n", yuan);
nl=pow⑵ bl+1);
do
{
resultO=qiuzhi();
if(resultO==O)
{
for(i=l;i<=bl+l;i++)
{
printf(*\t%d*, zhi[i]); biao2[geshu2][i]=zhi[i];
}
geshu2++;
}
else
{
for(i=l;i<=bl+l;i++)
{
printf C\t%d*, zhi [i]); biao[geshu] [i]=zhi [i];
}
geshu++;
}
printf("\t %d\n", resultO);
zzhi (bl+1);
}while(++m<(int)nl);
printf C主析取范式为:\n");
for(i=0;i<geshu;i++)
{
for(j=l;j<=bl;j++)
{
if(biao[i][j]==l)
printf("%c/\", bianl[j-1]);
else
printf C'n bianl [j-1]);
if (biao[i] [j]==l)
printf bianl [j-1]);
else
printf (z/-i %c", bianl [j-1]);
if(i<geshu-l)
printf(“ V ”);
else printf("\n");
}
printf C主合取范式为:\n");
for(i=0;i<geshu2;i++)
{
printf(* (”);
for(j=l;j<=bl;j++)
{
if(biao2[i][j]==0)
printf (^cV*, bianl [j-1]);
else
printf (/z-i %cV*, bianl [j-1]); } if(biao2[i][j]==0)
printf bianl [j-1]);
else
printf (*-| %c", bianl [j-1]);
if(i<geshu2-l)
printf(*) A ”);
else printf (zz) \n");
main()
char ch;
while (1)
mainO();
printfC任意键继续,按#退岀“);
fflush(stdin);
scanf("%c",&ch);
system("CLS");
if (ch==,#') break:
}
}
七其他收获和体会。
本次试验收获颇丰,不仅加深了对离散数学的知识的理解,而且
积累了很多编写程序的经验,锻炼了动手能力和调试程序的能力。
写实验A的时候,为了实现提示错误和循环输入用了wh订c语句, 但是,最初运行的时候却出现了死循环,后来找了很久,才知道是要
清空缓存,调用了函数fflush (stdin)o
实验B,刚开始看到题冃不知道怎么下手,想起上课时老师说到过逆波兰式,然后去找了一些资料学习了逆波兰式的原理,再花了很多时间将所学的理论写成程序,期间遇到了很多问题,比如:如何给变量赋真值,怎样让真值以二进制的形式加一,怎样判断优先级…… 后来都想出了解决的办法:将真值存在数组中,求值时先用for循环找岀该变量的次序再直接替换了成对应的真值,用递推的办法实现二进制进位的问题,编程的时候手动判断优先级(可能是比较笨的办法) 然后采取相应的措施……
实验C:B编岀来之后,C就在B的基础上加了一些数组,将真值为1和0的真值指派分别存在不同的数组中,然后用循环输出主范式。
总而言之,这次实验尽管花费了很多时间,但是收获真的很大,尤其是经验的积累,对C语言又进一步地熟练了。