离散数学大作业任务书

news/2024/11/15 20:46:28/

目   录

  • 实际的练习题目、系统的总功能和各子模块的功能………………………………………………………………………………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.ppmy.cn/news/561181.html

相关文章

如何编写一个最简单的 udp 版本的 echo server 和 echo client(小白也懂!)

目录 目的 第一步 编写Server(服务器) 第二步 创建Server的各类参数 第三步 实现具体的Server内容 第四步 编写Client(客户端) 实现具体的Client内容 总流程 总代码 源码下载 目的 我们编写一个udp 版本的 echo server 和 echo client 实现在自己电脑上通过客户端…

VB高校缴费系统设计(论文+源代码+外文翻译+文献综述)

本论文是对针对日益复杂化的高校收费工作而设计开发的高校缴费系统的说明,主要介绍了系统开发的主要过程和方法,并且给出了基本的关系模型,完成了模型的静态分析与动态分析。 缴费管理系统是个典型的信息管理系统(MIS)。其设计开发主要包括了后台数据库设计建立和维护以及…

Nginx-Goaccess(实时日志服务)

goaccess的功能 1、使用webscoket协议传输&#xff08;双向传输协议&#xff09;2、基于终端的快速日志分析器3、通过access.log快速分析和查看web服务的统计信息、PV、UV4、安装简单、操作简易、界面炫酷5、按照日志统计访问次数、独立访客数量、累计消耗的带宽6、统计请求次…

​LeetCode解法汇总1254. 统计封闭岛屿的数目

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 二维矩阵 grid 由 0 &#xff08;土地&#xff09;和 1 &#xff08;水&#xf…

优化伊通河漂流旅行方案的模型——JLU数学学院2020级数学模型期末大作业

文章目录 题目描述背景介绍模型假设问题一的模型决策树模型游客安全最大化与旅行次数最大化模型统筹考虑游客安全与旅行次数的模型模型对比 第二问的模型每天下水的脚踏游船与电动游船的比率的敏感性分析全是电动游船的情形全是脚踏游船的情形每天下水的脚踏游船与电动游船成比…

c语言小游戏——扫雷

扫雷是一款经典的单人益智游戏&#xff0c;玩家需要在一个由许多方块组成的棋盘上找出所有的地雷&#xff0c;而不触发任何一颗地雷。 int input 0;do{menu();printf("请选择:>");scanf("%d", &input);//输入1进入游戏&#xff0c;输入0退出游戏…

电信设备进网许可证申请需要什么材料?

(一) 电信设备进网许可申请表&#xff08;1份&#xff0c;原件&#xff0c;由工业和信息化部提供格式文本&#xff09;。申请表应当由电信设备进网许可申请人的法定代表人或其委托代理人签字并加盖公章&#xff1b;申请人与生产企业为不同法人的&#xff0c;还应当提供由双方法…

国内首家!华为获5G核心网电信设备进网许可证;亚马逊或颠覆博通等芯片制造商?2020年5G总投资额达0.9万亿元……...

关注并标星星CSDN云计算 速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01; 每周两次&#xff0c;打卡即read 更快、更全了解泛云圈精彩news go go go iPhone 12全系渲染图&#xff08;图片来源网…