离散数学大作业任务书

news2024/11/19 5:42:31

目   录

  • 实际的练习题目、系统的总功能和各子模块的功能………………………………………………………………………………1

1.1题目及问题描述………………………………………………………………1

1.2功能概述………………………………………………………………………1

1.3技术选型………………………………………………………………………1

1.4代码实现………………………………………………………………………2

  • 主要算法简述…………………………………………………………9
  • 程序流程图……………………………………………………………10

3.1功能模块设计…………………………………………………………………10

3.2关键方法流程图………………………………………………………………11

3.3界面设计………………………………………………………………………11

3.4系统测试………………………………………………………………………11

  • 总结报告………………………………………………………………16

  • 实际的练习题目、系统的总功能和各子模块的功能
    1. 题目及问题描述

(1)求任意一个命题公式的真值表。

(2)利用真值表求任意一个命题公式的主范式。

(3)判断两个命题公式是否等值。

    1. 功能概述

1. 求任意一个命题公式的真值表:首先通过调用init函数输入命题公式,然后根据命题公式中包含的所有变量,生成该命题公式的真值表,即计算所有可能情况下命题公式的结果,并输出其真值表。

2. 利用真值表求任意一个命题公式的主范式:根据真值表中为1或0的情况,计算得出该命题公式的主合取范式和主析取范式,并分别输出。

3. 判断两个命题公式是否等值:输入两个命题公式,分别计算它们的真值表,并比较它们的真值表是否相等,最后输出结果。

    1. 技术选型

操作系统

Windows 11

编程语言(及版本)

C++

编辑软件/IDE

VS

    1. 代码实现
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;

