EXPPP

真值表法求取主析取范式及主合取范式的实现

+++

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 << ')';
			}
		}
	}
}
原文地址:https://www.cnblogs.com/scl0725/p/12698170.html