「離散數學」 打印任意命題公示的真值表和主範式

課本是高等教育出版社出版的《離散數學及其應用》。

程序會自動分析輸入的表達式,並且列出真值表,最後打印出主析取範式和主合取範式,最多支持256 個變元。

主要用到的算法:中綴表達式轉後綴表達式、後綴表達式求值還有一個二進制加法模擬。


下面上2 個圖,第一個是表達式開頭沒有非運算的(課本P85 例3.5.5):


第二個不但表達式開頭有非運算,而且非運算之後並不是一個數值,而是一個操作符(課本P83 例3.5.4):


下面是代碼,如果CSDN 的編輯器弄亂了就將就着看吧,本來代碼的縮進和排版都是整潔的。

file:///main.c

/* main.c
 * use MinGW Developer Studio to compile
 * by iSpeller ([email protected])
 */

#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;
}



發佈了41 篇原創文章 · 獲贊 13 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章