typedef struct optrstack
{
    char oper[30];
    int loc;
}OPStack;
void initop(OPStack& op)
{
    int  i;
    op.loc = 0;
    for (i = 0; i < 30; i++)op.oper[i] = '\0';
}
void push(OPStack& op, char c)
{
    op.oper[op.loc++] = c;
}
char pop(OPStack& op)
{
    return(op.oper[--op.loc]);
}
typedef struct opndstack
{
    int oper[60];
    int loc;
}OPndStack;
void initopnd(OPndStack& op)
{
    int  i;
    op.loc = 0;
    for (i = 0; i < 30; i++)op.oper[i] = '\0';
}
void pushopnd(OPndStack& op, int c)
{
    op.oper[op.loc++] = c;
}
int popopnd(OPndStack& op)
{
    return(op.oper[--op.loc]);
}
void init(char s[])
{
    int t;
    printf("\n请输入任意一个命题公式(命题变元为一个字符)\n");
    printf("非、析取、合取、条件、双条件词分别用符号!、|、&、-、+表示\n");
    cin>>s;
    t = strlen(s);
    s[t] = '@';
    s[t + 1] = '\0';
}
int is_optr(char c)
{
    char optr_list[] = "+-|&!()@";
    for (int i = 0; i < (int)strlen(optr_list); i++) if (c == optr_list[i])return 1;
    return 0;
}
char first(char op1, char op2)
{
    char tab[8][9] = {
          "><<<<<>>",
    ">><<<<>>",
    ">>><<<>>",
    ">>>><<>>",
    ">>>>><>>",
    "<<<<<<=E",
    ">>>>>E>>",
    "<<<<<<E=",
    };
    char optr_list[] = "+-|&!()@";//双条件、条件、析取、合取、非
    int op1_loc, op2_loc;
    for (op1_loc = 0; op1_loc < (int)strlen(optr_list); op1_loc++)if (optr_list[op1_loc] == op1)break;
    for (op2_loc = 0; op2_loc < (int)strlen(optr_list); op2_loc++)if (optr_list[op2_loc] == op2)break;
    return tab[op1_loc][op2_loc];
}
int operate(int x, char op, int y)
{
    switch (op) {
    case '+': return (((!x) || y) && (x || (!y))); break;
    case '-': return ((!x) || y); break;
    case '|': return x || y; break;
    case '&': return x && y; break;
    }
    return -1;
}
void divi(char s[], char c[])
{
    int i, j = 0, t;
    for (i = 0; s[i] != '@'; i++) if (!is_optr(s[i])) { for (t = 0; t < j; t++) if (c[t] == s[i]) break;  if (t == j)c[j++] = s[i]; }
    c[j] = '\0';
    char aa;
    for (i = 0; i < j - 1; i++)//按字典序排序
        for (t = i + 1; t < j; t++) if (c[i] > c[t]) { aa = c[i]; c[i] = c[t]; c[t] = aa; }
}
int locate(char s[], char c)
{
    int i;
    for (i = 0; i < (int)strlen(s); i++) if (s[i] == c)break;
    return i;
}
int calc(char s[100], int* p)
{
    char myopnd[10], c;
    int sloc = 0;
    OPStack optr;
    initop(optr);
    push(optr, '@');
    OPndStack  opnd;
    initopnd(opnd);
    divi(s, myopnd);
    c = s[sloc++];
    while (c != '@' || optr.oper[optr.loc - 1] != '@') {
        if (!is_optr(c)) { int d1;  d1 = p[locate(myopnd, c)];  pushopnd(opnd, d1);  c = s[sloc++]; }
        else {
            switch (first(optr.oper[optr.loc - 1], c)) {
            case '<': push(optr, c);  c = s[sloc++];  break;
            case '=': pop(optr);  c = s[sloc++];  break;
            case '>': char op;  op = pop(optr);
                if (op == '!') { int a;  a = !popopnd(opnd);  pushopnd(opnd, a); }
                else {
                    int a, b;  a = popopnd(opnd);  b = popopnd(opnd);
                    int res;  res = operate(b, op, a);  pushopnd(opnd, res);
                }
                break;
            }
        }
    }
    return opnd.oper[opnd.loc - 1];
}
void main()
{
    //(1)求任意一个命题公式的真值表:
    cout << "求任意一个命题公式的真值表:" << endl;
    char exp[100], myopnd[10];
    int i, j, n, m, A[1024][10], flag, k;
    int F[1024];
    init(exp);
    divi(exp, myopnd);
    n = (int)strlen(myopnd);
    m = (int)pow(2, n);
    for (j = 0; j < n; j++) {
        flag = 1;
        k = (int)pow(2, n - j - 1);
        for (i = 0; i < m; i++) {
            if (!(i % k))flag = !flag;
            if (flag)A[i][j] = 1;
            else  A[i][j] = 0;
        }
    }
    char ss[100];
    int t;
    strcpy(ss, exp);
    t = (int)strlen(ss);
    ss[t - 1] = '\0';
    printf("命题公式%s的真值表如下:\n", ss);
    for (j = 0; j < n; j++)printf("%4c", myopnd[j]);
    printf("   %s\n", ss);
    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++)printf("%4d", A[i][j]);
        F[i] = calc(exp, A[i]);
        printf("%6d", F[i]);
        printf("\n");
    }
  //  (2)利用真值表求任意一个命题公式的主范式。
    // 计算主合取范式
    printf("\n命题公式%s的主合取范式为:\n", ss);
    for (i = 0; i < m; i++) {
        if (F[i] == 0) continue;
        printf("(");
        for (j = 0; j < n; j++) {
            if (A[i][j] == 0) printf("%c", myopnd[j]);
            else printf("!%c", myopnd[j]);
            if (j != n - 1 && A[i + 1][j + 1] == 1) printf("&");
        }
        printf(")");
        if (i != m - 1 && F[i + 1]) printf("|");
    }

    // 计算主析取范式
    printf("\n命题公式%s的主析取范式为:\n", ss);
    for (i = 0; i < m; i++) {
        if (F[i] == 1) continue;
        printf("(");
        for (j = 0; j < n; j++) {
            if (A[i][j] == 1) printf("%c", myopnd[j]);
            else printf("!%c", myopnd[j]);
            if (j != n - 1 && A[i + 1][j + 1] == 1) printf("|");
        }
        printf(")");
        if (i != m - 1 && F[i + 1] == 0) printf("&");
    }
    cout << endl;


    //(3)判断两个命题公式是否等值。
    // 计算第一个命题公式的真值表
    //前面已经输入过第一个命题公式,其数据直接用就行
    cout << "请再输入一个命题公式,判断两个命题公式是否等值:" << endl;
     int F1[1024], F2[1024];
    divi(exp, myopnd);
    n = (int)strlen(myopnd);
    m = (int)pow(2, n);
    for (j = 0; j < n; j++)
