离散数学 实验一
标题:真值表求主析取范式、主合取范式的计算机语言实现
其他课程的一些其他实验源码也可在本人github主页找到哦
链接如下:https://github.com/Schiz0mania
背景
- 语言、环境选择
- C++
- Visual Studio 2010
- 知识涉及(关键词)
- 线性栈的使用
- 中缀表达式,后缀表达式
- 映射
- 二进制枚举
- 实验思路
- 将中缀表达式转化为后缀表达
- 二进制枚举出变元的所有赋值情况
- 计算后缀表达式的值
- 根据表达式的值打印主析取和式与主合取范式
- 实验要求
- 通过输入变元、变元个数与原表达式而打印出目标表达式
- 分析程序复杂程度
实验步骤
一、表达式转化
-
定义顺序
表达式出栈构成原表达式之逆,则为逆序,反之则正序。 -
定义运算符优先级
参见函数实现代码int rate(char c);
-
定义栈
hz-逆序后缀表达式栈
oper-运算符栈 -
操作步骤
参见函数实现代码
void trans(char c);
- 如果是操作数那么将其直接入栈到hz中
- 如果是操作符,需要进一步判断
(1)如果是左括号’(‘直接入栈到oper中
(2)如果是运算符,先判断oper的栈顶的操作数的优先级(如果是空栈那么直接入栈到oper),如果栈顶是左括号那么直接入栈到oper中,如果栈顶是运算符,且栈顶运算符的优先级大于该运算符那么将栈顶的运算符出oper栈,并入栈到hz中,重复步骤3,如果栈顶运算符优先级小于该运算符,那么直接将该运算符入栈到oper中
(3)如果是右括号’)’,那么说明在oper中一定有一个左括号与之对应(在你没输错的情况下),那么将oper中的运算符依次出栈,并入栈到hz中,直到遇到左括号’(’(注意左括号不用入栈到hz)
(4)如果中缀表达式扫描完了,那么将oper中的操作数依次出栈,并入栈到hz中就可以了,如果没有没有扫描完重复1-3步
- 结果
hz栈中有逆序后缀表达式(需要转化为正序以便下一步计算表达式使用)
二、二进制枚举变元赋值情况
- 二进制枚举知识
(关键词)
按位与运算符(&)
左移运算(<<)
三、后缀表达式的计算
-
建立映射
map <char,int> link;
变元-赋值情况
子式-每次计算所得的真假情况(即为字符型"T"或"F" 其中link["T"]=1,link["F"]=0
) -
定义栈
pf-正序后缀表达式栈
ans-表达式最终结果(“T"或"F”) -
定义操作符运算
参见函数实现代码void cal(int count,char *pfarr);
-
具体操作
从左到右读表达式,如果读到操作数就将它压入栈ans中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项按操作数运算,再将运算的结果代替原栈顶的n项,压入栈ans中 。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的表达式真值情况为结束。
- 结果
一次运算获得变元一种赋值情况下表达式的真值情况
若为真,则把row存入Tstc栈中,后续用于打印主析取和式。
若为假,则存入对应的Fstc栈,后续用于打印主合取范式。
解释:两个栈存放放表达式为真,假时的row情况,可倒推到table中row行各变元的赋值情况,加上之前变元var元素与table元素的映射,用于打印目标表达式。
四、打印结果
-
. 实验案例1
3
PQR
P&Q&R#
-
实验案例2
2
PR
(!!P)>R#
-
实验用例3
3
PRQ
((P>R)|Q)~(P&(R>Q))#
-
实验用例4
3
PQS
((P>S)&(P~Q))>S#
实验源码
#include<iostream>
#include<map>
#include<stack>
using namespace std; // 优先级: 非 5 !
char bds[50]={"0"}; //原表达式 析取 4 &
stack <char> hz;//后缀表达式栈 (逆序) 合取 3 |
int flag=1; // 条件 2 >
stack <char> oper;//操作符 栈 双条件 1 ~
char var[10];//不超过10个变元储存在这
int num=0; //变元个数
stack <char> pf;//后缀表达式(正序)以便于计算
stack <char> ans;//结果在这里
int table[1025][1025];//变元赋值情况表(不多于10个变元)
stack <int> Tstc;//存放表达式为真时的row情况,推到table中row行各变元的赋值情况
stack <int> Fstc;//同上
int row = 0;// 行标记值
map<char,int> link;
void Input()
{cout<<"欢迎使用本计算系统!(支持10个以内变元的计算)\n";
cout<<"输入变元个数(不大于10)"<<endl;
cin>>num;
while(num<1 || num>10)
{cout<<"请重新输入变元个数:\n";
cin>>num;
}
cout<<"输入单个变元(大写,不加空格)"<<endl;
for(int n=0;n<=num-1;n++)
cin>>var[n];
cout<<"本系统操作符规定如下:"<<endl;
cout<<"非 ! "<<endl;
cout<<"合取 &"<<endl;
cout<<"析取 |"<<endl;
cout<<"条件 > "<<endl;
cout<<"双条件 ~ "<<endl;
cout<<"输入需要计算的表达式(以“#”结尾):\n";
}
void assign(int num)//变元赋值
{
for(int i = 0; i < (1<<num); i++) //从0~2^n-1个状态
{
for(int j = 0; j < num; j++) //遍历二进制的每一位
{
if(i & (1 << j))//判断二进制第j位是否为1
{
table[i][j]=1;//如果为1,赋值table相应赋为1
}
}
}
}
int rate(char c)//运算优先级
{switch(c)
{case 33: // !
return 5;
case 38: // &
return 4;
case 124: // |
return 3;
case 62: // >
return 2;
case 126: // ~
return 1;
default :return 0;// 两个括号
}
}
void trans(char c)//转化表达式
{if(c>=65 && c<=90)//为大写字母,直接入栈
{hz.push(c);
return ;}
if(c == 35) // #
{while(!oper.empty())
{hz.push(oper.top());
oper.pop();
}
flag=0;//式子结束,flag赋值为0,hz完备
return;
}
//运算符的话
switch (c)
{ case 40:// 左括号直接入栈
oper.push(c);
return ;
case 33:// ! 优先级最高
oper.push(c);
return ;
default:
if(oper.empty() )//栈空 只要输入正确,肯定不会出现右括号入空栈
{oper.push(c);
return;}
if(rate(oper.top()) <= rate(c) ) oper.push(c);//优先级大的直接入栈
else//需要复杂操作的
{
while( (!oper.empty()) && (oper.top() != 40) )//栈顶不是左括号 或者 栈没空
{hz.push(oper.top());
oper.pop();
}
if(oper.top() ==40)//弹到左括号处停
{oper.pop();
return ;}
if(oper.empty())
{oper.push(c);
return ;}
}
}
}
void cal(int count,char *pfarr)//传了个后缀表达式数组过来计算------------------------这个函数一次只计算一种赋值下的表达式值
{ char a=char("T");
char b=char("F");
char temp;
for(int i=count-1;i>=0;i--)//建立计算栈pf
pf.push(pfarr[i]);
for(int i=0;i<=num-1;i++)
link[var[i]]=table[row][i];//建立映射关系,在row行(row+1为第row+1个变元赋值情况)下,第i个变元对应table里i列的赋值情况
while(!pf.empty())
{ if((pf.top()>=65)&&(pf.top()<=90))//字母直接入ans栈
ans.push(pf.top());
else//运算符的情况分类
{ switch(pf.top())
{case 33:// ! 一元
if(link[ans.top()])//变元对应赋值为真,压栈值则为"F"
{ans.pop();
ans.push(b);
}
else
{ans.pop();
ans.push(a);
}
break;
//二元,两个操作数,每次pop两次
case 38://& 逻辑和
temp=ans.top();
ans.pop();//pop 1
if(!link[temp] || !link[ans.top()])//有一个为假
{ans.pop();//pop2
ans.push(b);
}
else
{ans.pop();//pop2
ans.push(a);
}
break;
case 124:// | 逻辑或
temp=ans.top();
ans.pop();
if(link[temp] || link[ans.top()])//一个为真
{ans.pop();
ans.push(a);
}
else
{ans.pop();
ans.push(b);
}
break;
case 62:// > 条件
temp=ans.top();//这个时后操作数
ans.pop();
if(link[ans.top()])//这里注意逻辑短路 后假前真
{if(! link[temp])//唯假情况
{ans.pop();
ans.push(b);
}
else
{ans.pop();
ans.push(a);
}
}
else {ans.pop();
ans.push(a);
}
break;
case 126:// ~ 双条件
temp=ans.top();
ans.pop();
if (link[temp] == link[ans.top()])//赋值情况相同为真
{ans.pop();
ans.push(a);
}
else
{ans.pop();
ans.push(b);
}
break;
}
}
pf.pop();//统一在这里弹pf的栈顶
}
//最后ans栈里只有一个字符,对应这表达式的真假值
if(link[ans.top()])//表达式为真
Tstc.push(row);//建立表达式为真 变元赋值情况 的隐形对应
else
Fstc.push(row);
ans.pop();//清空
row++;//下一次运算取下一种变元赋值情况
}
void Output(int num)//实参:变元个数
{ // ∧ ¬ ∨
cout<<endl;
int count=1;//小项个数
cout<<"主析取范式:\n";
if( Tstc.empty()) cout<<"\t无主析取和式!\n";
while(! Tstc.empty())
{ if(count!=1)
cout<<"∨";
cout<<"(";
for(int i=0;i<=num-1;i++)
{ if(table[Tstc.top()][i])
cout<<var[i];
else
cout<<"!"<<var[i];
if(i+1 != num)
cout<<"∧";
}
cout<<")";
Tstc.pop();
count++;
}
cout<<endl;
count=1;
cout<<"主合取范式:\n";
if( Fstc.empty()) cout<<"\t无主合取范式!\n";
while( ! Fstc.empty() )
{ if(count!=1)
cout<<"∧";
cout<<"(";
for(int i=0;i<=num-1;i++)
{ if(!table[Fstc.top()][i])
cout<<var[i];
else
cout<<"!"<<var[i];
if(i+1 != num)
cout<<"∨";
}
cout<<")";
Fstc.pop();
count++;
}
cout<<endl;
cout<<"运算结束,谢谢使用!\n";
}
void main()
{//------------------------------------------------Input------------------------------------------------
Input();
//------------------------------------------------assign(变元赋值)------------------------------------------------
assign(num);
//------------------------------------------------trans(中缀转后缀)------------------------------------------------
int i;
for(i=0;(i<=49)&&flag;i++)//该循环结束后hz中为目标表达式的逆序(按出栈顺序)
{ cin>>bds[i];
trans(bds[i]);
}
//后缀表达式转入pfarr数组里
int count=int(hz.size());//也就是pfarr 元素个数
char *pfarr= new char[hz.size()];//
if(pfarr==NULL)
{cout<<"Allocation failed!"<<endl;
exit(1);}
for(int i=hz.size()-1;i>=0;i--) //令pfarr里有正序的后缀表达式
{pfarr[i]=hz.top();
hz.pop();
}
//------------------------------------------------cal(计算表达式的真值情况)------------------------------------------------
link[char("T")]=1;
link[char("F")]=0;
for(int time=1;time<=(1<<num);time++)
cal( count ,pfarr);
//------------------------------------------------Output------------------------------------------------
Output(num);
delete []pfarr;
}