[离散] 打印任意命题公示的真值表和主范式

课本是高等教育出版社出版的《离散数学及其应用》。

程序会自动分析输入的表达式,并且列出真值表,最后打印出主析取范式和主合取范式,最多支持256 个变元。

主要用到的算法:中缀表达式转后缀表达式、后缀表达式求值还有一个二进制加法模拟。


下面上2 个图,第一个是表达式开头没有非运算的(课本P85 例3.5.5):


第二个不但表达式开头有非运算,而且非运算之后并不是一个数值,而是一个操作符(课本P83 例3.5.4):


下面是代码,如果CSDN 的编辑器弄乱了就将就着看吧,本来代码的缩进和排版都是整洁的。

file:///main.c

/* main.c
 * use MinGW Developer Studio to compile
 * by iSpeller (shell_way@foxmail.com)
 */

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

#define	_BUF_LEN	(1<<10)
#define	_STACK_LEN	(_BUF_LEN/2)
#define	_PROP_LEN	(1<<7)

#define	VOID_NUM	(0)	/* 不存在的运算数 */
typedef	char *	string;
typedef	int	bool;
#define	true	(1)
#define	false	(0)
typedef	int		data_t;
/* 防止各种老式编译器来大姨妈 */
#ifndef	_SIZE_T
typedef	unsigned int	size_t;
#endif
#ifndef	_SSIZE_T
typedef	int	ssize_t;
#endif

/* 范式类型,     析取,    合取 */
enum paradigm   { EXTRACT, CONJUNCT, };
/* 优先级大小     等于, 小于,   大于 */
enum priorities	{ EG=0, NGE=-1, NLE=1, };
/* 联结词(包括英文圆括号)优先级,全真 */
/*     左括号, 非,    合取,  析取, 蕴含,   右括号,  结束符 */
enum { LEFT=5, NOT=4, AND=3, OR=3, CONT=3, RIGHT=2, END=1, };

/* 4个栈,expr 储存后缀表达式,truth_expr 是expr 的真值解释
 * ops 储存联结词, truth 做后缀表达式求值栈,truth 最后存放表达式真值 
 */
struct	stack {
	data_t data[_STACK_LEN];
	ssize_t	len;
	size_t len_max;
}expr, truth_expr, ops, truth;	
string	input_buf = NULL;	/* 存放输入的表达式 */
struct	prop {	
	char p;	/* 变元名 */
	bool v; /* 真值 */
};
struct	table {
	struct prop data[_PROP_LEN];
	ssize_t	len;
	size_t len_max;
} prop_table;	/* 原子命题变元的列表 */
typedef	int truth_item;
struct truth_table {
	truth_item data[_STACK_LEN];
	ssize_t len;
	size_t len_max;
} truth_table;	/* 真值表 */

#define	push(s,a)	((s).data[++(s).len] = (a))
#define	pop(s)		((s).data[(s).len--])
#define	is_empty(s)	((s).len+1)
#define get_top(s)	((s).data[(s).len])
#define	init(s,m)	(((s).len = -1) || ((s).len_max = (m)))

#define	STRCMP(a,r,b)	(strcmp ((a), (b)) r 0)

/* 判断字符是否是原子命题变元
 */
bool
is_op (char c) {
	switch (c) {
	case '|':	return OR;		break;
	case '&':	return AND;		break;
	case '!':	return NOT;		break;
	case '>':	return CONT;	break;
	case '(':	return LEFT;	break;
	case ')':	return RIGHT;	break;
	case '#':	return END;		break;	
	default : 	return false;
	}
}

/* 判断运算符的优先级
 */
enum priorities
get_priority (char op1, char op2) {
	if (is_op (op1) == is_op (op2))
		return EG;
	else if (is_op (op1) < is_op (op2))
		return NGE;
	else
		return NLE;
}

/* 进行数据运算
 */
data_t
do_op (char op, bool num1, bool num2) {
	bool truth = false;
	
	if('!' == op) 
		return num1 ? false : true;
	switch (op) {
	case '|':	truth = (num2 || num1);	break;
	case '&':	truth = (num2 && num1);	break;
	case '>':	truth = (num2 && !num1) ? false : true;	break;
	default:	fprintf (stderr, "Boy, WHAT Did U Have Done!!??\n");
				exit (0);
	}
	return truth;
}

