求命题公式的主范式

求命题公式的主范式

题目信息

输入命题公式的合式公式,求出公式的真值表,并输出该公式的主合取范式和主析取范式。

输入

命题公式的合式公式

输出

公式的主析取范式和主析取范式,输出形式为:mi ∨ mj ; Mi ∧ Mj ,极小项和 ∨ 符号之间有一个空格,极大项和 ∧ 符号之间有一个空格;主析取范式和主合取范式之间用;隔开,;前后各有一个空格。 永真式的主合取范式为 1 ,永假式的主析取范式为 0 。
输入公式的符号说明:
! 非,相当于书面符号中的 ¬
& 与,相当于书面符号中的
|或,相当于书面符号中的
-蕴含联结词,相当于书面符号中的
+ 等价联结词,相当于书面符号中的
( 前括号
) 后括号

解答

#include <iostream>
#include <cmath>
#include <stack>
#include <cstring>

using namespace std;
#define N 1000
#define MAX 10000000
char x[N];//s是存放输入进去的
bool table[30] = {false};
int explain[30] = {0};
int value[MAX] = {0};
int sum = 0;

int Priority(char c)
{//符号优先级判定
    switch (c)
    {
        case '!': return 5;
        case '&': return 4;
        case '|': return 3;
        case '-': return 2;
        case '+': return 1;
        case '(': return 0;
        default: return 0;
    }
}

void postfix()
{//中缀转后缀表达式,转换完后仍存放在原字符串中
    char post[N] = {'\0'};
    int p = -1;
    stack<char> s;

    int len = strlen(x);
    for (int i = 0; i < len; i++)
    {
        if (x[i] >= 'a' && x[i] <= 'z')
        {
            post[++p] = x[i];
        }
        else if (x[i] == '!' || x[i] == '&' || x[i] == '|' || x[i] == '-' || x[i] == '+')
        {
            while (!s.empty() && Priority(x[i]) <= Priority(s.top()))
            {
                post[++p] = s.top();
                s.pop();
            }
            s.push(x[i]);
        }
        else if (x[i] == '(')
        {
            s.push(x[i]);
        }
        else if (x[i] == ')')
        {
            while (s.top() != '(')
            {
                post[++p] = s.top();
                s.pop();
            }
            s.pop();//最后记得将(弹出
        }
    }
    while (!s.empty())
    {
        post[++p] = s.top();
        s.pop();
    }
    strcpy(x, post);
}

void settable()
{//寻找一共有多少个元素
    int len = strlen(x);
    for (int i = 0; i < len; i++)
    {
        if (x[i] >= 'a' && x[i] <= 'z')
        {
            table[x[i] - 'a'] = true;
        }
    }
    for (int i = 0; i < 26; i++)
    {
        if (table[i])
        {
            sum++;
        }
    }//一共有多少种组合
    sum = (int) pow(2, sum);
}

int BToI()
{//二进制转换为十进制
    int ans = 0, weight = 1;
    for (int i = 25; i >= 0; i--)
    {//再一次遍历所有的字母
        if (table[i])
        {
            if (explain[i])
            {
                ans += weight;
            }
            weight *= 2;
        }
    }
    return ans;
}

int calc(int a, int b, char c)
{//
    switch (c)
    {
        case '&':return a * b;
        case '|':
            if (a + b) return 1;
            else return 0;
        case '-':
            if (a == 1 && b == 0) return 0;
            else return 1;
        case '+':return !((a + b) & 1);
        default:return -1;
    }
}

int work()
{//运算真值表
    stack<int> s;
    int len = strlen(x);
    for (int i = 0; i < len; i++)
    {
        if (x[i] >= 'a' && x[i] <= 'z')
        {//如果是字母的话,将当前字母取得的0或1放入运算栈中
            s.push(explain[x[i] - 'a']);
        }
        else if (x[i] == '!')
        {//如果是非的话
            if (!s.empty())
            {
                int tmp = (s.top() + 1) & 1;
                s.pop();
                s.push(tmp);
            }
        }
        else
        {//如果是其他双元运算符的话,进入计算
            int b = s.top();
            s.pop();
            int a = s.top();
            s.pop();
            s.push(calc(a, b, x[i]));
        }
    }
    return s.top();
}

void Generate(char c)
{//列举每个出现的元素的0,1取值情况
    while (c <= 'z' && table[c - 'a'] == false)
    {//从第一个字母开始寻找
        c++;
    }
    if (c > 'z')
    {//会得到一个二进制值,将真值表计算出的答案存储在value中
        value[BToI()] = work();
        return;
    }
    explain[c - 'a'] = 0;
    Generate(c + 1);
    explain[c - 'a'] = 1;
    Generate(c + 1);
}

void OutputMIN()
{
    int i = 0;
    while (i < sum && !value[i])
    {
        i++;
    }
    if (i >= sum)
    {
        cout << "0 ; ";
        return;
    }
    cout << "m" << i;
    for (i++; i < sum; i++)
    {
        if (value[i])
        {
            cout << " ∨ m" << i;
        }
    }
    cout << " ; ";
}

void OutputMAX()
{
    int i = 0;
    while (i < sum && value[i]) i++;
    if (i >= sum)
    {
        cout << 1 << endl;
        return;
    }
    cout << "M" << i;
    for (i++; i < sum; i++)
    {
        if (!value[i])
        {
            cout << " ∧ M" << i;
        }
    }
    cout << endl;
}

int main()
{
    //freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
    cin >> x;
    postfix();
    settable();
    Generate('a');
    OutputMIN();
    OutputMAX();
    return 0;
}

想法

首先我们先明确题目里的含义:
一个公式的主析取范式的极小项的编码对应着该公式的所有成真赋值。
一个公式的主合取范式的极大项的编码对应着该公式的所有成假赋值。

例如a^b

二进制取值aba^b
0000
1010
2100
3111
此时我们取出所有最终值为0的构成主合取范式的极大项,即M0 ∧ M1 ∧ M2
取出所有最终值为1的构成主析取范式的极小项,即m3

例如avb

二进制取值abavb
0000
1011
2101
3111
取出所有最终值为0的构成主合取范式的极大项,即M0
取出所有最终值为1的构成主析取范式的极小项,即m1 ∨ m2 ∨ m3

那么此时程序可以有如下的思路
第一步,将中缀表达式转换为后缀表达式。
第二步,列举所有可能性的二进制取值,构成一张真值表。
第三步,运算出每一个真值表最后的答案。
第四步,按要求输出相应的主合取范式和主析取范式。

这里面有个运算的细节:
一、a & ba ^ b等价于a * b

aba & ba * b
0000
0100
1000
1111

二、a | ba v b等价于if (a + b) return 1; else return 0;
| a |b|a|b|ans
|–|–|–|–|
0| 0 |0|0|
0| 1 |1|1|
1| 0|1|1|
1| 1 |1|1|

三、a - ba → b等价于if (a == 1 && b == 0) return 0; else return 1;

aba - bans
0011
0111
1000
1111

四、a + ba ↔ b等价于!((a + b) & 1)

aba + bans
0011
0100
1000
1111
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值