离散数学实验一c语言(数理逻辑,输入命题公式,输出真值表,主析取范式,主合取范式,给出命题公式类别)

一、算法描述、算法思想:

(一)符号表示:

命题变元:字母(区分大小写,即大写和小写字母是不同的变元)

操作连接词:0--合取、1--析取、2--条件、3--双条件

否定:’(A’表示A变元的否定)

(二)数据结构

typedef struct Stack *S; //栈用来存储命题公式中的逻辑连结词,命题变元
struct Stack
{
    char *ch;
    int top;
};

该栈是用来计算命题公式的真值。

typedef struct SqlList *L; //线性表用来存储相同的变元的不同位置,其命题和命题的否定是分开存储的
struct SqlList
{
    char ch;
    int *elem;            //存该命题变元的位置
    int lengthe; // lengthe为该命题变元的总数
};

该线性表是用来存储命题公式中相同的变元的全部位置。

char ChangeStr[100][100];                     //存储所有可能的命题变元取真假后的命题公式
char XiStr[100][100];                         //存储主析取范式的命题公式
char HeStr[100][100];                         //存储主合取范式的命题公式
char targetch[100];                           //存储该命题公式所有不同的变元
int pos[100];                                 //映射作用,其下标对应变元的序号,值对应该变元第一次出现的位置
int total, totalXi, totalHe, cnt, panz, panj; // total为所有可能的命题变元取真假后的命题公式的个数,即ChangeStr的个数
// totalXi为主析取范式的个数,totalHe为主合取范式的个数,cnt为总共不同变元的个数,panz和panj为判断该命题公式类型的变量

其次,还有以上变量

(三)主要功能实现:

输出真值表:

处理原命题公式

L ProcessStr(char str[]) //处理原命题公式,计算所有可能的真值指派

首先将原命题公式所有的命题变元改为真值F,方便计算指派变元真值后,该命题公式的真值。

用线性表数组,cnt为不同命题变元的序号,l[cnt]则存储第cnt个变元的所有位置,方便后序指派真值。

进行真值指派:

void Find(L l, int now, int e, char str[]) //将str里的所有命题变元进行真值指派,now指当前到第几个变元,e为总共的变元数

利用递归思想,分析str中所有的命题变元均有两个取值,即F、T,那么我们就递归的将每一个变元进行真值指派,即先令第一个变元为F,然后第二个变元为F......照这样将所有命题变元指派完后,存储该可能的命题公式的指派,然后回退return,即回到上一层将最后一个变元真值改为T,然后再记录,再回退,以此类推。

该str为处理后的命题公式,即所有的变元均为F,now为当前正在处理第cnt个变元,e指总共不同的变元个数。l线性表即存放着每个命题变元的位置。

计算命题公式的真值:

char Calculate(char str[]) //计算str公式的真值

利用栈的思想,将操作连接符存到一个栈里,将变元存到另一个栈里。首先匹配命题公式,遇到操作符,根据操作符等级,如果为左括号或者操作符栈为空,则一定入操作符栈,遇到当前操作符的优先级>=操作符栈栈顶优先级,则也一定入栈,小于等于则先取出操作符栈和变元栈顶元素计算,然后再次判断,直到符合要求为止。遇到变元也将其入栈,一但遇到右括号,那么就要再次出栈计算,计算后再将其入栈,直到遇到左括号为止,最后匹配完公式,还得继续计算剩下的操作符和变元。

char Pan(char op, char le1, char le2) //对变元le1,变元le2进行逻辑连接词判断真假
char Pan2(char op, char le1)//计算变元否定的操作

计算变元经过操作连结词后的真值。Pan是对二元操作符如析取、合取等的计算,Pan2是一元操作符如取否定的计算。

输出真值表:

L InputTrueTable(char str[])

将计算后的真值和公式,对应输出其对应的命题变元的真值,和该命题公式真值。

判断命题公式类型:

char res = Calculate(ChangeStr[i]); //计算该命题公式的真值
        if (res == 'T')
        {
            strcpy(XiStr[totalXi++], ChangeStr[i]);
            panj = 0; //则该命题公式不可能为一个永假式
        }
        else
        {
            strcpy(HeStr[totalHe++], ChangeStr[i]);
            panz = 0; //该命题公式不可能为一个用真式
        }

在输出真值表的同时,判断下是否该命题公式中的变元经过指派后,该公式的真值一直为真,或者假,然后输出即可。

输出主析取范式、主合取范式:

void FindZhu(int cnt, L l) //输出主析取范式、主合取范式

在输出真值表的同时,也将那些真值为T的命题存下,真值为F的存下,然后在根据变元位置,重新组成命题公式后输出,主析取范式,即取命题最后为T的,如果变元为T则取原变元,为F取该变元的否定,并永合取将其连结这就是一个合取式,最后再将合取式析取为主析取范式。主合取范式类似,即找到真值为F的析取式,将其合取起来。