for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++)
        F1[i] = calc(exp, A[i]);
     
    }

    // 计算第二个命题公式的真值表
    char exp2[100], myopnd2[10];
    init(exp2);//插入新的命题公式,并计算其真值表
    divi(exp2, myopnd2);

    printf("第二个命题公式%s的真值表如下:\n", exp2);
    for (j = 0; j < n; j++)printf("%4c", myopnd2[j]);
    printf("   %s\n", exp2);
    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++)printf("%4d", A[i][j]);
        F2[i] = calc(exp2, A[i]);
        printf("%6d", F2[i]);
        printf("\n");
      
    }
  printf("\n");
    // 判断两个命题公式是否等值
    int equal = 1;
    for (i = 0; i < m; i++) {
        if (F1[i] != F2[i]) {
            equal = 0;
            break;
        }
    }
    if (equal) printf("****两个命题公式相等\n");
    else printf("****两个命题公式不相等\n");

   
}


  • 主要算法简述

这份代码实现的是命题逻辑中的真值表,主合取范式、主析取范式和判断两个命题公式是否等值的功能。

在计算命题公式的真值表时,程序先输入命题公式,然后将每一个命题变元的可能取值全部遍历一遍,最后通过逐行计算得到整个真值表。在计算主合取范式和主析取范式时,程序会扫描整个真值表,并将所有取值为1或0的行(具体要看是求主合取范式还是主析取范式)转换为对应的逻辑表达式。

至于判断两个命题公式是否等值,则是先计算出两个真值表,再逐行进行比较。

整个程序用到的数据结构主要是栈,用于控制运算符优先级以及存储操作数。由于命题逻辑的运算符并不多,所以代码里使用了一个二维数组来存储两个运算符之间的优先级关系,从而避免了繁琐的if-else语句。

需要注意的是,这份代码实现的是命题逻辑,而不是一阶逻辑。如果要实现更高阶别的逻辑,需要修改部分代码和数据结构。

  • 程序流程图

    1. 功能模块设计

     

  • 基础功能
  • 读取命题公式:从用户处获取输入的命题公式。
  • 解析命题公式:将中缀表达式转换为后缀表达式,并建立一个命题变元编号与变量名的对应关系,可以利用栈来实现。
  • 生成真值表:根据命题变元的个数生成真值表的表头,并依次遍历每一行,在当前行计算出命题公式的取值结果,并将结果填充至真值表中。
  • 计算主合取范式和主析取范式:扫描整张真值表,找出所有取值为1或0的行(具体要看是求主合取范式还是主析取范式),并将它们转换为对应的逻辑表达式。这部分可以使用字符串拼接来实现。
  • 判断两个命题公式是否等价:分别计算出两个命题公式的真值表,然后逐行比较它们的取值是否相同。
  • 输出结果:将命题公式的真值表、主合取范式、主析取范式和是否等价的结果输出给用户。
    1. 关键方法流程图
  • 生成真值表
  • 计算主合取范式和主析取范式
  • 判断两个命题公式是否等价
     
  • 总结报告
  1. 大作业完成中遇到的主要问题和解决方法

在完成命题逻辑代码的过程中,可能会遇到以下一些问题:

1- 在解析命题公式时,可能会遇到表达式中带有多个括号的情况。这时可以使用栈来处理,每当遇到左括号时就将其入栈,在遇到右括号时就将栈顶的左括号出栈,并将中间的表达式加入后缀表达式中,即将不同运算符设定不同的优先级,从而确定括号的结合顺序。为了简化这个问题,我们可以采用逆波兰式表示法,将操作数放在前面,运算符放在后面的形式来表示算式。这样就不需要考虑括号在算式中的位置了。

