1.实验目的
通过实验理解命题、命题公式及真值表的概念和正确表示方法,掌握范式的定义,学会利用真值表求出命题的主析取范式和主合取方式。能够利用真值表判断命题是否为永真式、永假式,判断两个命题是否是蕴含的或等价的。在此基础上,实现对命题推理的正确性进行判断,解决一些实际推理问题。
2.实验要求
(1) 测试用例要包含以下多种情况 :
(a) 对命题表达式正确性的判断
(b) 包含5个逻辑联结词
(c) 根据真值表求出命题的主析取范式和主合取方式
(d) 输入两个命题公式,判断其逻辑关系
(e) 输入一个逻辑推理式子,判断其正确性。
(2) 实验报告中给出算法设计思想及流程图
(3) 给出测试结果截图
(4) 写出实验总结、分析及心得
3.实验内容
输入:
命题公式
输出:
(1) 命题真值表
(2) 命题的类别
(3) 命题的主析取范式与主合取范式
(4) 两个命题公式的逻辑关系
(5) 判断命题推理的正确性
4.代码:
#include<iostream>
#include<math.h>
#include<string.h>
#include<stack>
#include<vector>
using namespace std;
int sum=2;
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中<br>//比如 (!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;
if(truthtab[i])sum++;
}
if(sum==CircleNum+2)sum=1;
if(sum==2)sum=0;
}
string CategorizeProposition(const string& proposition) {
// 判断命题是否是等价命题
bool isEquivalent = false;
for (char ch : proposition) {
if (ch == '+') {
isEquivalent = true;
break;
}
}
// 判断命题是否是蕴涵命题
bool isImplication = proposition.find('-') != string::npos;
// 判断命题是否是合取命题
bool isConjunction = proposition.find('&') != string::npos;
// 判断命题是否是析取命题
bool isDisjunction = proposition.find('|') != string::npos;
// 判断命题是否是否定命题
bool isNegation = proposition.find('!') != string::npos;
string category = "";
if (isEquivalent) category += "等价命题 ";
if (isImplication) category += "蕴涵命题 ";
if (isConjunction) category += "合取命题 ";
if (isDisjunction) category += "析取命题 ";
if (isNegation) category += "否定命题 ";
return category;
}
// 命题的主析取范式(DNF)
string GetDNF(const string& proposition) {
// 分割命题公式中的子句
vector<string> clauses;
string clause = "";
for (char ch : proposition) {
if (ch == '|') {
clauses.push_back(clause);
clause = "";
} else {
clause += ch;
}
}
clauses.push_back(clause);
// 构建主析取范式
string dnf = "";
for (const string& c : clauses) {
dnf += "(" + c + ") & ";
}
dnf.erase(dnf.length() - 3); // 移除最后的 "& "
return dnf;
}
// 命题的主合取范式(CNF)
string GetCNF(const string& proposition) {
// 分割命题公式中的子句
vector<string> clauses;
string clause = "";
for (char ch : proposition) {
if (ch == '&') {
clauses.push_back(clause);
clause = "";
} else {
clause += ch;
}
}
clauses.push_back(clause);
// 构建主合取范式
string cnf = "";
for (const string& c : clauses) {
cnf += "(" + c + ") | ";
}
cnf.erase(cnf.length() - 3); // 移除最后的 "| "
return cnf;
}
int main() {
while (true) {
cout << "*****************************************************" << endl;
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);
if(sum==1)cout<<"\n"<<"这个表达式是永真"<<endl;
else if(sum==0) cout<<"\n"<<"这个表达式是永假"<<endl;
else cout<<"\n"<<"这个命题不是永真式也不是永假式"<<endl;
string category = CategorizeProposition(expression);
cout << "命题类型:" << category << endl;
expression.pop_back();
string dnf = GetDNF(expression);
cout << "主析取范式(DNF):" << dnf << endl;
string cnf = GetCNF(expression);
cout << "主合取范式(CNF):" << cnf << endl;
system("pause");
system("cls");
}
}