题目要求
对给出的任意一个合式公式(不超过四个命题变元),用程序编程表示出来,能够计算它在各组真值指派下所应有的真值,画出其真值表,并输出构成主析取范式的所有小项。涉及的运算符包括∧(&),∨(|),¬(!),→(−>),↔(<−>)五种运算符,对于无法由键盘输入的运算符,可以使用括号内的符号代替。例如输入:
(?⟶?)∨?
难度:较难
说明:
a) 能支持1-4个变元
b) 能支持1-4次逻辑运算
c) 人机交互友好,排版美观
IDE:Microsoft Visual C++ 6.0
代码:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#define MAXSIZE 40
typedef char elemtype;
char str[40],st[40],st1[40],st2[40];
int table1[16][5];
int flag=1;
/*str接收输入的字符,st是转化后的字符,
st1储存变量,st2变量转化为相应公式;
table1储存真值表*/
/*
数据结构自己学的,然后写后缀表达式时一直提示一些符号没有
然后我猜想是头文件没有,但是网上找了好多,都不对
于是我就对着买的书,把这些定义全部打上了。
*/
typedef struct // 定义栈结构
{ elemtype date[MAXSIZE];
int top;
}SqStack;
void InitStack(SqStack *&s) //初始化栈
{
s=(SqStack * ) malloc(sizeof(SqStack));
s->top=-1;
}
void DestoryStack(SqStack *&s) //销毁栈
{
free(s);
}
bool StackEmpty(SqStack *s) //判断栈是否为空
{
return(s->top==-1);
}
bool Push(SqStack *&s, elemtype e) //入栈
{
if(s->top==MAXSIZE-1)
return false;
s->top++;
s->date[s->top]=e;
return true;
}
bool Pop(SqStack *&s,elemtype &e) //出栈
{
if(s->top==-1)
return false;
e=s->date[s->top];
s->top--;
return true;
}
bool GetTop(SqStack *s,elemtype &e) //取栈顶元素
{
if(s->top==-1)
return false;
e=s->date[s->top];
return true;
}
int calFun(char st2[]) // 计算后缀表达式
{
SqStack *S; //定义栈
int i=0;
char x,x1,x2;
InitStack(S); //初始化栈
while (st2[i]!='\0')
{
switch(st2[i])
{
case'&': //计算析取
Pop(S, x2);
Pop(S, x1);
x=(x1-'0')&&(x2-'0');
Push(S,(x+'0'));
i++; break;
case'|': //计算合取
Pop(S, x2);
Pop(S, x1);
x=(x1-'0')||(x2-'0');
Push(S,(x+'0'));
i++; break;
case'-': //计算条件
Pop(S, x2);
Pop(S, x1);
x=!(x1-'0')||(x2-'0');
Push(S,(x+'0'));
i++; break;
case'=': //计算双条件
Pop(S, x2);
Pop(S, x1);
x=(x1-'0')==(x2-'0');
Push(S,(x+'0'));
i++; break;
case'~': //计算否定
Pop(S,x1);
x=!(x1-'0');
Push(S,(x+'0'));
i++; break;
default:
while(st2[i]=='1'||st2[i]=='0')
{
Push(S,st2[i]);
i++;
}
}
}
GetTop(S,x); //取栈顶元素,即合式公式真值
DestoryStack(S); //销毁栈
return (int)(x-'0'); //返回真值
}
void suffix(char *z,char st2[]) // 将中缀表达式转化为后缀表达式
{
char e;
SqStack *Optr; //定义栈
InitStack(Optr); //初始化栈
int i=0;
while(*z!='\0')
{
switch(*z)
{
case'(': //左括号
Push(Optr,'(');
z++;
break;
case')': //右括号
Pop(Optr,e);
while(e!='(')
{
st2[i++]=e;
Pop(Optr,e);
}
z++;
break;
case'-': //条件和双条件
case'=':
while(!StackEmpty(Optr))
{
GetTop(Optr,e);
if(e!='(')
{
st2[i++]=e;
Pop(Optr,e);
}
else
break;
}
Push(Optr,*z);
z++;
break;
case'&':
case'|': //合取和析取
while(!StackEmpty(Optr))
{
GetTop(Optr,e);
if(e!='('&&e!='-'&&e!='=')
{
st2[i++]=e;
Pop(Optr,e);
}
else
break;
}
Push(Optr,*z);
z++;
break;
case'!': //否定
while(!StackEmpty(Optr))
{
GetTop(Optr,e);
if(e!='!')
{
st2[i++]=e;
Pop(Optr,e);
}
else
break;
}
Push(Optr,*z);
z++;
break;
default: //对应真值
while(*z=='0'||*z=='1')
{
st2[i++]=*z;
z++;
}
//st2[i++]='#'; //我不知道这行有什么用,书上有,我加上后运行不了
}
}
while(!StackEmpty(Optr))
{
Pop(Optr,e);
st2[i++]=e; //出栈
}
st2[i]='\0'; //给st2加上结束符
DestoryStack(Optr); //销毁栈
}
void disjunctiveFun(int table1[][5],int h,int num)
{
int i,j;
printf("主析取范式小项为:\n"); //如果合式公式真值为1就打印
for(i=0;i<h;i++)
{ if(table1[i][num]==1)
{
for(j=0;j<num;j++)
{ //printf("%d",table1[i][j]);
if(table1[i][j]==0) //对应字母为0,加!
printf("%c",'!');
printf("%c",st1[j]);
if(j!=num-1) //加个&
printf("%c",'&');
}
printf("\n");
}
}
}
void trueTable(char st[],int num) //打印真值表
{
int h,i,j,k;
int a;
int table[40];
char z[40];
h=(int)pow(2,num); //计算真值表的行数
for(i=0;i<num;i++)
{
printf("%c\t", st1[i]);
}
printf("%s\n",str);
strcpy(z,st);
for(i=0;i<h;i++)
{ strcpy(z,st);
for(j=0;j<num;j++)
{
table[j]=(i/(int)pow(2,j)%2); //真值表赋值
printf("%d\t", table[j]);
table1[i][j]=table[j]; //储存真值
}
for(k=0;z[k]!='\0';k++) //将字母转化为对应真值
{
for(j=0;st1[j]!='\0';j++)
{
if(z[k]==st1[j])
{
z[k]=table[j]+'0'; //弄了好长时间,char不能储存int
}
}
}
suffix(z,st2); //转化为后缀表达式
// printf("%s",st2);
a=calFun(st2); //计算后缀表达式
printf("%d\n",a);
// printf("%s\n",z);
table1[i][num]=a; //合适公式真值储存
}
disjunctiveFun(table1,h,num); //调用打印主析取范式函数
}
int judgeNum(char st[]) //判断变量个数是否正确并且提取字母
{
int i,j,k=0,num=0 ;
for(i=0;st[i]!='\0';i++)
{
if(st[i]>='A'&&st[i]<='Z')
{
for(j=0;j<i;j++)
{
if(st[j]==st[i])break ;
}
if(i==j)
{
st1[k++]=st[i]; //将字母存放到数组st1中
num++ ;
}
}
}
if(num>=1&&num<=4);
else{ flag=0;}
return num;
}
void judgeSymbol(char st[]) //判断符号是否输入正确
{
int i,j,num;
char s0[40];
strcpy(s0,st);
for(i=0;s0[i]!='\0';i++) //先把括号移除
{
if(s0[i]=='(')
for(j=i;s0[j]!='\0';j++)
s0[j]=s0[j+1];
if(s0[i]==')')
for(j=i;s0[j]!='\0';j++)
s0[j]=s0[j+1];
}
for(i=0;s0[i+1]!='\0';i++) //判断字母是否连着字母
{
if(s0[i]>='A'&&s0[i]<='Z')
{
if(s0[i+1]=='&'||s0[i+1]=='|'||s0[i+1]=='-'||s0[i+1]=='=');
else{
flag=0;
break;}
}
if(s0[i]=='!') //判断!后跟的是不是字母
{
if(s0[i+1]>='A'&&s0[i+1]<='Z');
else{
flag=0;
break;}
}
if(s0[i]=='&'||s0[i]=='|'||s0[i]=='-'||s0[i]=='=') //判断符号后跟的是不是字母
{
if(s0[i+1]>='A'&&s0[i+1]>='Z'||s0[i+1]!='!');
else{
flag=0;
break;}
}
}
if(flag==1)
num=judgeNum(st); //判断变量个数并且返回值
if(flag==1)
trueTable(st,num);
else printf("合式公式错误!\n");
}
void judgeParentheses(char st[]) //判断括号匹配是否正确
{
int i,left,right;
left=0;right=0;
for(i=0;st[i]!='\0';i++)
{ if(st[i]=='(')left++;
if(st[i]==')')right++;
if(left>right+1||left+1<right)break;
}
if(left!=right){flag=0;}
else judgeSymbol(st); //调用判断符号是否正确
}
void transform(char st[]) //把条件和双条件转化为-和=表示
{
int i,j;
for(i=0;st[i]!='\0';i++) //把双条件转化为=
{
if(st[i]=='<')
{
st[i]='=';
if(st[i+2]!='\0')
for(j=i;st[j]!='\0';j++)
st[++i]=st[i+2];
else if(st[i+2]=='\0')
st[++i]='\0';
}
}
for(i=0;st[i]!='\0';i++) //把条件转化为-
{
if(st[i]=='-')
{
if(st[i+1]!='\0')
for(j=i;st[j]!='\0';j++)
st[++i]=st[i+1];
else if(st[i+2]=='\0')
st[++i]='\0';
}
}
}
void main()
{
while(1){ //循环运行
printf("请输入1-4变元的一个合式公式:\n");
printf("仅支持大写英文字母的变元!\n");
printf("合取(&),析取(|),否定(!),条件(->),双条件 (<->):\n");
gets(str);
strcpy(st,str);
transform(st); //调用转化函数
judgeParentheses(st); //调用判断函数
system("pause"); // 按一下继续
system("cls"); //清屏
}
}
感想:转专业来到物联网学院,因为转专业需要补修大一课程的原因,打乱了自己的课表,和物联网不同的班级上课,发现每个班的优秀的同学都好多的,感觉自己大一像是玩了一样,而他们却学课外的知识,什么Python、java、单片机、MySQL等等。
这次离散数学大作业,感觉内容还好吧,虽然对我来说有点吃力,没系统的学过数据结构,C语言主要内容学完的情况下,能够完成感觉自己还是不错的。上课课程其他地方没有落下,就是某些算法,耦合度等关键词,因为数据结构没上,了解的也不是很多。只能大二下再补课。
写完这次大作业之后,感觉写其他的程序代码也不是很长。首先,克服了感觉自己代码输长了就闲麻烦,或者感觉输入的不正确。其次,为以后打程序打下了一个比较良好的基础。