2- 在生成真值表时,可能会遇到变量名长度不同、包含空格或其他特殊字符等问题。为了解决这些问题,可以使用哈希表来存储变量名和变元编号之间的对应关系。

3- 在计算主合取范式和主析取范式时,可以采用字符串拼接的方式来组合起真值表中取值为1或0的行所对应的逻辑表达式。

4-处理非操作符(~)和双重否定:在处理命题公式的过程中,我遇到非操作符和双重否定的情况。为了对这些情况进行处理,可以使用递归的方式来实现。具体来说,在从左到右解析命题公式的过程中,如果遇到非操作符(~),则将后面的命题公式作为参数递归调用自身,再添加一个“~”操作符,作为新的一项存入栈中;如果遇到双重否定,则直接跳过两个“~”操作符并继续解析后面的命题公式。

5-处理表达式中的变量名:有时在命题公式中会包含变量名,需要对这些变量名进行处理才能计算出表达式的值。一个简单的方法是将变量名转换为布尔型变量,例如,将“P”和“Q”转换为True和False。但是,如果变量名比较多或者变量名的长度不同,这种方法就会变得复杂和低效。为了解决这个问题,我们可以使用哈希表(Hash Table)来存储变量名和对应的布尔型变量之间的映射关系。具体来说,我们可以将变量名所对应的布尔值存储在哈希表中,然后在解析命题公式时,可以直接查找哈希表中的值并进行替换。

6-处理表达式中的逻辑运算符:在逻辑运算的时候,我不知如何明确表示逻辑运算符。命题公式中常见的逻辑运算符包括“与(&)”、“或(|)”、“非(~)”、“蕴含(->)”和“等价(<->)”。为了正确处理这些运算符,我们需要考虑它们的优先级和结合性。一般来说,非运算符的优先级最高,其次是与运算和或运算,蕴含和等价运算的优先级相同,但比与运算和或运算低。而这些运算符的结合性都是从左到右。因此,在解析命题公式时,我们需要将运算符按照优先级和结合性进行合理地组合。

(2)创新和得意之处

1- 采用后缀表达式的形式来处理命题公式,减少了符号优先级和括号的复杂性。

2- 使用哈希表来存储变量名和变元编号的对应关系,可以快速地查找变元。

3- 通过字符串拼接的方式生成主合取范式和主析取范式,减少了对逻辑运算符的依赖。

我为这些创新感到得意,并对它们的实现感到满意。

(3)大作业完成中存在的不足,需进一步改进的设想

在命题逻辑代码的编写过程中,可能存在以下一些不足之处:

1- 对于比较复杂的命题公式,程序可能会出现栈溢出等问题。此时可以考虑使用递归来解决这个问题。

2- 当命题公式中出现重复的变量名时,程序可能会出现错误。这时可以考虑在哈希表中加入变量名的出现次数来进行判断。

3-在计算主合取范式和主析取范式时,目前的代码使用了字符串拼接的方式。但是,在处理比较复杂的公式时,这种方式的效率可能会很低。这时候我们可以考虑使用二叉树的方式存储逻辑表达式,并利用树的遍历方式,将其转换成主合取范式和主析取范式。同时,如果我们想要求其他更加高级的逻辑运算,比如蕴含、等价等,也可以采用类似的方式来处理和计算。

(4)大作业完成中的感想和心得体会

在完成命题逻辑代码的过程中,我深深体会到了编程的乐趣和挑战。通过不断地思考、调试和优化,我不仅学会了如何设计和实现一个小型的逻辑计算器,还提升了对算法和数据结构的理解和应用能力。同时,我也清楚地意识到自己的不足之处,并希望在今后的学习和工作中能够不断地改进和优化自己的编程技能,为社会、为人类创造更多的价值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/681419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

02 | 日志系统:一条SQL更新语句是如何执行的?

