一.实验内容

(1)求任意一个命题公式的真值表。

(2)利用真值表求任意一个命题公式的主范式。

(3)利用真值表进行逻辑推理。

注:(2)和(3)可在(1)的基础上完成。

二.实验目的

真值表是命题逻辑中的一个十分重要的概念,利用它几乎可以解决命题逻辑中的所有问题。例如,利用命题公式的真值表,可以判断命题公式的类型、求命题公式的主范式、判断两命题公式是否等价,还可以进行推理等。

本实验通过编写一个程序,让计算机给出命题公式的真值表,并在此基础上进行命题公式类型的判定、求命题公式的主范式等。目的是让学生更加深刻地理解真值表的概念,并掌握真值表的求解方法及其在解决命题逻辑中其他问题中的应用。

三.算法的主要思想

利用计算机求命题公式真值表的关键是:①给出命题变元的每一组赋值;②计算命题公式在每一组赋值下的真值。

真值表中命题变元的取值具有如下规律:每列中0和1是交替出现的,且0和1连续出现的个数相同。n个命题变元的每组赋值的生成算法可基于这种思想。

含有n个命题变元的命题公式的真值的计算采用的方法为“算符优先法”。

为了程序实现的方便,约定命题变元只用一个字母表示,非、合取、析取、条件和双条件联结词分别用!、&、|、-、+来表示。

算符之间的优先关系如表1-32所示:

表1-32 算符优先级

+   -    |    &    !    (   )   #

|

&

!

#

>   <   <   <   <    <  >   >

>   >   <   <   <    <  >   >

>   >   >   <   <    <  >   >

>   >   >   >   <    <  >   >

>   >   >   >   >    <  >   >

<   <   <   <   <    <  =    E

>   >   >   >   >    E   >   >

<   <   <   <   <   <   E    =

为实现算符优先算法,我们采用两个工作栈。一个称作OPTR,用以寄存运算符;另一个称作OPND,用以寄存操作数或运算结果。

四.算法的具体实现  

(1)定义全局的属性:

    (这里栈就直接调用C++有的库#include<stack>,就不用自己再写栈的代码)

     运算符栈OPTR

     操作数或运算结果栈OPND

     常量操作字符集合operateChar

     公式中的变元VarList  

stack<bool> OPND;//操作数或运算结果栈
stack<char> OPTR;//运算符栈
const char operateChar[8] = { '+', '-', '|', '&', '!', '(', ')','#' };//运算符(+:等价、-:蕴涵、|:析取、&:合取、!:否定
string VarList;//保存公式中的变元

(2)判断字符是否是运算符

//判断字符是否是运算符
bool In(char ch) {
for (int i = 0; i < (int)strlen(operateChar); i++)
if (operateChar[i] == ch)
return true;
return false;
}

(3)比较运算符的优先级

     这里可以直接用二维字符数组实现运算符优先级表,将OPTR的栈顶运算符(theta1)与新输入的运算符(theta1)作比较,各自按照operateChar的顺序赋值就可以得到优先级

//比较运算符的优先级
char Precede(char theta1, char theta2) {
//运算符优先级表
char operate_Table[8][9] = {
"><<<<<>>",
">><<<<>>",
">>><<<>>",
">>>><<>>",
">>>>><>>",
"<<<<<<=E",
">>>>>E>>",
"<<<<<<E="
};
int theta1_Index=0, theta2_Index=0;//定义运算符1和运算符2的索引
for (int i = 0; i < (int)strlen(operateChar); i++){
if (operateChar[i] == theta1)
theta1_Index = i ;
if (operateChar[i] == theta2)
theta2_Index = i ;
}
return operate_Table[theta1_Index][theta2_Index];
}

(4)计算表达式,并将结果返回(这里是计算双目的,在这次试验命题公式中只有!是单目运算符所以到时直接在计算表达式时直接计算)

//计算表达式,并将结果返回(双目)
bool Calculate(bool x, char operate, bool y) {
switch (operate)
{
case '+': return (((!x) || y) && (x || (!y))); break;
case '-': return ((!x) || y); break;
case '|': return x || y; break;
case '&': return x && y; break;
}
return -1;
}

(5)判断是否是字母(用来将公式中的命题变元提取出来),然后将变元按递增顺序赋值给VarList

     我就只做了小写字母的,大写字母可以自己改下判断条件

//判断是否是字母(仅小写字母)
bool isAlpha(char ch) {
if (((int)ch >= 97 && (int)ch <= 122)&&VarList.find(ch)==-1)//若字符的ascii码在97~122中则是字母
return true;
else return false;
}
//提取公式字符串中全部命题变元,并按递增顺序存放在VarList中
void createVarList(string source) {
int indexNum=0;//比较字母用的下标 for (auto ch:source) {
if (isAlpha(ch))
VarList += ch;
}
for (int i = 0; i < VarList.length()-1;i++) {
for (int j = 0; j < VarList.length() - i-1; j++) {
if ((int)VarList[j] > (int)VarList[j + 1]) {
char temp = VarList[j];
VarList[j] = VarList[j + 1];
VarList[j + 1] = temp;
}
}
}
}

