课本是高等教育出版社出版的《离散数学及其应用》。
程序会自动分析输入的表达式,并且列出真值表,最后打印出主析取范式和主合取范式,最多支持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;
}