以下内容出自《MySQL 实战 45 讲》 02 | 日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f; 查询语句的那套流程&#xff0c;更新语句也会走一遍。 更新流程中和查询不一样的是&#xff0c;更新流程中涉及了两个重要的日志模块。redo log (重做日志) 和 binglog&a…

如何编写用于Neo-Hookean材料的Abaqus VUMAT Fortran子例程

引言 大家好&#xff0c;我是一个热爱编程、研究有限元分析的普通程序员。我非常感谢你们能够抽出宝贵的时间来阅读我的文章&#xff0c;你们的支持是我前行的动力。今天&#xff0c;我们将讨论一个非常专业的话题&#xff0c;即如何编写用于Neo-Hookean材料的Abaqus VUMAT Fo…

Unreal 5 实现UI制作

这一篇讲解一下unreal engine里面的内置ui插件UMG&#xff0c;虚幻示意图形界面设计器&#xff08;Unreal Motion Graphics UI Designer&#xff09;(UMG) 是虚幻引擎内置的一套ui制作工具&#xff0c;通过它我们能够实现平面ui&#xff0c;场景hud内容 实现背景图片填充整个…

【MySQL数据管理】:插入、修改、删除操作

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL数据插入、修改、删除的讲解✨ 目录 前言一、插入数据二、修改数据三、删除数据四、总结 一、插入数据 使用INSERT INTO语句来向表中插入数据 ✨语法&#xff1a; 给指定字段添加数据 INSERT INTO 表名 (字段…

ctfshow web入门 php特性web98-102

1.web98 get会被post方式覆盖&#xff0c;传入的参数需要等于flag&#xff0c;才能读取到flag值,如果直接传http_flagflag,返回的结果会是一个空数组&#xff0c;因为get变量被覆盖了&#xff0c;而post没有传参 payload: get 11 post HTTP_FLAGflag 2.web99 array_push在数组…

机器视觉初步8:特征提取专题

文章目录 1.角点检测2.纹理特征提取3.特征描述符匹配3.1 Harris角点描述符3.2 SIFT&#xff08;尺度不变特征变换&#xff09;描述符3.3 SURF&#xff08;加速稳健特征&#xff09;描述符 4.基于深度学习的特征提取 在机器视觉中&#xff0c;特征提取是从目标图像中提取有用的视…

C语言:打印菱形(输入菱形上半部分行数)

题目&#xff1a; 用C语言在屏幕上输入以下图案&#xff1a; 思路&#xff1a; 总体思路&#xff1a; &#xff08;一&#xff09;. 输入菱形上半部分行数 -- scanf()函数 &#xff08;二&#xff09;. 使用 for循环 进行 菱形上半部分三角形 的打印&#xff0c; 菱形上半部分…

基于5G网络的视频远程操控应用实践——低延迟视频技术及应用

本次分享将分为三个部分&#xff1a;第一部分介绍低延迟视频所涉及到的关键技术&#xff0c;包括低延迟视频编解码、视频传输、视频处理低延时框架、视频采集和显示&#xff1b;第二部分重点介绍5G环境下低延迟视频对抗弱网提出的要求&#xff0c;包括&#xff1a;弱网状态的探…

Vulcanexus-一体化ROS2工具集

Vulcanexus机器人操作系统ROS2一站式工具集&#xff08;GalacticHumble&#xff09;2022 Humble Hierro v2 x86_64 arm64 Ubuntu Jammy (22.04) Vulcanexus是ROS 2的一站式工具集&#xff0c;用于构建机器人应用程序。它固定了DDS中间件&#xff0c;使用了Fast DDS&#xff…

word文件未保存 如何恢复

问题 word文件未保存 如何恢复 详细问题 笔者关闭已编辑完成的word文件&#xff0c;误触不保存&#xff0c;再次打开文件恢复至编辑前的状态&#xff0c;如何恢复至编辑完成后的状态 解决方案 文件 → \rightarrow →打开 → \rightarrow →恢复未保存的文件 或 1、打开 …

Spring关于@Configuration配置处理流程解析