(6)计算表达式

bool InfixValue(string source) {
OPTR.push('#');
char item = source[0];
bool OPNDtop2, OPNDtop1;
char OPTRtop;
int i = 1;
while (item != '#' || OPTR.top() != '#') {
if (!In(item)) {
if (item == '0')
OPND.push(false);
else
OPND.push(true);
item = source[i++];
}
else if (OPTR.top() == '!')
{
OPTR.pop();
OPNDtop1 = OPND.top();
OPND.pop();
OPND.push(!OPNDtop1);
}
else
{
switch (Precede(OPTR.top(), item))
{
case '<':
OPTR.push(item);
item = source[i++];
break;
case '>':
OPTRtop = OPTR.top();
OPTR.pop();
OPNDtop1 = OPND.top();
OPND.pop();
OPNDtop2 = OPND.top();
OPND.pop();
OPND.push(Calculate(OPNDtop2, OPTRtop, OPNDtop1));
break;
case '=':
OPTR.pop();
item = source[i++];
break;
}
}
}
return OPND.top();
}

(7)因为我们要获得变元所有的取值情况,所以可以当成二进制来依次递增

     比如  变元有 a b c 三个,就是从 000 开始依次取到111

     这就需要一个对二进制增值的方法

//依次取值的二进制值加1
void IncreaseVarValue(char(&v)[26], int& flag) {
int m = 1; int n = VarList.length();
for (int j = n - 1; j > -1; j--) {
int temp;
temp = int(v[j]) - 48;
flag = flag + temp;
if (flag == 2) {
v[j] = '0'; flag = 1;
}
else {
v[j] = '1'; flag = 0; break;
}
} }

(8)得到真值表

void TruthTable(string expression,bool * &truthTab,string *&expressionValueList,string *&trowList,int &CircleNum) {
int m = 1;
int n = VarList.length();
int flag;
char trow[26];//表达式中变元的依次取值
for (int i = 0; i < n; i++) { m *= 2; trow[i] = '0'; }
string* expressionValueList_IN = new string[m];
string* trowList_IN = new string[m];
bool *truthtable_IN=new bool[m];//真值表中的值
CircleNum = m; //转换成用0或1表示的表达式
for (int i = 0; i < m; i++) {
string value1 = expression;//因为公式是字符串无法直接计算,所以定义value1,将里面的命题变元变成0或1
//使表达式的变元用0或1表示
for (int j = 0; j < n; j++) {
char x = VarList[j];
for (int k = 0; k < expression.length(); k++) {
char a = value1[k];
if (value1[k] == x)
value1[k] =trow[j];
} }
trowList_IN[i] = trow;
expressionValueList_IN[i] = value1;
truthtable_IN[i] = InfixValue(value1);//将得出来的值依次给truthtable
flag = 1;
IncreaseVarValue(trow,flag);
}
truthTab = truthtable_IN;
expressionValueList = expressionValueList_IN;
trowList = trowList_IN;
}

(9)最后输出真值表

