真值表法求取主析取范式及主合取范式的实现
+++
1.需要了解什么
1.栈(stack)
-
怎么理解栈: 想象有一个圆桶,可以往桶里放东西。如果想要从桶里拿东西,只能拿出桶最上面的东西,因为之前放进去的东西在下面,拿不到。这也是栈这个容器最重要的性质:先进去的元素后出来。
-
本次实验为什么要用到栈? 我们平常碰到的式子,运算符在数字中间的,被叫做中缀表达式,我们计算时可以按照优先级情况,先算优先级高的再算优先级低的。但是计算机不可以做到挑选这个动作,只会机械化的顺序遍历整个式子。所以本实验我们要把中缀表达式通过栈来转化为后缀表达式(计算机方便阅读的式子形式)。
-
https://blog.csdn.net/walkerkalr/article/details/22798365 中缀转后缀方法(转载)
-
https://blog.csdn.net/xiaoniba1024/article/details/6575523 后缀表达式求值(转载)
#include <stack>//要有这个头文件
stack<int> s;//一个盛放int的栈,刚创建是栈是空的
s.empty();//判断栈s是否为空,是空的返回1,否则返回0
s.top();//栈顶元素,可以理解成圆桶最上面的元素
s.pop();//删除栈顶元素,可以理解成拿走圆桶最上面的元素
int m; s.push(m);//元素m入栈,而且在栈顶,也就是把元素m放到圆桶里,并且位于桶内元素的最上面
2.映射(map)
-
什么是映射? 举个简单的例子,我们每个人都有名字,也都有学号,通过学号我们可以知道是哪个人。这就是最简单的映射,把名字映射成学号,然后通过学号就可以知道人的名字。
在c++中,我们可以将任意两种数据类型进行映射,比如:map<char, int> m;m就是字符型和整
-
本实验为什么要用到映射? 因为运算符(非,合取,析取,单条件,双条件)是有优先级的,直接比较字符的优先级有点麻烦,但是比较两个int的大小时就很简单,所以我们把char(运算符)映射成int,然后通过比较int的大小来判断运算符优先级。
#include <map>//要用到的头文件
//我们规定数字大的优先级高
map<char, int> m = { { '!', 5 },{ '&', 4 },{ '|', 3 },{ '>', 2 },{ '=', 1 } };//通过映射来标明运算符优先级
//我们可以把char看成是m的下标来访问对应的int
//如何访问map
cout << m['!'];//输出结果是5
string s = "!";
cout << m[s[0]];//输出结果是5(此时不用单引号)
3.集合(set)
-
集合set的性质 : 有序(自动排序),唯一性(每种元素都不重复),如果向set中加入一个已有的元素,集合不会发生变化。
-
本实验为什么要用到set? 因为命题变元名字和个数是不确定的,而且范式要求命题变元按照字典序输出,所以我们要通过set来收集命题变元并且按照字典序排列(方便使用)。
#include <set>//要用到的头文件
set<char> s;//盛放char元素的集合
//用到的函数
char c;
s.insert(c);//字符c进入集合
+++
2.函数各部分代码
-
主函数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
//有的头文件可能没用带,习惯了就都打上去了
using namespace std;
map<char, int> m = { { '!', 5 },{ '&', 4 },{ '|', 3 },{ '>', 2 },{ '=', 1 } };//通过映射来标明运算符优先级
map<char, int> mm;//存放命题变元的映射
stack<char> temp;//临时栈,处理符号入栈优先次序
string Nocal_Suffix_Formula;//未计算前的后缀表达式
set<char> Set;
string formula;//存放输入
int num;//命题变元个数
string element;//存放命题变元
//多开一列是为了标记,如果真值情况为真则在最后一列标记为true
bool st[260][9];//存放不同情况下的真值情况
int Dfs_Cnt(int n);//返回所有情况的个数
void In_Stack(string);//中缀转后缀,入栈
void Dfs_Process();//运算后缀表达式
void Print_conjunction();//打印主合取范式
void Print_extraction();//打印主析取范式
int main()
{
cout << "支持格式: '!' 非 -- '&' 合取 -- '|' 析取 -- '>' 单条件 '=' 双条件" << endl;
cout << "请输入命题变元个数 (字母大写): " << endl;
cin >> num;
cout << "Input your formula : " << endl;
cin >> formula;
//中缀表达式(输入的表达式)转后缀表达式(通过栈实现)
In_Stack(formula);
//运算后缀表达式的值(计算真值表最后一列的真值情况)
Dfs_Process();
//计算主析取范式并输出
cout << "主析取范式: " << endl;
Print_extraction();
cout << endl;
//计算主合取范式并输出
cout << "主合取范式: " << endl;
Print_conjunction();
cout << endl;
system("pause");
return 0;
}
-
int Dfs_Cnt(int n);//返回所有情况的个数
int Dfs_Cnt(int n)
{
return 1 << n;//本句话是计算2的n次方,位运算我之后教你
}
-
void In_Stack(string);//中缀转后缀,入栈
void In_Stack(string s)
{
for (unsigned int i = 0; i < s.size(); i++)
{
char x = s[i];
judge_again://goto语句
if (x >= 65 && x <= 90)
{
Nocal_Suffix_Formula += x;
Set.insert(x);
}
else if (x != '(' && x != ')')
{
if (temp.empty() || temp.top() == '(') temp.push(x);
else if (m[x] > m[temp.top()]) temp.push(x);
else(m[x] <= m[temp.top()] && !temp.empty())
{
Nocal_Suffix_Formula += temp.top();
temp.pop();
goto judge_again;//goto语句
}
}
else
{
if (x == '(') temp.push(x);
else
{
while (temp.top() != '(')
{
Nocal_Suffix_Formula += temp.top();
temp.pop();
}
temp.pop();
}
}
}
while (!temp.empty())//最后把栈中剩余符号压入后缀表达式
{
Nocal_Suffix_Formula += temp.top();
temp.pop();
}
}
-
void Dfs_Process();//运算后缀表达式
这里有一点你可能不懂,看到这里我打电话教你
void Dfs_Process()
{
int i = 0;
for (auto x : Set)
{
element += x;//从集合中读取命题变元到element中
mm.insert({ x, i++ });
}
for (int op = 0; op < Dfs_Cnt(num); op++)//所有情况
{
for (int i = 0; i < num; i++) st[op][i] = (op >> i & 1);//位运算取不同真值情况
stack<char> Cal_Suffix_Formula;//计算过的后缀表达式,只有一个元素且栈顶为'0' or '1'
for (unsigned int i = 0; i < Nocal_Suffix_Formula.size(); i++)
{
char t = Nocal_Suffix_Formula[i];
if (t >= 65 && t <= 90)//如果是命题变元,赋值后入栈
{
int j = st[op][mm[t]];
Cal_Suffix_Formula.push(j);
}
else if (t == '!')//非运算
{
Cal_Suffix_Formula.top() ^= 1;
}
else
{
int r = Cal_Suffix_Formula.top();
Cal_Suffix_Formula.pop();
int l = Cal_Suffix_Formula.top();
Cal_Suffix_Formula.pop();
if (t == '&')//合取运算
{
if (l + r == 2) Cal_Suffix_Formula.push(1);
else Cal_Suffix_Formula.push(0);
}
else if (t == '|')//析取运算
{
if (l + r == 0) Cal_Suffix_Formula.push(0);
else Cal_Suffix_Formula.push(1);
}
else if (t == '>')//单条件
{
if (l == 1 && r == 0) Cal_Suffix_Formula.push(0);
else Cal_Suffix_Formula.push(1);
}
else//双条件
{
if (l == r) Cal_Suffix_Formula.push(1);
else Cal_Suffix_Formula.push(0);
}
}
}
st[op][num] = Cal_Suffix_Formula.top();
}
}
-
void Print_conjunction();//打印主合取范式
变量flag1和flag2的作用是因为n个命题变元中间有n-1个运算符,用来控制输出格式的
void Print_conjunction()
{
int flag1 = 1;
for (int i = 0; i < Dfs_Cnt(num); i++)//所有情况
{
if (!st[i][num])//如果真值情况为假
{
if (flag1)
{
flag1 = 0;
cout << '(';
int flag2 = 1;
for (int j = 0; j < num; j++)//变元真值情况
{
if (flag2)
{
flag2 = 0;
if (st[i][j]) cout << '!';
cout << element[j];
continue;
}
cout << '|';
if (st[i][j]) cout << '!';
cout << element[j];
}
cout << ')';
}
else
{
cout << "&(";
int flag2 = 1;
for (int j = 0; j < num; j++)//变元真值情况
{
if (flag2)
{
flag2 = 0;
if (st[i][j]) cout << '!';
cout << element[j];
continue;
}
cout << '|';
if (st[i][j]) cout << '!';
cout << element[j];
}
cout << ')';
}
}
}
}
-
void Print_extraction();//打印主析取范式
变量flag1和flag2的作用是因为n个命题变元中间有n-1个运算符,用来控制输出格式的
void Print_extraction()
{
int flag1 = 1;
for (int i = 0; i < Dfs_Cnt(num); i++)//所有情况
{
if (st[i][num])//如果真值情况为真
{
if (flag1)
{
flag1 = 0;
cout << '(';
int flag2 = 1;
for (int j = 0; j < num; j ++ )//变元真值情况
{
if (flag2)
{
flag2 = 0;
if (!st[i][j]) cout << '!';
cout << element[j];
continue;
}
cout << '&';
if (!st[i][j]) cout << '!';
cout << element[j];
}
cout << ')';
}
else
{
cout << "|(";
int flag2 = 1;
for (int j = 0; j < num; j++)//变元真值情况
{
if (flag2)
{
flag2 = 0;
if (!st[i][j]) cout << '!';
cout << element[j];
continue;
}
cout << '&';
if (!st[i][j]) cout << '!';
cout << element[j];
}
cout << ')';
}
}
}
}