Configuration配置处理流程解析 AnnotationConfigApplicationContext基于注解配置ApplicationContext启动刷新流程Spring关于Configuration解析处理流程那些年被忽略问题 AnnotationConfigApplicationContext基于注解配置 Spring通过上下文应用AnnotationConfigApplicationCon…

10- c语言复合数据类型 (C语言)

一 结构体 1.1 引入 1、在自然界中 任何一个物体&#xff0c;都有多个属性&#xff0c;如果用计算机语言来描述的话&#xff0c;一个属性也许可以用某一个基本数据类型来表示&#xff0c;但是当有多个属性的时候&#xff0c;一个基本数据类型就不能表示了。例如&#xff1a;学…

ML算法——线代预备知识随笔【机器学习】

文章目录 数学预备知识3、线性代数3.1、矩阵奇异值分解&#xff08;SVD&#xff09;3.2、广义逆矩阵&#xff08;Moore-Penrose &#xff09;3.3、数据白化&#xff08;Data Whitening&#xff09;3.4、向量导数 4、其它 数学预备知识 3、线性代数 3.1、矩阵奇异值分解&#…

最小化暗数据风险的 5 个步骤

超过一半的公司数据存储库包含哪些内容&#xff0c;但大多数人甚至不知道自己拥有什么&#xff1f;这是暗数据&#xff0c;是公司在不知不觉中收集的信息&#xff0c;它们不是日常业务交互的组成部分&#xff0c;因此通常位于后台。 虽然这些数据对于大多数公司来说似乎是不必…

C语言——数据的输入输出

数据的输入输出 前言&#xff1a;一、格式输入输出函数1.格式输出函数printf&#xff08;&#xff09;2.格式输入函数scanf&#xff08;&#xff09; 二、字符输入输出函数1.字符输出函数putchar&#xff08;&#xff09;2.字符输入函数getchar&#xff08;&#xff09; 三、字…

JUC并发工具类--阻塞队列BlockingQueue

JUC并发工具类--阻塞队列BlockingQueue 队列队列&#xff08;Queue接口&#xff09;提供的方法 阻塞队列阻塞队列&#xff08;BlockingQueue接口&#xff09;提供的方法应用场景JUC包下的阻塞队列如何选择适合的阻塞队列选择策略线程池对于阻塞队列的选择 队列 是限定在一端进…

Servlet技术实现服务端,Android平台作为客户端,实现一个个人店铺

背景&#xff1a; 使用Servlet技术实现服务端&#xff0c;使用Android平台作为客户端&#xff0c;实现一个个人店铺&#xff0c;店铺商品不限。功能要求如下&#xff1a; 1. 提供登录、注册功能&#xff1b;&#xff08;10分&#xff09; 2. 首页面包括“商品列表”子页面、“…

ATA-8000系列射频功率放大器——在生物医学中的应用

ATA-8000系列射频功率放大器——在生物医学研究中的应用 ATA-8000系列是一款射频功率放大器。其P1dB输出功率500W&#xff0c;饱和输出功率最大1000W。增益数控可调&#xff0c;一键保存设置&#xff0c;提供了方便简洁的操作选择&#xff0c;可与主流的信号发生器配套使用&…

VulnHub靶机渗透:SKYTOWER: 1

SKYTOWER: 1 靶机环境介绍nmap扫描端口扫描服务扫描漏洞扫描总结 80端口目录爆破 3128端口获取立足点获取立足点2提权总结 靶机环境介绍 https://www.vulnhub.com/entry/skytower-1,96/ 靶机IP&#xff1a;192.168.56.101 kali IP&#xff1a;192.168.56.102 nmap扫描 端口扫…

h5手写签名示例

前言 业务中需要用户进行签字&#xff0c;如何让用户在手机端进行签字&#xff1f; 示例如下 代码已分享至Gitee: https://gitee.com/lengcz/qianming 原示例&#xff1a; https://www.jq22.com/jquery-info13488 H5实现手写签字 创建一个html页面 <!DOCTYPE html> …