重言式判别.docx
《重言式判别.docx》由会员分享,可在线阅读,更多相关《重言式判别.docx(14页珍藏版)》请在冰豆网上搜索。
重言式判别
重言式判别
一、问题描述
一个逻辑表达式如果对于其变元的任一种取值都为真,则称为重言式;反之,如果对于其变元的任一种取值都为假,则称为矛盾式;然而,更多的情况下,既非重言式,也非矛盾式。
试写一程序,通过真值表判断一个逻辑表达式属于那一类。
二、需求分析
(1)逻辑表达式从终端输入,长度不超过一行。
逻辑运算符包括“|”,“&”和“~”,
分别表示或、与和非,运算优先程度递增,但可以有括号改变,即括号内的运算优先。
逻辑变元为大写字母。
表达式中任何地方都可以含有多个空格符。
(2)若是重言式或矛盾式,可以只“显示Trueforever”或“Falseforever”,否则显示“Satisfactible”以及变量名序列,与用户交互。
若用户对表达式中变元取一组值,程序就求出并显示逻辑表达式的值。
(3)测试数据:
①(A|~A)&(B|~B)
②(A&|~A)&C
③A|B|C|D|E|~A
④A&B&C&~B
⑤(A|B)&(A|~A)
⑥A&~B|~A&B
三、概要设计
voidcreatzuhe(intn)
初始条件:
n是循环体,以便产生各种情况。
操作结果:
产生变量的各种取值组合。
voidcreate(bitree&root,bitreel,bitreer)
初始条件:
root是根结点,l是左子树,r是右子树。
操作结果:
按照运算符优先级关系建立二叉树。
charyouxianji(charlie,charhang)
初始条件:
lie是第一个运算符,hang是第二个运算符。
操作结果:
得出优先级比较关系。
voidcreatstack(sqstack&st)
初始条件:
若栈st存在。
操作结果:
把栈置空。
voidpush(sqstack&st,bitreee)
初始条件:
若栈st存在,对树结点e操作。
操作结果:
把操作符入栈。
voidpop(sqstack&st,bitree&e)
初始条件:
若栈st存在,对树结点e操作。
操作结果:
把操作符出栈。
voidGettop(sqstack&st,bitree&e)
初始条件:
若栈st存在,对树结点e操作。
操作结果:
取栈顶元素。
voidcreattree(chars[],bitree&tree)
初始条件:
是s[]存放运算符,tree是按照运算符优先级建立的二叉树。
操作结果:
判断重言式。
intvalue_tree(bitreetree)
初始条件:
tree是按照运算符优先级建立的二叉树。
操作结果:
对逻辑表达式求值。
算法思想:
自底向上地根据运算符地优先级来建立分子树函数;当逻辑表达式读完后-子根root就是一棵完整的二叉树。
用穷举法得出所有可能组合,对二叉树进行先序遍历,对存放在上面的表达式进行求值。
并用两个栈分别存放运算符和变量,来判别是否为重言式。
四、程序实现关键代码
typedefstructbtdnode{
chardata;
structbtdnode*lchild;
structbtdnode*rchild;
}*bitree;
//识别表达式使用的堆栈定义,它存放的都是树的结构;
typedefstructStack{
structbtdnode**base;//栈中的元素都是树的结点结构;
structbtdnode**top;
intstacksize;
}sqstack;
//用于产生变量的各种取值组合;
voidcreatzuhe(intn)
{
inti,num=0,j=0,e;
inttemp[max];
for(i=0;izuhe[i]=0;
while(n)
{
e=n%2;
num++;
temp[j++]=e;
n=n/2;
}
j=j-1;
num=N-num;
while(j>=0)
{
e=temp[j--];
zuhe[num++]=e;
}
}
//自底向上地根据运算符地优先级来建立分子树函数;当逻辑表达式读完后-子根root就是一棵完整的二叉树
intk=0;//建树的标志,k=1表示第一次建立分子树,要对左右孩子的指针域处理
voidcreate(bitree&root,bitreel,bitreer)
{
root->lchild=l;
root->rchild=r;//分树的链接
if(l&&r)
{
if(int(l->data)>=65&&int(l->data)<=90)
{
l->lchild=NULL;
l->rchild=NULL;
}
if(int(r->data)>=65&&int(r->data)<=90)
{
r->lchild=NULL;
r->rchild=NULL;
}
}
}
//逻辑运算符的优先级判别;
charyouxianji(charlie,charhang)
{
inti,j;
charbijiao[7][7]={'','|','&','~','(',')','#',
'|','>','<','<','<','>','>',
'&','>','>','<','<','>','>',
'~','>','>','>','<','>','>',
'(','<','<','<','<','=','',
')','>','>','>','','>','>',
'#','<','<','<','<','','='};
for(j=0;j<7;j++)
if(bijiao[0][j]==lie)
break;
for(i=0;i<7;i++)
if(bijiao[i][0]==hang)
break;
returnbijiao[i][j];
}
//对操作符栈和变量堆栈的操作;
voidcreatstack(sqstack&st)//置空栈
{
st.base=(bitree*)malloc(stack_size_normal*sizeof(btdnode));
st.top=st.base;
st.stacksize=stack_size_normal;
}
voidpush(sqstack&st,bitreee)//入栈
{
if(st.top-st.base*st.top++=e;
}
voidpop(sqstack&st,bitree&e)//出栈
{
e=*--st.top;
}
voidGettop(sqstack&st,bitree&e)//栈顶元素
{
e=*(st.top-1);
}
/////////////////////////////////////////////////////////////////////////////////
//重言式的识别函数;
voidcreattree(chars[],bitree&tree)
{
sqstackBiangliang_Stack;//变量栈;
sqstackYunsuan_Stack;//逻辑运算符栈;
creatstack(Biangliang_Stack);//变量栈的初始化;
creatstack(Yunsuan_Stack);//逻辑运算符栈初始化;
bitreelogic_di,variables,logics,e,a,b,theta,kuohao;//定义栈中的元素,theta为最后的二叉树的根
logic_di=(bitree)malloc(sizeof(btdnode));//定义新空间
logic_di->data='#';//令运算符栈顶元素为'#'
push(Yunsuan_Stack,logic_di);//运算符入栈
while(*s!
=NULL)//输入数据不为空
{
if(int(*s)>=65&&int(*s)<=90)//且在大写字母A~Z之间(限定为A~Z)
{
variables=(bitree)malloc(sizeof(btdnode));
variables->data=*s;
push(Biangliang_Stack,variables);
}
elseif(int(*s)>90||int(*s)<65)
{
Gettop(Yunsuan_Stack,e);//取运算符栈的栈顶元素进行优先级比较
switch(youxianji(*s,e->data))//优先级比较
{
case'<':
//栈顶的运算符优先级低,逻辑运算符进栈
logics=(bitree)malloc(sizeof(btdnode));
logics->data=*s;
push(Yunsuan_Stack,logics);//如果ASC码小于,则入栈
break;
case'=':
pop(Yunsuan_Stack,kuohao);//脱括号并接受下一个字符;
break;
case'>':
//如果ASC码大于,则运算符和变量出栈
pop(Yunsuan_Stack,theta);//弹出逻辑运算符
pop(Biangliang_Stack,a);//弹出变量
b=NULL;
if(theta->data!
='~')//如果根结点的值大于"~"
pop(Biangliang_Stack,b);//弹出变量
//建树的函数调用
k=k+1;
create(theta,b,a);//建立二叉树
push(Biangliang_Stack,theta);//将临时的根作为新的变量压入变量栈中;
if(*s!
='#'&&*s!
=')')//(作用是当输入一个元素是运算符时,将其入栈,否则该运算符无法运算)
{
logics=(bitree)malloc(sizeof(btdnode));
logics->data=*s;
push(Yunsuan_Stack,logics);//入运算符栈
}
elses=s-1;//(防止在开头连续输入相同两个运算符,如果连续输入两个运算符,只能对第一个运算符进行运算)
break;
}
}
s++;
}
tree=theta;
}
//根据变量的取值组合并利用逻辑表达式的性质对树进行求值
intvalue_tree(bitreetree)
{
if(!
tree)return0;//遇到空的结点;
elseif(tree->data!
='|'&&tree->data!
='&'&&tree->data!
='~')//找到的是变量;
returnzuhe[int(tree->data)-65];
elseif(int(tree->data)<65||int(tree->data)>90)//找到的是运算符;
switch(tree->data)
{
case'|':
return(value_tree(tree->lchild)||value_tree(tree->rchild));//如果是或,则将左子树和右子树进行或运算
case'&':
return(value_tree(tree->lchild)&&value_tree(tree->rchild));//如果是与,则将左子树和右子树进行与运算
case'~':
return(!
value_tree(tree->rchild));////如果是非,则将左子树和右子树进行非运算
}
}
五、调试分析
刚开始将程序编译到无错误时发现无法出现结果。
DEBUG调试发现变量根本没有附值,所以设计一个zuhe函数,将每个变量给值0或1,得出了结果。
由于将栈清空,所以每次读到第一个运算符时总要将其入栈,很麻烦,所以决定在栈放入“#”号,这样就只要判别优先级就可以决定进出栈。
发现连续输入两个运算符就会出现无法运行的情况,找到程序位置禁止连续输入相同的符号,使程序可读性更好。
六、测试结果和分析
测试数据①(A|~A)&(B|~B)
当A、B取值为0,0;0,1;1,0;1,1;时表达式的值分别为1,1,1,1,为重言式。
测试数据②(A&|~A)&C
当A、C取值为0,0;0,1;1,0;1,1;时表达式的值分别为0,0,0,0,为矛盾式。
测试数据③A|B|C|D|E|~A
测试结果为重言式。
测试数据④A&B&C&~B
测试结果为矛盾式。
⑤(A|B)&(A|~A)
测试结果为非重言式和矛盾式。
测试数据⑥A&~B|~A&B
测试结果为非重言式和矛盾式。
从以上六个测试用例来看,结果正确。
但是程序还有一些有待解决的问题。
比如,表达式中没有解决空格的问题,当有空格存在,就无法继续下去。
另一个问题是表中显示的大写字母和表达式中的大写字母不对应,只是机械的按照次序。
但总的来说程序已经比较完善,书上的要求也基本符合,并且加入了对矛盾式的判别。
七、感想与建议
重言式是离散数学中一个很重要的概念,原本我以为这个程序会相对比较简单。
但在编的过程中明显发现了困难。
它运用到了二叉树存储结构,还利用了栈的概念对变量和运算符分别保存。
充分运用了我们学过的知识,对我们之前实验所做的很多东西进行了总结。
这个实验不仅仅是一次课程设计,更是对我们整个数据结构的第二章和第六章的复习,具有很重要的锻炼价值。