程式設計題: 命題邏輯應用系統
命題邏輯應用系統
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();
}
}
相關文章
- 離散數學——6.命題邏輯的應用
- 程式設計師面試邏輯題解析程式設計師面試
- 命題邏輯重要知識點筆記筆記
- 邏輯題
- 離散數學——3.命題邏輯的等值演算
- 離散數學——5.命題邏輯的推理理論
- 離散數學——4.命題邏輯公式的正規化公式
- 武器系統邏輯程式碼分析
- 應用程式邏輯錯誤總結
- 離散數學——2.命題邏輯公式語法和語義公式
- SQL Server資料體系和應用程式邏輯詳解SQLServer
- 當邏輯程式設計遭遇CQRS時程式設計
- 題庫明細 使用java理解程式邏輯Java
- 業務邏輯層快取應該設計快取
- Unix程式設計/應用問答中文版 ---1.系統管理配置問題(轉)程式設計
- 邏輯程式設計與函式程式設計的介紹程式設計函式
- [02] 多執行緒邏輯程式設計執行緒程式設計
- 計算機程式的思維邏輯 (47) – 堆和PriorityQueue的應用計算機
- 計算機程式的思維邏輯 (47) - 堆和PriorityQueue的應用計算機
- 系統設計 相關面試題面試題
- Unix程式設計/應用問答中文版 ---4.系統資源相關問題(轉)程式設計
- 型別系統和邏輯型別
- 深度探究MMO社交對話系統(二):聊天系統結構設計和功能邏輯
- JAVA程式設計題-用java解決兔子問題Java程式設計
- 幾道經典邏輯推理題,提高你的邏輯思考能力
- 遊戲機制設計:生活邏輯轉化為遊戲邏輯的設計形式遊戲
- OA系統應用的四個難題
- ERP系統應用問題分析(轉)
- 應用系統規劃與設計
- 用node.js做程式設計題Node.js程式設計
- AI「王道」邏輯程式設計的復興?清華提出神經邏輯機,已入選ICLRAI程式設計ICLR
- 物流供應鏈管理是什麼?供應鏈物流管理系統邏輯結構應用
- 好程式設計師web前端分享邏輯運算程式設計師Web前端
- 程式設計師需要了解的邏輯學思想程式設計師
- 提高程式設計邏輯的7種方法 - DEV程式設計dev
- 讀人工智慧全傳06邏輯程式設計人工智慧程式設計
- 系統慢慢變壞的邏輯
- 用程式碼證明自己閒的蛋疼(二)——寫暴力去做邏輯推理題