用 C 语言实现逻辑表达式求主析取范式和主合取范式

 

算法概述

我们首先定义了两种栈数据结构 OPNDOPTR,分别用于存储操作数和操作符。接下来,我们定义了一些基本操作,如栈的初始化、入栈、出栈等。然后,我们定义了一个结构体 Variable 来存储变量和对应的值,并实现了一些相关函数。之后,我们使用位运算生成了逻辑表达式的赋值表,然后通过实现算法来求解逻辑表达式的真值。

代码详解

我们逐行分析了代码的实现过程,包括栈的初始化、入栈、出栈等基本操作,以及如何生成逻辑表达式的赋值表和求解逻辑表达式的真值。我们特别介绍了逻辑运算的实现和如何得到主析取范式和主合取范式。

#define _CRT_SECURE_NO_WARNINGS // 忽略 Visual Studio 中的特定警告

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAXSIZE 100 // 定义栈的最大大小

// 定义操作数栈结构体
typedef struct {
	int* base; // 栈底指针
	int* top;  // 栈顶指针
	int stacksize; // 栈的容量
}OPND;

// 定义操作符栈结构体
typedef struct {
	char* base; // 栈底指针
	char* top;  // 栈顶指针
	int stacksize; // 栈的容量
}OPTR;

// 定义结构体用于存储变量和对应的值
typedef struct {
	char var;  // 变量名
	int value; // 变量值
} Variable;

// 初始化操作数栈
void initStack_ND(OPND& OP) {
	OP.base = (int*)malloc(sizeof(int) * MAXSIZE);
	OP.top = OP.base;
	OP.stacksize = MAXSIZE;
}

// 初始化操作符栈
void initStack_TR(OPTR& OP) {
	OP.base = (char*)malloc(sizeof(char) * MAXSIZE);
	OP.top = OP.base;
	OP.stacksize = MAXSIZE;
}

// 获取操作数栈顶元素
int GetTop_ND(OPND& OP) {
	if (OP.base == OP.top) {
		return 0; // 栈空返回0
	}
	return *(OP.top - 1);
}

// 获取操作符栈顶元素
char GetTop_TR(OPTR& OP) {
	if (OP.base == OP.top) {
		return ' '; // 栈空返回空字符
	}
	return *(OP.top - 1);
}

// 操作数入栈
void Push_ND(OPND& OP, int e) {
	if (OP.top - OP.base == OP.stacksize) {
		return; // 栈满直接返回
	}
	*OP.top = e;
	OP.top++;
}

// 操作符入栈
void Push_TR(OPTR& OP, char e) {
	if (OP.top - OP.base == OP.stacksize) {
		return; // 栈满直接返回
	}
	*OP.top = e;
	OP.top++;
}

// 操作数出栈
void Pop_ND(OPND& OP, int* e) {
	if (OP.base == OP.top) {
		return; // 栈空直接返回
	}
	OP.top--;
	*e = *OP.top;
}

// 操作符出栈
void Pop_TR(OPTR& OP, char* e) {
	if (OP.base == OP.top) {
		return; // 栈空直接返回
	}
	OP.top--;
	*e = *OP.top;
}

// 判断字符是否为变量
int isVar(char ch) {
	if (ch >= 'a' && ch <= 'z') {
		return 1; // 是变量返回1
	}
	else {
		return 0; // 否则返回0
	}
}

// 在字符数组中查找字符是否存在
int findVar(char* var, char v) {
	int count = sizeof(var) / sizeof(char);
	for (int i = 0; i < count; i++)
	{
		if (var[i] == v) {
			return 1; // 如果存在返回1
		}
	}
	return 0; // 否则返回0
}

