离散数学 实验一

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

其他课程的一些其他实验源码也可在本人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--)//建立计算栈pfpf.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 1if(!link[temp] || !link[ans.top()])//有一个为假{ans.pop();//pop2ans.push(b);}else{ans.pop();//pop2ans.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;
}

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

  1. 【c++】[自动生成真值表/主析取范式/主合取范式的计算器]

    关于自动生成真值表/主析取范式/主合取范式的计算器 我用c++写了一个,需要的自取,如果好用请点赞 链接:https://pan.baidu.com/s/1Ji1zPDtjAc6-TDxovEzMVw ...

  2. 【离散数学】Java语言实现利用真值表法求主析取范式和主合取范式

    C++版本的看这个链接: [离散数学]C++语言实现利用真值表法求主析取范式和主合取范式_zhtstar的博客-CSDN博客https://blog.csdn.net/weixin_56319483/ ...

  3. 【离散数学】C++语言实现利用真值表法求主析取范式和主合取范式

    Java版本的如下链接所示: Java语言实现利用真值表法求主析取范式和主合取范式_zhtstar的博客-CSDN博客https://blog.csdn.net/weixin_56319483/art ...

  4. 离散数学范式c语言实验报告,离散数学实验报告-利用真值表法求主析取范式及主合取范式的实现...

    1.实 验 报 告( / 学年 第 一 学期)课程名称离散数学实验名称利用真值表法求主析取范式及主合取范式的实现实验时间年月日指导单位指导教师学生姓名班级学号学院(系)专 业 实 验 报 告实验名称利 ...

  5. 【数理逻辑】范式 ( 合取范式 | 析取范式 | 大项 | 小项 | 极大项 | 极小项 | 主合取范式 | 主析取范式 | 等值演算方法求主析/合取范式 | 真值表法求主析/合取范式 )

    文章目录 一. 相关概念 1. 简单 析取 合取 式 ( 1 ) 简单合取式 ( 2 ) 简单析取式 2. 极小项 ( 1 ) 极小项 简介 ( 2 ) 极小项 说明 ( 3 ) 两个命题变项 的 极 ...

  6. 利用真值表法求取主析取范式以及主合取范式的实现(C++)

    代码如下: #include <iostream> #include <stack> #include <string> #include <vector&g ...

  7. 离散数学 求命题公式的主析取范式和主合取范式

    Description 输入命题公式的合式公式,求出公式的真值表,并输出该公式的主合取范式和主析取范式. Input 命题公式的合式公式 Output 公式的主析取范式和主合取范式,输出形式为:&qu ...

  8. 使用C++求命题公式的主析取范式与主合取范式

    最近的离散数学的一个上机作业,要求任意输入一个命题公式,求它的真值表与主析取范式和主合取范式.其中的命题连接词都是用特殊符号来表示(怕麻烦--),并最终选择使用C++来编写程序. 先贴代码: // 五 ...

  9. 求主析取范式与主合取范式

    定义设A为恰含命题变元p1,-,pn的公式.公式A称为A的主析(合)取范式(majordisjunctive(conjunctive)normal form),如果A是A的析(合)取范式,并且其每个合 ...

最新文章

  1. esxi宿主机的本地存储-非活动
  2. 验证手机号码归属地_手机号码归属地能否取消? 工信部回复了
  3. E. Almost Sorted(构造,递归)
  4. BZOJ1004 [HNOI2008]Cards 【burnside定理 + 01背包】
  5. 如何将自己的代码自动添加版权信息[转]
  6. 微信公众平台客户端调试工具
  7. 物联卡与SIM卡相比优势在哪
  8. 试验设计第二版茆诗松课后题答案_试验设计习题及答案
  9. 520,张一鸣宣布卸任字节跳动CEO!
  10. AI:大力出奇迹?Bigger is better?AI下一代浪潮?—人工智能的大语言模型(LLMs)/预训练大模型的简介、发展以及未来趋势
  11. $.ligerDialog弹出对话框
  12. matlab 渐变色
  13. win8.1+ubuntu13.10 双硬盘 uefi+gpt ASUS S400 安装经验
  14. 局域网不能访问的问题
  15. 机器学习:分类模型大全
  16. alluxio java_学习 Alluxio(四):Java API
  17. JGit操作之2.0版
  18. 开心消消乐java下载_开心消消乐下载_开心消消乐下载最新iPhone版-太平洋下载中心...
  19. ESPHome 和 Home Assistant点灯握手
  20. 新手安装R以及Rstudio软件

热门文章

  1. abb工业机器人指令lf怎么用_史上最全的ABB工业机器人的指令介绍
  2. 用Echarts自定义多系列左右双坐标对比的图表,适合展示TGI对比等场景
  3. Linux用户与权限管理(一)
  4. 气动与PLC控制实验装置带机械手QY-QDSY01
  5. firewalld防火墙总结
  6. 终端服务的剪贴板的缺陷,导致WPF调用Clipboard.SetText() 失败
  7. 2022年深圳市专精特新中小企业认定标准、材料清单
  8. 浅谈抢票软件的危害及模拟分析
  9. Ambari2.7.3自定义安装Ambari-Doris-Service
  10. WorkNC配置与MAKINO牧野 MCC2013 6轴加工中心