//输出真值表
void PrintTable(string expression) {
string* expressionValueList;//用来保存所有的表达式
string* trowList;//保存变元的所有取值
bool* truthtab;//保存所有的表达式的值
int CircleNum;//循环次数
createVarList(expression);
TruthTable(expression, truthtab, expressionValueList, trowList,CircleNum);
//打印真值表
for (int i = 0; i < VarList.length(); i++) {
cout << VarList[i] << "\t";
}
cout << expression <<"\t" <<"值"<<endl; for (int i = 0; i < CircleNum; i++) {
for (int j = 0; j < VarList.length(); j++) {
cout << trowList[i][j] << "\t";
}
cout << expressionValueList[i] << "\t" << truthtab[i] << endl;;
}
}

  

完整的代码:

#include<iostream>
#include<math.h>
#include<string.h>
#include<stack>
using namespace std; stack<bool> OPND;//操作数或运算结果栈
stack<char> OPTR;//运算符栈
const char operateChar[8] = { '+', '-', '|', '&', '!', '(', ')','#' };//运算符(+:等价、-:蕴涵、|:析取、&:合取、!:否定
string VarList;//保存公式中的变元 //判断字符是否是运算符
bool In(char ch) {
for (int i = 0; i < (int)strlen(operateChar); i++)
if (operateChar[i] == ch)
return true;
return false;
} //比较运算符的优先级
char Precede(char theta1, char theta2) {
//运算符优先级表
char operate_Table[8][9] = {
"><<<<<>>",
">><<<<>>",
">>><<<>>",
">>>><<>>",
">>>>><>>",
"<<<<<<=E",
">>>>>E>>",
"<<<<<<E="
};
int theta1_Index=0, theta2_Index=0;//定义运算符1和运算符2的索引
for (int i = 0; i < (int)strlen(operateChar); i++){
if (operateChar[i] == theta1)
theta1_Index = i ;
if (operateChar[i] == theta2)
theta2_Index = i ;
}
return operate_Table[theta1_Index][theta2_Index];
}
//计算表达式,并将结果返回(双目)
bool Calculate(bool x, char operate, bool y) {
switch (operate)
{
case '+': return (((!x) || y) && (x || (!y))); break;
case '-': return ((!x) || y); break;
case '|': return x || y; break;
case '&': return x && y; break;
}
return -1;
} //判断是否是字母(仅小写字母)
bool isAlpha(char ch) {
if (((int)ch >= 97 && (int)ch <= 122)&&VarList.find(ch)==-1)//若字符的ascii码在97~122中则是字母
return true;
else return false;
} //提取公式字符串中全部命题变元,并按递增顺序存放在VarList中
//比如 (!a-d)-(c+b) VarList值就是 "abcd"
void createVarList(string source) {
int indexNum=0;//比较字母用的下标 for (auto ch:source) {
if (isAlpha(ch))
VarList += ch;
}
for (int i = 0; i < VarList.length()-1;i++) {
for (int j = 0; j < VarList.length() - i-1; j++) {
if ((int)VarList[j] > (int)VarList[j + 1]) {
char temp = VarList[j];
VarList[j] = VarList[j + 1];
VarList[j + 1] = temp;
}
}
}
} //计算表达式
bool InfixValue(string source) {
OPTR.push('#');
char item = source[0];
bool OPNDtop2, OPNDtop1;
char OPTRtop;
int i = 1;
while (item != '#' || OPTR.top() != '#') {
if (!In(item)) {
if (item == '0')
OPND.push(false);
else
OPND.push(true);
item = source[i++];
}
else if (OPTR.top() == '!')
{
OPTR.pop();
OPNDtop1 = OPND.top();
OPND.pop();
OPND.push(!OPNDtop1);
}
else
{
switch (Precede(OPTR.top(), item))
{
case '<':
OPTR.push(item);
item = source[i++];
break;
case '>':
OPTRtop = OPTR.top();
OPTR.pop();
OPNDtop1 = OPND.top();
OPND.pop();
OPNDtop2 = OPND.top();
OPND.pop();
OPND.push(Calculate(OPNDtop2, OPTRtop, OPNDtop1));
break;
case '=':
OPTR.pop();
item = source[i++];
break;
}
}
}
return OPND.top();
} //依次取值的二进制值加1
void IncreaseVarValue(char(&v)[26], int& flag) {
int m = 1; int n = VarList.length();
for (int j = n - 1; j > -1; j--) {
int temp;
temp = int(v[j]) - 48;
flag = flag + temp;
if (flag == 2) {
v[j] = '0'; flag = 1;
}
else {
v[j] = '1'; flag = 0; break;
}
} } //得到真值表
void TruthTable(string expression,bool * &truthTab,string *&expressionValueList,string *&trowList,int &CircleNum) {
int m = 1;
int n = VarList.length();
int flag;
char trow[26];//表达式中变元的依次取值
for (int i = 0; i < n; i++) { m *= 2; trow[i] = '0'; }
string* expressionValueList_IN = new string[m];
string* trowList_IN = new string[m];
bool *truthtable_IN=new bool[m];//真值表中的值
CircleNum = m; //转换成用0或1表示的表达式
for (int i = 0; i < m; i++) {
string value1 = expression;//因为公式是字符串无法直接计算,所以定义value1,将里面的命题变元变成0或1
//使表达式的变元用0或1表示
for (int j = 0; j < n; j++) {
char x = VarList[j];
for (int k = 0; k < expression.length(); k++) {
char a = value1[k];
if (value1[k] == x)
value1[k] =trow[j];
} }
trowList_IN[i] = trow;
expressionValueList_IN[i] = value1;
truthtable_IN[i] = InfixValue(value1);//将得出来的值依次给truthtable
flag = 1;
IncreaseVarValue(trow,flag);
}
truthTab = truthtable_IN;
expressionValueList = expressionValueList_IN;
trowList = trowList_IN;
} //输出真值表
void PrintTable(string expression) {
string* expressionValueList;//用来保存所有的表达式
string* trowList;//保存变元的所有取值
bool* truthtab;//保存所有的表达式的值
int CircleNum;//循环次数
createVarList(expression);
TruthTable(expression, truthtab, expressionValueList, trowList,CircleNum);
//打印真值表
for (int i = 0; i < VarList.length(); i++) {
cout << VarList[i] << "\t";
}
cout << expression <<"\t" <<"值"<<endl; for (int i = 0; i < CircleNum; i++) {
for (int j = 0; j < VarList.length(); j++) {
cout << trowList[i][j] << "\t";
}
cout << expressionValueList[i] << "\t" << truthtab[i] << endl;;
}
}
void main() {
system("pause");
while (true) {
cout << "*****************************************************" << endl;
cout << "*** +表示等价 ***" << endl;
cout << "*** -表示蕴涵 ***" << endl;
cout << "*** &表示合取 ***" << endl;
cout << "*** |表示析取 ***" << endl;
cout << "*** 注:必须用#结束 ***" << endl;
cout << "*** 输入1退出程序 ***" << endl;
cout << "*****************************************************" << endl;
cout << "请输入命题公式:";
string expression;
cin >> expression;
if (expression == "1") {
break;
}
//如果没有#
if (expression.find('#') != expression.length() - 1)
{
cout << "请以#结束!" << endl;;
system("pause");
system("cls");
continue;
}
PrintTable(expression);
system("pause");
system("cls");
} }