// 冒泡排序,用于排序变量数组,将变量从a-z顺序进行排列
void sort(char* arr, int n) {
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (arr[j] > arr[j + 1]) {
				char tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

// 初始化二维数组,生成逻辑表达式的赋值表
/*
*例如 变量:p q 
* 赋值情况:0 0
*           0 1
*           1 0
*           1 1
* 是0到(1 << count)即2的count(变量的个数)次方的二进制的每位;
* 所以可以定义二维数组存储所有的赋值情况,再以每行给p q赋值
*/
void initArr(int** assignArr, int count) {
	// 生成赋值表的每行
	for (int i = 0; i < (1 << count); i++) {
		assignArr[i] = (int*)malloc(count * sizeof(int)); // 分配内存
		if (assignArr[i] == NULL) {
			printf("内存分配失败\n"); // 内存分配失败提示并退出程序
			for (int j = 0; j < i; j++) {
				free(assignArr[j]);
			}
			free(assignArr);
			exit(0);
		}
		// 使用位运算生成二进制数并存储在二维数组中
		for (int j = 0; j < count; j++) {
			assignArr[i][j] = (i >> (count - 1 - j)) & 1;
		}
	}
}

// 初始化操作符数组,用于存储操作符及其优先级
void initOperatorArr(Variable* operators) {
	// 定义各个操作符及其优先级
	operators[0].var = '!'; // 非运算
	operators[0].value = 2;

	operators[1].var = '&'; // 与运算
	operators[1].value = 1;

	operators[2].var = '|'; // 或运算
	operators[2].value = 1;

	operators[3].var = '-'; // 蕴含
	operators[3].value = 1;

	operators[4].var = '='; // 等价
	operators[4].value = 1;

	operators[5].var = '('; // 左括号
	operators[5].value = 3;

	operators[6].var = ')'; // 右括号
	operators[6].value = 0;

	operators[7].var = '#'; // 开始结束标志
	operators[7].value = 0;
}

// 根据变量名查找其对应的值
int findValueByVar(Variable* variables, char var, int count) {
	for (int i = 0; i < count; i++)
	{
		if (variables[i].var == var) {
			return variables[i].value; // 找到变量返回其值
		}
	}
	return -1; // 没找到返回-1
}

// 打印每组赋值及真值
void printArr(int* arr, int result, int count) {
	for (int i = 0; i < count; i++) {
		printf("%d ", arr[i]); // 打印每个变量的赋值
	}
	printf("    %d", result); // 打印真值
	printf("\n");
}

// 为所有变量赋值
void assignValues(char* var, Variable* variables, int* assignArr, int count) {
	for (int i = 0; i < count; i++)
	{
		variables[i].var = var[i]; // 将变量名赋值给变量数组
		variables[i].value = assignArr[i]; // 将赋值表中的值赋给变量数组中的值
	}
}

// 计算操作数栈顶的两个操作数及操作符的结果
int compute(int num1, int num2, char sign) {
	switch (sign)
	{
	case '&':
		return num1 & num2; // 与运算
	case '|':
		return num1 | num2; // 或运算
	case '-':
		return (num1 - num2 != 1); // 蕴含,只有1->0时为0
	case '=':
		return (num1 == num2); // 等于
	default:
		return -1; // 其他情况返回-1,表示错误
	}
}

// 求解逻辑表达式的真值
int evaluateExpression(OPND& OP1, OPTR& OP2, char* expression, Variable* variables, Variable* operators, int count, int len) {
	int n = 0;
	Push_TR(OP2, expression[n++]); // 将开始标志入栈
	while (expression[n] != '#' || GetTop_TR(OP2) != '#') {//当栈只剩开始标志,并且表达式遍历到结束标志时,循环结束
		if (isVar(expression[n])) {//判断是变量还是运算符
			int result = findValueByVar(variables, expression[n], count);//找到变量对应的赋值
			if (result != -1) {
				Push_ND(OP1, result);
				n++;
			}
		}
		else {
			int result1 = findValueByVar(operators, expression[n], 8);
			int result2 = findValueByVar(operators, GetTop_TR(OP2), 8);
			if (result1 > result2) {//判断运算符的优先级
				Push_TR(OP2, expression[n++]);
			}
			else {
				int num1, num2;
				char sign;
				if (expression[n] == ')' && GetTop_TR(OP2) == '(') {
					Pop_TR(OP2, &sign);
					n++;
				}
				else {
					if (GetTop_TR(OP2) == '!') {//处理单目运算符
						Pop_ND(OP1, &num1);
						Pop_TR(OP2, &sign);
						Push_ND(OP1, !num1);

					}
					else if (OP1.top - OP1.base < 2) {//预防类似: !(p-q) 这种情况,出现'-',操作数栈只有p,应将‘-’入运算符栈
						Push_TR(OP2, expression[n++]);
					}
					else {
						Pop_ND(OP1, &num2);
						Pop_ND(OP1, &num1);
						Pop_TR(OP2, &sign);
						int result = compute(num1, num2, sign);
						if (result == -1) {
							printf("表达式运算符错误,请检查后再重新输入\n");
							exit(0);
						}
						Push_ND(OP1, result);
					}

				}
			}

		}
	}
	int result;
	Pop_ND(OP1, &result);
	return result;
}

// 主函数
int main() {
	OPND opnd;
	OPTR optr;
	initStack_ND(opnd); // 初始化操作数栈
	initStack_TR(optr); // 初始化操作符栈
	char expression[100]; // 存储输入的逻辑表达式
	char var[100]; // 存储表达式中的变量
	int count = 0; // 变量数量
	printf("请输入表达式(以#开始以#结束):");
	scanf("%s", expression);
	int len = strlen(expression);
	// 提取表达式中的变量
	for (int i = 0; i < len; i++)
	{
		if (isVar(expression[i])) {
			if (!findVar(var, expression[i])) {
				var[count] = expression[i];
				count++;
			}

		}
	}
	sort(var, count); // 对变量进行排序
	printf("变量\n");
	for (int i = 0; i < count; i++)
	{
		printf("%c ", var[i]); // 打印变量
	}
	printf("    真值\n");
	// 定义变量数组并为其赋值
	Variable* variables = (Variable*)malloc(sizeof(Variable) * count);
	if (!variables) {
		printf("内存分配失败\n");
		exit(0);
	}
	// 分配二维数组内存并初始化
	int** assignArr = (int**)malloc((1 << count) * sizeof(int*));
	if (assignArr == NULL) {
		printf("内存分配失败\n");
		exit(0);
	}
	initArr(assignArr, count);
	Variable operators[8]; // 定义操作符数组
	initOperatorArr(operators); // 初始化操作符数组
	int* record1 = (int*)malloc(sizeof(int) * (1 << count)); // 存储主析取范式结果
	int* record2 = (int*)malloc(sizeof(int) * (1 << count)); // 存储主合取范式结果
	int n1 = 0;
	int n2 = 0;
	for (int i = 0; i < (1 << count); i++)
	{
		assignValues(var, variables, assignArr[i], count); // 为变量数组赋值
		int result = evaluateExpression(opnd, optr, expression, variables, operators, count, len); // 计算逻辑表达式的真值
		if (result == 1) {
			record1[n1++] = i; // 记录主析取范式结果
		}
		else {
			record2[n2++] = i; // 记录主合取范式结果
		}
		printArr(assignArr[i], result, count); // 打印每组赋值及真值
	}
	printf("主析取范式为:");
	for (int i = 0; i < n1; i++)
	{
		if (i == n1 - 1) {
			printf("m%d\n", record1[i]); // 打印主析取范式结果
		}
		else {
			printf("m%d∨", record1[i]);
		}
	}
	printf("主合取范式为:");
	for (int i = 0; i < n2; i++)
	{
		if (i == n2 - 1) {
			printf("M%d\n", record2[i]); // 打印主合取范式结果
		}
		else {
			printf("M%d∧", record2[i]);
		}
	}
	return 0; // 程序结束
}

运行结果展示

结束语

逻辑表达式的求解是计算机科学中的一个重要问题,它在逻辑推理、布尔代数等领域都有着广泛的应用。希望本文对读者理解逻辑表达式求解算法有所帮助,并能够在实际编程中加以应用。

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
主析取范式主合取范式是布尔逻辑中的两种标准形式,分别是由所有项的析取和合取构成的布尔表达式,可以通过以下步骤实现: 1. 主析取范式(SOP):将布尔表达式转化为所有项的析取形式,即将表达式中的每个子项都取反并组合起来,再将整个表达式取反。例如,对于表达式(A and B) or (C and D),可以得到主析取范式为(A' or C' or D') and (B' or C' or D')。 2. 主合取范式(POS):将布尔表达式转化为所有项的合取形式,即将表达式中的每个子项都保留原样并组合起来,再将整个表达式取反。例如,对于表达式(A or B) and (C or D),可以得到主合取范式为(A' and C') or (A' and D') or (B' and C') or (B' and D')。 下面是使用C语言实现主析取范式主合取范式的示例代码: 主析取范式: ``` #include <stdio.h> #include <stdbool.h> int main() { bool A = true; bool B = false; bool C = true; bool D = false; bool result = !(A && B) || !(C && D); //布尔表达式 //转化为主析取范式 bool sop = (!A || !C || !D) && (!B || !C || !D); printf("Result: %d\nSOP: %d\n", result, sop); return 0; } ``` 主合取范式: ``` #include <stdio.h> #include <stdbool.h> int main() { bool A = true; bool B = false; bool C = true; bool D = false; bool result = (A || B) && (C || D); //布尔表达式 //转化为主合取范式 bool pos = (!A && !C) || (!A && !D) || (!B && !C) || (!B && !D); printf("Result: %d\nPOS: %d\n", result, pos); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值