「离散数学」 打印任意命题公示的真值表和主范式
2012-09-23 14:03
399 查看
课本是高等教育出版社出版的《离散数学及其应用》。
程序会自动分析输入的表达式,并且列出真值表,最后打印出主析取范式和主合取范式,最多支持256 个变元。
主要用到的算法:中缀表达式转后缀表达式、后缀表达式求值还有一个二进制加法模拟。
下面上2 个图,第一个是表达式开头没有非运算的(课本P85 例3.5.5):
第二个不但表达式开头有非运算,而且非运算之后并不是一个数值,而是一个操作符(课本P83 例3.5.4):
下面是代码,如果CSDN 的编辑器弄乱了就将就着看吧,本来代码的缩进和排版都是整洁的。
file:///main.c
程序会自动分析输入的表达式,并且列出真值表,最后打印出主析取范式和主合取范式,最多支持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; }
相关文章推荐
- [栈的应用] 打印任意命题公示的真值表和主范式(这是转载同学的)
- 求命题真值表及主范式
- 离散数学蕴含等值式前件为假时命题为真的理解
- 离散数学命题逻辑之p->q
- 离散数学 第一章 命题逻辑 1-2 联结词
- 给一维数组输入任意n(n<10)个数,假设输入6个整数是7,4,8,9,1,5,建立如下内容的方阵并打印。 5 7 4 8 9 1 1 5 7 4 8 9 9 1 5 7
- 实现打印任意大小,任意多个菱形(在int数据类型下)
- [离散] 编程求命题公式真值表
- 命题与连接词(1) 离散数学
- 离散数学输入表达式打印真值表和主析/合取范式
- Java打印任意数组元素出现的次数
- 离散数学 第一章 命题逻辑 1-3命题公式与翻译
- 任意输入一个数,打印对应的空心实心金字塔以及菱形
- 编写程序,打印任意行数的菱形星号组合
- 水晶报表实现选择任意字段打印(VB)
- 打印任意位数的水仙花数
- 离散数学 第一章 命题逻辑 1-4真值表与等价公式
- 证明任意两个正整数相等(伪命题)
- 1049 命题逻辑(java)— 离散数学应用(详解)