离散实验 真值表求主析取范式、主合取范式的计算机语言实现

离散数学 实验一

标题:真值表求主析取范式、主合取范式的计算机语言实现

其他课程的一些其他实验源码也可在本人github主页找到哦
链接如下:https://github.com/Schiz0mania

背景

  • 语言、环境选择
    1. C++
    2. Visual Studio 2010
  • 知识涉及(关键词)
    1. 线性栈的使用
    2. 中缀表达式,后缀表达式
    3. 映射
    4. 二进制枚举
  • 实验思路
    1. 将中缀表达式转化为后缀表达
    2. 二进制枚举出变元的所有赋值情况
    3. 计算后缀表达式的值
    4. 根据表达式的值打印主析取和式与主合取范式
  • 实验要求
  1. 通过输入变元、变元个数与原表达式而打印出目标表达式
  2. 分析程序复杂程度

实验步骤

一、表达式转化

  • 定义顺序
    表达式出栈构成原表达式之逆,则为逆序,反之则正序。

  • 定义运算符优先级
    参见函数实现代码int rate(char c);

  • 定义栈
    hz-逆序后缀表达式栈
    oper-运算符栈

  • 操作步骤

参见函数实现代码 void trans(char c);

  1. 如果是操作数那么将其直接入栈到hz中
  2. 如果是操作符,需要进一步判断
    (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;
}
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值