求命题公式的真值表及主范式(栈实现)

设计思路

首先用一个串来储存使用者所输入的命题表达式,依据人类的思维,会对输入的命题公式进行分析和计算,但计算机可不会,这时我们就要写入相关代码把使用者输入的命题表达式转换为计算机所能进行计算的后缀表达式,我们用计算机进行对数字相关运算时也是这个原理!首先用一个字符串来储存遍历命题表达式所遇到的命题变元,之后遇到操作符时就利用开发者根据C++运算符优先级自定义的操作符优先级函数对操作符的优先级比较来判断操作符是否要压入创建的字符栈,等这些工作都做完了,最终字符栈内储存的就是操作符,而字符串储存的都是变元,然后将字符栈内的操作符全部出栈,储存到字符串中。这样字符串里储存的就是命题公式的后缀表达式,之后要对后缀表达式进行计算!
例:
系统使用者输入的命题表达式为:!p&q|r
经过转化,后缀表达式为:pqr|&!
如果要想计算表达式,首先遍历后缀表达式,然后遇到操作符时,根据它是几元运算符,来对前面的变元进行逻辑运算,比如这里首先遍历到的是|,由于他是二元运算符,这时进行的运算是q|r,那么问题来了,如果把运算符直接出栈进行运算,这时进行的运算就是p|q,这时不符合运算要求的!
所以这时候我们根据栈先进后出的特性,再建造一个数字栈,首先进行储存的是各个变元的真值,这时候可以发现他就解决了前面逻辑运算遇到的问题,之后再根据遇到的操作符类型进行逻辑运算,边出栈边压栈,最终经过计算,数字栈储存的就剩一个数字,这个数字就是该命题公式的真值,最后根据命题表达式的真值和主析取范式用于判断真,主合取范式用于判断假的特性来对用数组对命题公式变元真值的各种情况进行储存,最后根据主范式特性来进行主范式的输出。

重要步骤

  • 操作符优先级的比较
  • 中缀表达式转后缀表达式
  • 字符栈内操作符的储存
  • 数字栈内真值情况的储存
  • 逻辑运算的进行
  • 主范式的输出

实现代码