C++实现求离散数学命题公式的真值表的更多相关文章

  1. 构造命题公式的真值表--biaobiao88

    对给出的任意一个命题公式(不超过四个命题变元),使学生会用C语言的程序编程表示出来,并且能够计算它在各组真值指派下所应有的真值,画出其真值表. #include<iostream> usi ...

  2. 求pi 的公式

    pi = 3.1415926..... 下面用c 语言来求解PI 现有公式 (pi*pi)/6 = 1 + 1/(2*2) + 1/(3*3) + ... + 1/(n*n); #include &l ...

  3. POJ 3047 Bovine Birthday 日期定周求 泽勒公式

    标题来源:POJ 3047 Bovine Birthday 意甲冠军:.. . 思考:式 适合于1582年(中国明朝万历十年)10月15日之后的情形 公式 w = y + y/4 + c/4 - 2* ...

  4. Excel公式-求最低价网站名字

    p{ font-size: 15px; } .alexrootdiv>div{ background: #eeeeee; border: 1px solid #aaa; width: 99%; ...

  5. NewtonPrincipia_物体的运动_求向心力

    NewtonPrincipia_物体的运动_求向心力 让我们看一下十七世纪的被苹果砸中的艾萨克,是怎样推导出向心力公式的 在现在的观点看来,其中涉及到的很多没有符号表示的微分量.下面的内容只是叙述了推 ...

  6. 大数求模 sicily 1020

        Search

  7. fzu 1330:Center of Gravity(计算几何,求扇形重心)

    Problem 1330 Center of Gravity Accept: 443    Submit: 830Time Limit: 1000 mSec    Memory Limit : 327 ...

  8. Harmonic Number 求Hn; Hn = 1 + 1/2 + 1/3 + ... + 1/n; (n<=1e8) T<=1e4; 精确到1e-8; 打表或者调和级数

    /** 题目:Harmonic Number 链接:https://vjudge.net/contest/154246#problem/I 题意:求Hn: Hn = 1 + 1/2 + 1/3 + . ...

  9. HDU 5159 Card (概率求期望)

    B - Card Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Sta ...

随机推荐

  1. 使用vscode编辑和提交github仓库代码

    写在前面 在github上想删除仓库中的某个文件或文件夹,亦或是重命名操作都很麻烦,这里提供一种vscode的解决方案.在vscode中克隆远程github仓库,然后对代码或文件进行编辑,最后提交即可 ...

  2. Git+Gitlab+Ansible的roles实现一键部署Nginx静态网站(4)

    前言 截止目前已经写了<Ansible基础认识及安装使用详解(一)–技术流ken>,<Ansible常用模块介绍及使用(二)–技术流ken><Ansible剧本介绍及使用 ...

  3. 《Java从入门到失业》第四章:类和对象(4.3):一个完整的例子带你深入类和对象

    4.3一个完整的例子带你深入类和对象 到此为止,我们基本掌握了类和对象的基础知识,并且还学会了String类的基本使用,下面我想用一个实际的小例子,逐步来讨论类和对象的一些其他知识点. 4.3.1需求 ...

  4. netty字节分包

    高并发压测时,发现来自网关的消息出现粘包现象:分包就是势在必行的 前置和处理平台(暂时)使用netty通话,由于都是服务器平台使用DelimiterBasedFrameDecoder来解决分包 和网关 ...

  5. google protocol buffer——protobuf的问题和改进2

    这一系列文章主要是对protocol buffer这种编码格式的使用方式.特点.使用技巧进行说明,并在原生protobuf的基础上进行扩展和优化,使得它能更好地为我们服务. 在上一篇文章中,我们举例了 ...

  6. 3.Channel详解

  7. SSH登录问题 .ssh/known_hosts和 ECDSA key

    问题描述: fakerdeMacBook-Pro:~ faker$ ssh root@123.***.***.34 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...

  8. 面经手册 · 第12篇《面试官,ThreadLocal 你要这么问,我就挂了!》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 说到底,你真的会造火箭吗? 常说面试造火箭,入职拧螺丝.但你真的有造火箭的本事吗,大 ...

  9. jpeg编解码概述

    本博文为概览性介绍.后面有空了再分几篇博文分别介绍所用到的技术细节. 1.编解码目标 编码和解码是个逆过程.jpeg编码的目的在于图形去冗余,进行数据压缩,解码的目的在于还原图像,使能够进行预览. 2 ...

  10. safari 浏览器版本升级后提示“此网页出现问题,已重新载入网页” 解决办法

    safari回退条件 版本回退的前提是关闭电脑的SIP机制,命令行 csrutil status 检测状态.Mac os 10.14以下版本回退Safari后插件还是可以用的,升了新系统退了也没法用了 ...