二、流程图:

Main

InputTrueTable(输出真值表)

ProcessStr(处理原命题公式,使其更方便计算)

Find(列出所有真值指派的命题公式)

Calculate(计算命题公式的真值)

Devide(判断命题公式的类型)

FindZhu(输出主析取范式,主合取范式)

ChangeStr(处理主析取范式中的合取式、主合取范式的析取式)

运行截图:

由图可看出,对于命题公式:P→(Q∧R)∧(┐p→(┐Q∧┐R))

该命题公式包含了优先级、括号、非、合取、条件等运算,大概率可以检测程序的可靠性

说明:

先是输出该命题公式的真值表、命题公式类型

根据真值输出主析取范式、主合取范式

源代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Stack *S; //栈用来存储命题公式中的逻辑连结词,命题变元
struct Stack
{
    char *ch;
    int top;
};
typedef struct SqlList *L; //线性表用来存储相同的变元的不同位置,其命题和命题的否定是分开存储的
struct SqlList
{
    char ch;
    int *elem;            //存该命题变元的位置
    int lengthe; // lengthe为该命题变元的总数
};
char ChangeStr[100][100];                     //存储所有可能的命题变元取真假后的命题公式
char XiStr[100][100];                         //存储主析取范式的命题公式
char HeStr[100][100];                         //存储主合取范式的命题公式
char targetch[100];                           //存储该命题公式所有不同的变元
int pos[100];                                 //映射作用,其下标对应变元的序号,值对应该变元第一次出现的位置
int total, totalXi, totalHe, cnt, panz, panj; // total为所有可能的命题变元取真假后的命题公式的个数,即ChangeStr的个数
// totalXi为主析取范式的个数,totalHe为主合取范式的个数,cnt为总共不同变元的个数,panz和panj为判断该命题公式类型的变量
int prior[10] = {1, 1, 0, 0, 2};
char p[10] = {'0', '1', '2', '3', '\''};
//对栈的操作
S InitStack(int size) //初始化栈
{
    S s = (S)malloc(sizeof(struct Stack));
    s->ch = (char *)malloc(sizeof(char) * size);
    s->top = 0;
}
int EmptyStack(S s) //判断栈是否为空,空则返回1,否则返回0
{
    if (s->top == 0)
        return 1;
    else
        return 0;
}
void PushStack(S s, char c) //将c入栈
{
    s->ch[s->top++] = c;
}
char StackTop(S s)//返回栈顶元素,不出栈
{
    return s->ch[s->top-1];
}
char PopStack(S s) //返回栈顶元素,并出栈
{
    return s->ch[--s->top];
}
void freeStack(S s) //释放栈的内存
{
    free(s->ch);
    free(s);
}
void PrintStack(S s)//用来调试程序
{
    int total = 1;
    while (total<=s->top)
    {
        printf("%c ", s->ch[s->top-total]);
        total++;
    }
    printf("\n");
}

//对线性表的操作
L InitList(int size) //初始化线性表,返回线性表数组
{
    L l = (L)malloc(sizeof(struct SqlList) * (size + 1));
    for (int i = 0; i <= size; i++)
    {
        l[i].ch = '\0';
        l[i].elem = (int *)malloc(sizeof(int) * size);
        l[i].lengthe = 0;
    }
    return l;
}
void freeList(L l) //释放该线性表内存
{
    free(l->elem);
    free(l);
}