#include<iostream>
#include<cctype>
#include<set>
#include<string>
#include<stack>
using namespace std;
#define Max 1000
int Array_one[Max][3]={0},Array_two[Max][3]={0},Counter_one=0,Counter_two=0;//储存各个变元的真值,计数器
string s;//串s,储存命题公式
stack<char> Stack;//创建字符栈
stack<int> Num;//创建数字栈
char save[Max],c;//用于储存后缀表达式和临时字符
int follow=0;//计数器
int p,q,r;//用于储存真值
int Amp();//操作符优先级比较函数
void Transform();//中缀表达式转后缀表达式
void fei();//非运算
void yu();//与运算
void huo();//或运算
void tiaojian();//条件运算
void Shuangtiaojian();//双条件运算
void Truenum();//真值运算
void Output_xiqu();//输出主析取式
void Output_hequ();//输出主合取式
int main()
{
	cout<<"------------------------------------------------欢迎您进入三元逻辑运算系统---------------------------------------------"<<endl;
	cout<<"变元为p,q,r"<<endl;
	cout<<"! 非运算,& 与运算,| 或运算,> 条件运算,$ 双条件运算,( 左括号,) 右括号"<<endl;
	cout<<"请输入命题公式!"<<endl;
	cin>>s;
	Transform();
	cout<<"\t\t\t**真值表**"<<endl;
	cout<<"p\t\t"<<"q\t\t"<<"r\t\t"<<s<<endl;
	Truenum();
	Output_hequ();
	Output_xiqu();
	cout<<endl;
	cout<<"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$第一小组制作$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"<<endl;
	cout<<"小组成员:"<<endl;
	cout<<"$张凯袁$\t"<<"$郭子晗$\t"<<"$王政耀$\t"<<"$彭宪超$\t"<<"$蔺雨菲$\t"<<endl;
	cout<<"系统创造者————————————☆☆☆郭子晗☆☆☆"<<endl;
	return 0;
}
int Amp(char a) {
	switch(a)
	{
		//由逻辑运算符优先级得到
		case '(':return 5;break;
		case '!':return 4;break;
		case '&':return 3;break;
		case '|':return 2;break;
		case '>':return 1;break;
		case '$':return 0;break;
		case ')':return -1;break;
	}
}
void Transform() {
	for(int i=0;i<s.length();i++) {//遍历命题公式
		c=s[i];//临时储存字符
		if(isalpha(c))//如果为字母,后缀表达式直接储存
			save[follow++]=c;
		if(c=='!'||c=='&'||c=='|'||c=='>'||c=='$'||c=='('||c==')') {//当为操作符时
			if(Stack.empty()) Stack.push(c);//字符栈为空,直接压入
			else if(c!=')') Stack.push(c);//当遍历未读取到右括号时,操作符直接入栈
			else if(c==')') {//当读取到右括号时
				while(!Stack.empty()&&Amp(Stack.top())>Amp(c))//当字符栈不为空且栈内操作符优先级大于站外优先级时
				{
					if(Stack.top()=='(') break;//读取到左括号时,直接跳出循环
					save[follow++]=Stack.top();//栈顶操作符储存到后缀表达式中
					Stack.pop();//操作符出栈
				}
				if(Stack.top()=='(') Stack.pop();//当栈顶操作符为左括号时,直接出栈
			}
		}
	}
	while(!Stack.empty())//输出全部栈内操作符存储到后缀表达式中
	{
		save[follow++]=Stack.top();
		Stack.pop();
	}
}
void Truenum() {
	for(p=0;p<=1;p++) {
		for(q=0;q<=1;q++) {
			for(r=0;r<=1;r++) {
				cout<<p<<"\t\t"<<q<<"\t\t"<<r<<"\t\t";
				for(int i=0;i<follow;i++) {
					c=save[i];//临时存储字符
					if(isalpha(c)) {//当为字母变元时,转换为对应的真值
						switch(c)
						{
							//真值压入数字栈
							case 'p':Num.push(p); break;
							case 'q':Num.push(q); break;
							case 'r':Num.push(r); break;
						}
					}
					else if(c=='!') fei();
					else if(c=='&') yu();
					else if(c=='|') huo();
					else if(c=='>') tiaojian();
					else if(c=='$')	Shuangtiaojian();
				}
				cout<<Num.top()<<endl;
			if(Num.top()==1) {//由主析取范式特性进行判断
				Array_one[Counter_one][0]=p;
				Array_one[Counter_one][1]=q;
				Array_one[Counter_one++][2]=r;
			}
			else{//由主合取范式特性进行判断
				Array_two[Counter_two][0]=p;
				Array_two[Counter_two][1]=q;
				Array_two[Counter_two++][2]=r;
			}
			Num.pop();//最终的命题公式真值出栈
			}
		}
	}
}
void Output_xiqu() {
	cout<<"主析取式"<<endl;
	for(int i=0;i<Counter_one;i++) {
		cout<<"(";
		if(Array_one[i][0]==0) cout<<"(!p)";
		else {cout<<"p";}
		if(Array_one[i][1]==0) cout<<"&(!q)";
			else {cout<<"&q";}
		if(Array_one[i][2]==0) cout<<"&(!r)";
		else {cout<<"&r";}
		if(i==Counter_one-1) cout<<")";
		else {cout<<")|";}
	}
	cout<<endl;
}
void Output_hequ() {
	cout<<"主合取式"<<endl;
	for(int i=0;i<Counter_two;i++) {
		cout<<"(";
		if(Array_two[i][0]==0) cout<<"p";
		else {cout<<"(!p)";}
		if(Array_two[i][1]==0) cout<<"|q";
		else {cout<<"|(!q)";}
		if(Array_two[i][2]==0) cout<<"|r";
		else {cout<<"|(!r)";}
		if(i==Counter_two-1) cout<<")";
		else {cout<<")&";}
	}
	cout<<endl;
}
void fei() {//一元操作符,!expr
	int True=!Num.top(); Num.pop();//弹出一个真值,并出栈
	Num.push(True);//操作结果压栈
}
void yu() {//二元操作符,Expr&&expr
	int True_one=Num.top(); Num.pop();//弹出两个真值,并出栈
	int True_two=Num.top(); Num.pop();
	int True=True_one&&True_two;
	Num.push(True);//操作结果压栈
}
void huo() {//二元操作符,Expr||expr
	int True_one=Num.top(); Num.pop();
	int True_two=Num.top(); Num.pop();
	int True=True_one||True_two;
	Num.push(True);
}
void tiaojian() {//二元操作符,Expr->expr
	int True_one=Num.top(); Num.pop();
	int True_two=Num.top(); Num.pop();
	int True;
	if(True_one==0&&True_two==1) True=0;
	else True=1;
	Num.push(True);
}
void Shuangtiaojian() {//二元操作符,Expr<->expr
	int True_one=Num.top(); Num.pop();
	int True_two=Num.top(); Num.pop();
	int True;
	if(True_one==True_two) True=1;
	else True=0;
	Num.push(True);
}