/* 判断识别的命题变元是否已经存在
 */
bool
prop_find (struct prop item) {
	ssize_t count = 0;
	
	for (count=0; count<prop_table.len+1; ++count)
		if (item.p == prop_table.data[count].p)
			return true;
	return false;
}

/* 判断变元真值是否设置完毕
 * 如果真值全真返回真
 */
bool
set_truth_end (void) {
	ssize_t count;
	
	for (count=0; count<prop_table.len+1; ++count)
		if (!prop_table.data[count].v)
			return false;
	
	return true;
}
 
/* 输出“你好”
 */
void
start (void) {
	printf ("输入诸如\"P|(Q&R)>!P\" 的表达式,程序将会列出真值表并求出主范式。\n\n");
	printf ("其中:\"|\"表示析取;\"&\"表示合取;\"!\"表示非;\">\"表示蕴含。支持英文圆括号。\n");
	printf ("字符串处理不是算法的核心,所以不会处理非法输入\n");
	printf ("\t----因此当你输入了非法的表达式,你也会得到一个非法的结果 :D\n");
	printf ("exit 指令退出。\n\n");
}

/* 要求用户输入表达式和原子命题变元的真值
 */
void
get_input () {
	char *loc = input_buf;
	size_t size = 0;
	struct prop item;
	
	/* 清栈 */
	init (expr, _STACK_LEN); 
	init (ops, _STACK_LEN);
	init (truth_expr, _STACK_LEN);
	init (truth, _STACK_LEN);
	init (truth_table, _STACK_LEN);
	init (prop_table, _PROP_LEN);
	
	if (!(input_buf = (char *)malloc (_BUF_LEN))) {
		perror ("malloc ()");
		exit (1);
	}
			
	/* 获取表达式 */
	do {
		printf (" # ");
		if (!fgets (input_buf, _BUF_LEN-1, stdin)) {
			perror ("fgets ()");
			exit (1);
		}
		input_buf[strlen (input_buf)-1] = '#';	/* 结束符号 */
	
		if (STRCMP ("exit#", ==, input_buf)) {
			printf ("再见 :D\n");
			exit (0);
		}
	} while (STRCMP ("#", ==, input_buf));
	
	/* 识别原子命题变元和联结词并压入变元列表 */
	size = strlen (input_buf);
	for (loc = input_buf; loc-input_buf < size; ++loc) {
		if (' ' != *loc) 
			if (!is_op (*loc)) {
				item.p = *loc;
				if (!prop_find (item))
					push (prop_table, item);	/* 现在并不赋真值 */
			} 
	}
}

/* input_buf 中的表达式转换为后缀表达式
 */
void
make_postfix_expr () {
	data_t item;
	ssize_t count;
	enum priorities level;
	
	push (ops, '#');	/* 栈底元素,结束符号,优先级最小 */
	for (count=0; count<strlen (input_buf); ++count) {
		item = input_buf[count];
		
		if (' ' == item)
			continue;
		
		if (!is_op (item)) 	/* 是操作数则压入表达式栈 */
			push (expr, item);
		else if (')' == item || '#' == item) {	/* 去除成对的括号和结束标记'#' */
			while ('#' != (item = pop (ops)))
				push (expr, item);
			pop (ops);
		} else {
			level = get_priority (item, get_top (ops));
			
			/* 通过压入一个不存在的操作数,把单目运算符'!' 
			 * 当作双目运算符来处理
			 */
			if ('!' == item)	
				push (expr, VOID_NUM);
			
			if (NLE == level) {		/* 如果后进运算符高于栈顶元素 */
				push (ops, item);		/* 压入运算符栈 */	
				if ('(' == item)		/* 如果压入了一个左括号 */
					push (ops, '#');		/* 压入运算符栈一个结束标记来保持正确的优先级 */
			} else {				/* 否则 */
				push (expr, pop (ops));	/* 栈顶元素压入表达式栈 */
				push (ops, item);		/* 后进运算符压入运算符栈 */
			}
		}
	}
	free (input_buf);
}