//对命题公式的操作
char Pan(char op, char le1, char le2) //对变元le1,变元le2进行逻辑连接词判断真假
{
    if (op == '1') //析取
    {
        if (le1 == 'T' || le2 == 'T')
            return 'T';
        else
            return 'F';
    }
    else if (op == '0') //合取
    {
        if (le1 == 'T' && le2 == 'T')
            return 'T';
        else
            return 'F';
    }
    else if (op == '2') //条件
    {
        if (le1 == 'T' && le2 == 'F')
            return 'F';
        else
            return 'T';
    }
    else if (op == '3') //非条件
    {
        if (le1 == le2)
            return 'T';
        else
            return 'F';
    }
}
char Pan2(char op, char le1)//计算变元否定的操作
{
    if (op == '\'')
    {
        if (le1 == 'F')
        return 'T';
        else
        return 'F';
    }
}
char Cal(S s1, S s2)//s1符号、s2字母
{
    char op = PopStack(s1), le1, le2;
    if (op == '\'')
    {
        char le1 = PopStack(s2);
        return Pan2(op, le1);
    }
    else
    {
        char le2 = PopStack(s2);
        char le1 = PopStack(s2);
        return Pan(op, le1, le2);
    }
}
int panLevel(char ch)
{
    if (ch == '\'')
    return prior[4];
    else if (ch == '(')
    return -100;
    else
    return prior[ch-'0'];
}
char Calculate(char str[]) //计算str公式的真值
{
    char le1, le2, op, res;
    int len = strlen(str);
    S s1 = InitStack(len); //符号栈(1、0)
    S s2 = InitStack(len); //字母栈 (T、F)
    int i = 0;
    while (i < len)
    {
        if (str[i] == '1' || str[i] == '0' || str[i] == '(' || str[i] == '2' || str[i] == '3' || str[i] == '\'') //若当前字符为操作符号
        {
            if (str[i] == '('||EmptyStack(s1))
            {
                PushStack(s1, str[i]); //入操作符栈
                i++;
                continue;
            }
            int level = panLevel(str[i]);
            int level2 = panLevel(StackTop(s1));
            if (level >= level2)
            PushStack(s1, str[i]); //入操作符栈
            else
            {
                while (1)
                {
                    res = Cal(s1, s2);
                    PushStack(s2, res);
                    level = panLevel(str[i]);
                    level2 = panLevel(StackTop(s1));
                    if (level>=level2)
                    {
                        PushStack(s1, str[i]);
                        break;
                    }
                }
            }
        }
        else if (str[i] == ')') //若为右括号,则将操作符里的连结词出栈,计算,直到操作符栈中处理到左括号为止
        {
            while (1)
            {
                op = StackTop(s1);
                if (op == '(')
                {
                    PopStack(s1);
                    break;
                }
                res = Cal(s1, s2);
                PushStack(s2, res);
            }
        }
        else
        PushStack(s2, str[i]);
        i++;
        
    }
    while (!EmptyStack(s1) && !EmptyStack(s2)) //若命题变元匹配完成,操作符和字母栈中仍有元素,继续计算
    {
        res = Cal(s1, s2);
        PushStack(s2, res);
    }
    char ans = PopStack(s2); //取出答案
    freeStack(s1);
    freeStack(s2);
    // printf("\n");
    return ans;
}
void Find(L l, int now, int e, char str[]) //将str里的所有命题变元进行真值指派,now指当前到第几个变元,e为总共的变元数
{
    if (now > e) //若已经将所有命题变元都指派真值后
    {
        total++;                       //总数+1
        strcpy(ChangeStr[total], str); //存储
        return;                        //回到上一级
    }
    char str2[100], str3[100]; //存储原先的str
    strcpy(str2, str);
    strcpy(str3, str);

    int total = l[now].lengthe;     //该变元的所有位置
    for (int i = 0; i < total; i++) //所有位置都变
    {
        int p = l[now].elem[i];
        str2[p] = 'F';
    }
    Find(l, now + 1, e, str2);      //到下一个命题变元
    total = l[now].lengthe;         //该变元的所有位置
    for (int i = 0; i < total; i++) //所有位置都变
    {
        int p = l[now].elem[i];
        str3[p] = 'T';
    }
    Find(l, now + 1, e, str3); //找下一个命题变元
}
int Repeat(char str[], char ch) //在str中找ch是否已经出现过,如果已经出现,则返回其第一次出现的位置,未出现过则返回-1
{
    int len = strlen(str);
    for (int i = 0; i < len; i++)
    {
        if (ch == str[i])
            return i;
    }
    return -1;
}
L ProcessStr(char str[]) //处理原命题公式,计算所有可能的真值指派
{
    int len = strlen(str);
    char str2[100], str3[100] = "\0";
    strcpy(str2, str); //记录下原命题变元
    L l = InitList(len);
    //将该命题变元全部变为'F',方便判断真值,并记录下所有命题变元的位置
    for (int i = 0; i < len; i++)
    {
        if ((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z')) //找所有变元位置
        {
            int f = Repeat(str3, str[i]); //找第一个和str[i]相同的字母的位置
            if (f == -1)                      //如果该变元未出现过
            {
                cnt++;                                       //变元总数+1
                targetch[cnt - 1] = str[i];                  //存所有不相同的字母
                l[cnt].ch = str[i];                          //存该字母
                l[cnt].elem[l[cnt].lengthe++] = i;  
                pos[i] = cnt;                                //映射,第一个字母位置对应的cnt
            }
            else //若该变元不是第一次出现
            {
                l[pos[f]].elem[l[pos[f]].lengthe++] = i; //存该字母的所有位置(从0开始)
            }
        }
        str3[i] = str[i];
    }
    for (int i = 0; i < cnt; i++)
        printf("\t%c ", targetch[i]); //输出真值表的第一行,先将所有的命题变元输出
    printf("\t%s\n", str2);           // str2为原字符串
    Find(l, 1, cnt, str);            //str为改为FT字符串
    return l;                         //返回线性表数组,方便后序处理
}

//判断主析取、主合取范式
char *ChangeXiStr(char Xistr[], int cnt, L l) //根据变后的主析取范式,即该主析取范式变元全为F\T
{                                             //对应线性表l中字母和位置,将其修改存储
    char ans[100] = "\0";
    char tar; //目标范式
    int len = strlen(Xistr), lena = 0, p;
    for (int i = 1; i <= cnt; i++) //找所有的变元
    {
        tar = l[i].ch; //找到字母
        p = l[i].elem[0];
        if (Xistr[p] == 'F') //找真值
        {
            ans[lena++] = tar;
            ans[lena++] = '\'';
        }
        else
            ans[lena++] = tar;
        if (i<cnt)
        ans[lena++] = '0';
    }
    return ans;
}
char *ChangeHeStr(char Hestr[], int cnt, L l) //根据变后的主合取范式,即该主合取范式变元全为F\T
{                                             //对应线性表l中字母和位置,将其修改存储,操作与找主析取范式类似
    char ans[100] = "\0";
    char tar;
    int len = strlen(Hestr), lena = 0, p;
    for (int i = 1; i <= cnt; i++)
    {
        tar = l[i].ch; //找到字母
        p = l[i].elem[0];
        if (Hestr[p] == 'T') //找真值
        {
            ans[lena++] = tar;
            ans[lena++] = '\'';
        }
        else
            ans[lena++] = tar;
        if (i<cnt)
        ans[lena++] = '1';
    }
    return ans;
}
void FindZhu(int cnt, L l) //输出主析取范式、主合取范式
{
    char chxi[100], chhe[100];
    printf("主析取范式:\n");
    for (int i = 0; i < totalXi; i++) //将所有真值为T的命题公式进行处理,合成主析取范式
    {
        strcpy(chxi, ChangeXiStr(XiStr[i], cnt, l));
        if (i == 0) //处理输出格式
            printf("(%s)", chxi);
        else
            printf("1(%s)", chxi);
    }
    printf("\n主合取范式:\n");
    for (int i = 0; i < totalHe; i++) //将所有真值为F的命题公式进行处理,合成主析取范式
    {
        strcpy(chhe, ChangeHeStr(HeStr[i], cnt, l));
        if (i == 0) //处理输出格式
            printf("(%s)", chhe);
        else
            printf("0(%s)", chhe);
    }
}

//判断命题公式类型
void Devide(int panz, int panj)
{
    if (panz == 1) // panz为1,说明没有出现真值为F的公式
        printf("该命题公式为一个永真式\n");
    else if (panj == 1) // panj为1,说明没有出错真值为T的公式
        printf("该命题公式为一个永假式\n");
    else //说明T、F均出现
        printf("该命题公式为一个可满足式\n");
}

//输出真值表
L InputTrueTable(char str[])
{
    L l = ProcessStr(str); //处理str,并返回其位置
    panj = 1, panz = 1;
    for (int i = 1; i <= total; i++)
    {
        char res = Calculate(ChangeStr[i]); //计算该命题公式的真值
        if (res == 'T')
        {
            strcpy(XiStr[totalXi++], ChangeStr[i]);
            panj = 0; //则该命题公式不可能为一个永假式
        }
        else
        {
            strcpy(HeStr[totalHe++], ChangeStr[i]);
            panz = 0; //该命题公式不可能为一个用真式
        }

        for (int j = 1; j <= cnt; j++) //遍历所有变元,输出其指派的真值
        {
            int p;                   //该判断是看是否只有一个命题变元的否定或者只有一个变元
            p = l[j].elem[0];
            printf("\t%c ", ChangeStr[i][p]);
        }
        printf("\t%c\n", res);
    }
    return l;
}

//初始化输出界面
void initInput()
{
    printf("请输入一个命题公式:\n");
    printf("变量用字母表示(本程序区分大小写,即大写字母A和小写字母a不同)\n");
    printf("在字母后加上'表示该变量为非\n");
    printf("以下为命题符号表示\n");
    printf("0 ---- 合取\n");
    printf("1 ---- 析取\n");
    printf("2 ---- 条件\n");
    printf("3 ---双条件\n");
    printf("请注意输入命题公式的正确性!!!\n");
    printf("请输入您想要判断的命题公式:");
}
int main()
{
    char str[100];
    initInput();               //输出初始化界面
    scanf("%s", str);          //用户输入命题公式
    L l = InputTrueTable(str); //输出真值表
    Devide(panz, panj);        //输出该命题公式的类型
    FindZhu(cnt, l);           //输出其主析取范式、主合取范式
    freeList(l);               //释放存储空间
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值