运行测试

在这里插入图片描述

您可以通过编写一个程序来实现利用真值表任意一个命题公式的主范式的功能。具体的实现方式可以参考以下代码示例: ```c #include <stdio.h> #include <stdbool.h> #include <stdlib.h> int main() { // 输入命题公式的变量个数 int n; printf("请输入命题公式的变量个数:"); scanf("%d", &n); // 根据变量个数生成真值表 int rows = 1 << n; // 真值表的行数 bool **table = (bool **)malloc(rows * sizeof(bool *)); for (int i = 0; i < rows; i++) { table[i] = (bool *)malloc(n * sizeof(bool)); for (int j = 0; j < n; j++) { table[i][j] = (i >> (n - j - 1)) & 1; // 生成第 i 行的值 } } // 输入命题公式真值表 bool *result = (bool *)malloc(rows * sizeof(bool)); printf("请输入命题公式真值表:\n"); for (int i = 0; i < rows; i++) { scanf("%d", &result[i]); } // 范式 bool *prime_implicants = (bool *)malloc(rows * sizeof(bool)); for (int i = 0; i < rows; i++) { prime_implicants[i] = false; // 初始化为不是主因子 } for (int i = 0; i < rows; i++) { if (result[i]) { // 如果结果为真 prime_implicants[i] = true; // 标记为主因子 for (int j = i + 1; j < rows; j++) { bool is_implicant = true; // 判断是否为主因子 for (int k = 0; k < n; k++) { if (table[i][k] != table[j][k] && table[i][k] != 2 && table[j][k] != 2) { is_implicant = false; break; } } if (is_implicant) { prime_implicants[j] = true; // 标记为主因子 } } } } // 输出主范式 printf("命题公式的主范式为:"); for (int i = 0; i < rows; i++) { if (prime_implicants[i]) { printf("("); for (int j = 0; j < n; j++) { if (table[i][j]) { printf("%c", 'A' + j); } else { printf("!%c", 'A' + j); } if (j != n - 1) { printf("&&"); } } printf(")"); if (i != rows - 1) { printf("||"); } } } printf("\n"); // 释放内存 for (int i = 0; i < rows; i++) { free(table[i]); } free(table); free(result); free(prime_implicants); return 0; } ``` 在上面的代码中,我们使用了动态内存分配来生成真值表并存储命题公式真值表。然后,我们根据命题公式真值表出主范式,并输出结果。需要注意的是,由于主范式可能包含多个项,因此我们使用 `||` 运算符进行连接。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无所畏惧的man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值