您的位置:首页 > 其它

「离散数学」 打印任意命题公示的真值表和主范式

2012-09-23 14:03 399 查看
课本是高等教育出版社出版的《离散数学及其应用》。

程序会自动分析输入的表达式,并且列出真值表,最后打印出主析取范式和主合取范式,最多支持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>
#include <stdint.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		data_t;
typedef int		bool_t;
#define TRUE		(1)
#define FALSE		(0)

/* 范式类型,	     析取,     合取 */
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];
int32_t len;
uint32_t len_max;
}expr, truth_expr, ops, truth;
string	input_buf = NULL;	/* 存放输入的表达式 */
struct	prop {
char p;	/* 变元名 */
bool_t v;	/* 真值 */
};
struct	table {
struct prop data[_PROP_LEN];
int32_t len;
uint32_t len_max;
} prop_table;		/* 原子命题变元的列表 */
typedef int truth_item;
struct truth_table {
truth_item data[_STACK_LEN];
int32_t len;
uint32_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_t
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_t num1, bool_t num2) {
bool_t 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_t
prop_find (struct prop item) {
int32_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_t
set_truth_end (void) {
int32_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;
uint32_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;
int32_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) {
int32_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_t carry = FALSE;	/* 进位标志 */
int32_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;
int32_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) {
int32_t count;
bool_t 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[]) {
int32_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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: