程序设计题: 命题逻辑应用系统

命题逻辑应用系统


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();
    }
}




  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值