/* 设置变元的初始真值,全假
 */
void
init_props_truth (void) {
	ssize_t count = 0;
	
	for (count=0; count<prop_table.len+1; ++count) 
		prop_table.data[count].v = false;
	
	for (count=0; count<prop_table.len+1; ++count)
		printf ("%d ", prop_table.data[count].v);
		
	/* 立刻计算一次真值,因为其后的计算不包含全假的情况 */
	void find_truth (void);
	find_truth ();
}

/* 设置变元的真值,把所有变元当做一个二进制数
 * 用二进制加法模拟真值,每次调用函数都会给二进制数加一
 */
void
set_props_truth (void) {
	bool carry = false;	/* 进位标志 */
	ssize_t count, count2;
	
	for (count=0, carry=true; carry && (count<prop_table.len+1); ++count) {
			if (prop_table.data[count].v) {
				prop_table.data[count].v = (carry ? 0 : 1);
				if (prop_table.len>0)
					/* 同时要处理前面的位 */
					for (count2=1; count2<count+1; ++count2)
						prop_table.data[count-count2].v = false;
				carry = (prop_table.data[count].v ? false : true);
			} else {	
				prop_table.data[count].v = (carry ? 1 : 0);
				carry = false;
			}
	}
	
	for (count=0; count<prop_table.len+1; ++count)
			printf ("%d ", prop_table.data[count].v);
	void find_truth (void);
	find_truth ();
}

/* 调用函数之时默认prop_table 已经设置了一组有效的真值
 * 函数计算在这组真值下整个后缀表达式的真值
 */
void
find_truth (void) {
	truth_item item;
	ssize_t count, count2;
	data_t data, num1, num2, ans;
	
	/* 首先把truth_expr 中的变元全部换成真值 */
	truth_expr = expr;
	for (count=0; count<truth_expr.len+1; ++count) {
		data =  truth_expr.data[count];
		
		if (!is_op (data)) {
			for (count2=0; count2<prop_table.len+1; ++count2)
				if (data == prop_table.data[count2].p) {
					truth_expr.data[count] = prop_table.data[count2].v;
					break;
				}
		}
	}
	
	/* 后缀表达式求值 */
	for (count=0; count<truth_expr.len+1; ++count) {
		data =  truth_expr.data[count];
		
		if (!is_op (data))		/* 非运算符 */
			push (truth, data);
		else {					/* 是运算符 */
			num1 = pop (truth);
			num2 = pop (truth);
			ans = do_op (data, num1, num2);
			push (truth, ans);
		}
	}	
	
	/* 储存真值 */
	item = pop (truth);
	push (truth_table, item);
	
	/* 顺便打印真值 */
	printf ("\t%d\n", truth_table.data[truth_table.len]);
}

/* 打印主范式
 */
void	
print_main_paradigm (enum paradigm type) {
	ssize_t count;
	bool has_find;
	
	if ((EXTRACT!=type) && (CONJUNCT!=type))
		exit (0);
	
	printf ("主%s范式为 : ", (EXTRACT==type) ? "析取" : "合取");
	for (count=0, has_find=false; count<truth_table.len+1; ++count) {
		if ((EXTRACT==type) 
			? truth_table.data[count]
			: !truth_table.data[count]) {
			has_find = true;
			printf ("%c%d %s ", (EXTRACT==type) ? 'm' : 'M', 
								count,
								(EXTRACT==type) ? "∨" : "∧");
		}
	}
	if (has_find)
		printf ("\b\b  \n");
	else 
		printf ("为空");
}

/* MAIN 
 */
int
main (int argc, char *argv[]) {
	ssize_t count;
	
	start ();
	
	while (true) {
		get_input ();
		make_postfix_expr ();
		
		for (count=0; count<prop_table.len+1; ++count) {
			printf ("%c ", prop_table.data[count].p);
		}
		printf ("\t真值\n\n");
		
		init_props_truth ();
		
		while (!set_truth_end ())
			set_props_truth ();
		
		print_main_paradigm (EXTRACT);	/* 主析取范式 */
		print_main_paradigm (CONJUNCT); /* 主合取范式 */
	}
	
	return 0;
}




  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值