命题逻辑应用系统
1 问题描述
该系统要求实现命题逻辑中基本算法及其应用系统,包括真值表的计算、主析取和主合取范式的计算。通过此课题,熟练掌握命题公式的计算机表示、命题等价常见算法的实现,实现一个简单的命题逻辑应用系统。
2 需要实现的功能或函数
(1)计算显示任一个命题公式的真值表;
(2)计算命题公式的主析取范式;
(3)计算命题公式的主合取范式;
(4)判断两个命题公式是否等价(或蕴含);
(5)如有可能,试着利用命题公式的推理关系解决下列逻辑问题:
一对夫妻带着他们的一个孩子在路上碰到一个朋友。朋友问孩子:“你是男孩还是女孩?”朋友没听清孩子的回答。孩子的父母中某一个说,我孩子回答的是“我是男孩”,另一个接着说:“这孩子撒谎,她是女孩。”这家人中男性从不说谎,而女性从来不连续说两句真话,也不连续说两句假话。试问这小孩性别,以及谁是其父亲,谁是其母亲?
(6)有个简单直观的界面,以便显示使用上述函数功能。
程序:
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <stack> #include <map> using namespace std; string epr, epr1, epr2; string pdnf, pcnf; //pdnf析取, pcnf合取 map <char, bool> mp; //哈希表 stack <char> st; //用于得到后缀表达式 stack <int> dst; //用于计算表达式的真值 int val[26]; int res, num; bool CanIn(char fir, char sec) //能否入栈 { int i, o; switch(fir) //设置栈内,栈外元素优先级 { case '#': i = 0; break; case '(': i = 1; break; case '~': i = 3; break; case '>': i = 5; break; case '|': i = 7; break; case '&': i = 9; break; case '!': i = 11; break; case ')': i = 12; break; } switch(sec) { case '#': o = 0; break; case '(': o = 12; break; case '~': o = 2; break; case '>': o = 4; break; case '|': o = 6; break; case '&': o = 8; break; case '!': o = 10; break; case ')': o = 1; break; } if(i < o) return true; else return false; } void to_suffix() //中缀表达式转后缀表达式 { while(st.size()) //清空栈 st.pop(); string tmp = ""; //中间变量,用来存后缀表达式 st.push('#'); //'#'为栈底标记 int len = epr.length(); for(int i = 0; i < len; i++) { if(mp[epr[i]]) //如果当前是字母,则直接加到后缀表达式中 { tmp = tmp + epr[i]; continue; } if(CanIn(st.top(), epr[i])) //拿当前的字符和栈顶元素相比,若栈顶元素优先级小于栈外优先级,则入栈 st.push(epr[i]); else if(epr[i] == ')') //处理括号嵌套 { while(st.top() != '(') { tmp = tmp + st.top(); st.pop(); } st.pop(); } else //如果栈顶元素优先级大于栈外元素优先级,则将元素出栈,直到栈顶元素优先级小于栈外优先级 { do { tmp = tmp + st.top(); st.pop(); }while(!CanIn(st.top(), epr[i])); st.push(epr[i]); } } while(st.top() != '#') //把剩余元素出栈 { tmp = tmp + st.top(); st.pop(); } st.pop(); epr = tmp; //此时tmp为得到的后缀表达式,赋值给epr //cout << "epr = " << epr << endl; } void Or(int a, int b) //或运算 { res = a + b; res = res > 1 ? 1 : res; dst.push(res); } void And(int a, int b) //与运算 { res = a * b; dst.push(res); } void Not() //非运算 { int a = dst.top(); dst.pop(); res = a == 1 ? 0 : 1; dst.push(res); } void If(int a, int b) //条件运算 { res = (b == 1 && a == 0) ? 0 : 1; dst.push(res); } void Iif(int a, int b) //双条件运算 { res = (a == b) ? 1 : 0; dst.push(res); } void cal() //计算真值 { while(dst.size()) //清空栈dst dst.pop(); int len = epr.length(); int a, b; for(int i = 0; i < len; i++) { if(mp[epr[i]]) { for(int j = 0; j < 26; j++) { if(epr[i] == (j + 'A')) { dst.push(val[j]); break; } } continue; } if(epr[i] != '!') { a = dst.top(); dst.pop(); b = dst.top(); dst.pop(); } switch(epr[i]) { case '|': Or(a, b); break; case '&': And(a, b); break; case '!': Not(); break; case '~': Iif(a, b); break; case '>': If(a, b); break; } } } void Input1() //输入一个表达式 { epr = ""; printf("请输入两个真值表达式: (! 否定), (| 析取), (& 合取), (> 条件), (~ 双条件)\n"); cin >> epr; } void preprea() //处理表达式 { num = 0; mp.clear(); //清空map int len = epr.length(); for(int i = 0; i < len; i++) { if(epr[i] >= 'A' && epr[i] <= 'Z') { if(mp[epr[i]] == false) { mp[epr[i]] = true; //记录那些字母在表达式中 num ++; //num表示表达式中有几种字母 } } } } void Input2() //输入两个表达式 { epr = ""; epr1 = ""; epr2 = ""; printf("请输入两个真值表达式: (! 否定), (| 析取), (& 合取), (> 条件), (~ 双条件)\n"); printf("请输入第一个\n"); cin >> epr1; printf("请输入第二个\n"); cin >> epr2; } //idx是字母的标号,cnt表示已经处理的字母的个数,p标记是否输出真值表 void DFS(int idx, int cnt, int p) //深度优先搜索得到真值表和主范式 { if(cnt == num) { cal(); if(res == 1) { pdnf += "("; int c = 0; for(int i = 0; i < 26; i++) { char t[1]; t[0] = i + 'A'; string ch(t); if(mp[ch[0]]) { c ++; if(val[i] == 1) pdnf += ch; else pdnf += "!" + ch; if(c != num) pdnf += "&"; } } pdnf += ")"; pdnf += "|"; } else { pcnf += "("; int c = 0; //记录字母个数 for(int i = 0; i < 26; i++) { char t[1]; t[0] = i + 'A'; string ch(t); if(mp[ch[0]]) //判断字母是否存在 { c ++; if(val[i] == 0) pcnf += ch; else pcnf += "!" + ch; if(c != num) pcnf += "|"; } } pcnf += ")"; pcnf += "&"; } if(!p) { for(int i = 0; i < 26; i++) if(mp[i + 'A']) printf("%d\t", val[i]); printf("%d\n", res); } return; } int idxx = 0; for(int i = idx; i < 26; i++) { if(mp[i + 'A']) { idxx = i; break; } } val[idxx] = 1; DFS(idxx + 1, cnt + 1, p); val[idxx] = 0; DFS(idxx + 1, cnt + 1, p); return; } void Print(bool p) //输出真值表和主范式, 若p==0,则输出真值表 { if(!p) { for(int i = 0; i < 26; i++) if(mp[i + 'A']) printf("%c\t", i + 'A'); printf("res\n"); } int idx = 0; //idx用来记录0 - 26分别于A - Z对应 for(int i = 0; i < 26; i++) { if(mp[i + 'A']) { idx = i; break; } } val[idx] = 1; DFS(idx + 1, 1, p); val[idx] = 0; DFS(idx + 1, 1, p); } void output_table() //输出真值表接口函数 { Input1(); preprea(); to_suffix(); printf("真值表为:\n"); Print(0); } void output_pcnf() //输出主合取范式接口函数 { Input1(); preprea(); to_suffix(); pcnf = ""; //初始化 Print(1); int len = pcnf.length(); if(len == 0) printf("永真式\n"); else { pcnf.erase(pcnf.length() - 1); cout << "主合取范式:" << pcnf << endl << endl; } } void output_pdnf() //输出主析取范式接口函数 { Input1(); preprea(); to_suffix(); pdnf = ""; Print(1); int len = pdnf.length(); if(len == 0) printf("永假式\n"); else { pdnf.erase(pdnf.length() - 1); cout << "主析取范式:" << pdnf << endl << endl; } } //判断两个表达式的关系 //原理: 命题A与命题B若等价则(A<->B)永真,又永真式无主合取范式 // 命题A与命题B若存在蕴含关系(如A蕴含B)则(A->B)永真,又永真式无主合取范式 void judge_two() { bool flag1 = false, flag2 = false; Input2(); epr = "(" + epr1 + ")" + "~" + "(" + epr2 + ")"; preprea(); to_suffix(); pcnf = ""; Print(1); int len = pcnf.length(); if(len == 0) { flag1 = true; printf("\n两命题公式等价\n"); } if(!flag1) { epr = "(" + epr1 + ")" + ">" + "(" + epr2 + ")"; preprea(); to_suffix(); pcnf = ""; Print(1); len = pcnf.length(); if(len == 0) { flag2 = true; cout << endl << epr1 << endl; printf("蕴含\n"); cout << epr2 << endl; } epr = "(" + epr2 + ")" + ">" + "(" + epr1 + ")"; preprea(); to_suffix(); pcnf = ""; Print(1); len = pcnf.length(); if(len == 0) { flag2 = true; cout << endl << epr2 << endl; printf("蕴含\n"); cout << epr1 << endl; } } if(!flag1 && !flag2) printf("两命题公式既不等价又不存在蕴含关系\n"); } void solve() //解决逻辑问题 { printf("问题描述:\n"); printf("一对夫妻带着他们的一个孩子在路上碰到一个朋友.\n"); printf("朋友问孩子:\"你是男孩还是女孩?\"朋友没听清孩子的回答.\n"); printf("孩子父母中某一个说,我孩子回答的是\"我是男孩\",另一个接着说:\"这孩子撒谎,她是女孩.\"\n"); printf("这家人中男性从不说谎,而女性从来不连续说两句真话,也不连续说两句假话.\n"); printf("试问这小孩性别,以及谁是其父亲,谁是其母亲?\n\n"); printf("解决如下, 设:\n\n"); printf("P表示命题:第一个说话的是父亲\n"); printf("Q表示命题:第一个人说的话为真\n"); printf("R表示命题:第二个人说的第一句为真\n"); printf("S表示命题:第二个人说的第二句为真\n"); printf("根据已知矛盾信息,我们可以得到以下几种矛盾情况\n"); printf("1. P&!Q (即第一个说话的是父亲,父亲说谎)\n"); printf("2. P&Q (即第一个说话的是父亲,父亲没说谎,此时若母亲两句话都真或都假\n"); printf(" 则与已知矛盾,若一真一假,则均与父亲所说的矛盾)\n"); printf("3. !P&(!(R&S)) (即第二个说话的是父亲,两句中有假话)\n"); printf("4. !P&Q (即第一个说话的是母亲,且母亲说的是真的,则因为父亲不说假话)\n"); printf(" ,因此父亲的话为真与母亲的话矛盾\n"); printf("得到矛盾的四种情况,把他们或在一起得到表达式:\n"); printf("(P&!Q)|(P&Q)|(!P&(!(R&S)))|(!P&Q)\n"); printf("求解该表达式的真值表:\n"); epr = "(P&!Q)|(P&Q)|(!P&(!(R&S)))|(!P&Q)"; preprea(); to_suffix(); Print(0); printf("\n从真值表可以看出只有0 0 1 1的时候没有矛盾\n"); printf("即母亲先说且说谎,父亲后说没说谎,于是可得到答案:\n\n"); printf("第一个说话的是母亲\n"); printf("第二个说话的是父亲\n"); printf("孩子是女孩\n"); } void Menu() //菜单 { printf("\n欢饮使用\n"); printf("请选择操作:\n"); printf("1 - 输出真值表\n"); printf("2 - 输出主合取范式\n"); printf("3 - 输出主析取范式\n"); printf("4 - 比较两命题公式是否等价或蕴含\n"); printf("5 - 解决题目中的逻辑问题\n"); printf("6 - 退出\n"); } int main() { Menu(); while(true) { char tp[2]; //选择操作 scanf("%s", tp); if(tp[0] > '6' || tp[0] < '1') { printf("错误输入\n"); continue; } if(tp[0] == '1') output_table(); else if(tp[0] == '2') output_pcnf(); else if(tp[0] == '3') output_pdnf(); else if(tp[0] == '4') judge_two(); else if(tp[0] == '5') solve(); else if(tp[0] == '6') { printf("谢谢使用\n"); break; } Menu(); } }