结对项目:实现自动生成小学四则运算题目

发布时间 2023-09-28 18:56:56作者: FHZ003
这个作业属于哪个课程 软件工程
这个作业要求在哪里 结对项目
这个作业的目标 学会团队配合的流程完成结对项目:四则运算生成器

团队成员信息

姓名 学号 Github作业链接
傅浩钊 3121004993 傅浩钊:GitHub作业链接
车文超 3121002783 车文超:GitHub作业链接

PSP表格[[]]

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 90
-Estimate -估计这个任务需要多少时间 60 90
Development 开发 700 910
-Analysis -需求分析 (包括学习新技术) 300 360
-Design Spec -生成设计文档 60 80
-Design Review -设计复审 50 20
-Coding Standard -代码规范 (为目前的开发制定合适的规范) 30 100
-Design -具体设计 50 100
-Coding -具体编码 200 220
-Code Review -代码复审 10 30
Test 测试(自我测试,修改代码,提交修改) 350 380
-Reporting -报告 150 170
-Test Report -测试报告 100 130
-Size Measurement -计算工作量 50 30
-Postmortem & Process Improvement Plan -事后总结, 并提出过程改进计划 50 50
合计 1110 1380

项目需求分析

  1. 使用 -n 参数控制生成题目的个数,例如

Myapp.exe -n 10

将生成10个题目。

  1. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如

Myapp.exe -r 10

将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

  1. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。

  2. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。

  3. 每道题目中出现的运算符个数不超过3个。

  4. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。
    例如:
    23 + 45 = 和45 + 23 = 是重复的题目
    6 × 8 = 和8 × 6 = 也是重复的题目。
    3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。
    但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
1.四则运算题目1
2.四则运算题目2
……

其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

  1. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:

1.答案1
2.答案2

特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

  1. 程序应能支持一万道题目的生成。

  2. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
    Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)

其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

效能分析

性能分析图

image

image

程序消耗最大的函数

由图可见程序中消耗最大的函数是读写入文件的函数

设计实现过程

image

image

表达式抽象设计

有真分数和自然数两种,故可分门别类,划分为真分数和自然数两种数类型运算符分别为字符+-
运算符上限为3,数为4
故可以将两种数统一成一个结构体,这样就有数和运算符的统一表示法了
最后把数数组和运算符数组组合起来就可以形成一个表达式结构体
如何生成表达式?
生成大于0小于等于3个的运算符,并把运算符都存入表达式的运算符数组中. 生成数
生成自然数
. 自然数的分子分母均为1
· 生成的额外值即为自然数的值
生成真分数
· 真分数的额外值是0
.生成分母和分子
进行约分
表达式计算方法
中缀表达式的运算顺序受限于运算符的优先级顺序.前缀或后缀表达式的运算顺序只取决于读入的数和运算符顺序。
同时它们保证了运算顺序对运算结果具有唯一性
故可取其思想,按读入的顺序来计算答案
最后把答案和表达式均转为字符串并写入文件即可
表达式转字符串规则。
如+﹣字符后跟*/字符,要加括号

代码间的关系

image

流程图

image

代码说明

关键代码

创建题目

Status Create(var** exp, int size, int* length) 
{
    var* expre;
    int mark_num = random(1, 4);//计算符个数
    int pre = 0;//前括号在第pre个数字前
    int aft = 0;//后括号在第aft个数字后
    int judge = 0;//判断,0写入数字,1写入符号
    int n = 0;
    *length = mark_num + mark_num + 1;
    n = 0;
    if (mark_num > 1)//如果运算符有3个,则存在括号
    {
        pre = random(1, mark_num);
        if(pre == 1)//不让括号括住整个式子
            aft = random((pre + 1), (mark_num + 1));
        else
            aft = random((pre + 1), (mark_num + 2));
        (*length) += 2;
        expre = new var[*length + 1];
        expre[pre * 2 - 2].num_or_Symbol = 1;
        expre[pre * 2 - 2].Symbol = 4;
        expre[aft * 2].num_or_Symbol = 1;
        expre[aft * 2].Symbol = 5;
    }
    else
    {
        expre = new var[*length + 1];
    }
    n = 0;
    while (n < *length)
    {
        if (expre[n].Symbol < 4)
        {
            if (judge == 0)
            {
                expre[n].num_or_Symbol = 0;
                expre[n].Den = random(2, size);
                expre[n].numer = random(0, expre[n].Den);
                expre[n].num = random(1, size);
                judge = 1;
            }
            else
            {
                expre[n].num_or_Symbol = 1;
                expre[n].Symbol = random(0, 4);
                judge = 0;
            }
        }
        n++;
    }
    *exp = expre;
    return SUCCESS;
}

这段代码定义了一个名为Create的函数,它用于创建一个包含数字和运算符的表达式。函数接受三个参数:一个指向var类型的指针的指针exp,一个整数size和一个整数指针length。函数返回一个名为SUCCESS的状态。

代码中的思路如下:

  1. 首先,函数通过调用random函数生成1到4之间的随机整数,将其赋值给mark_num变量,表示运算符的个数。
  2. 接下来,函数初始化pre和aft变量为0,它们分别表示前括号和后括号的位置。
  3. 然后,函数检查mark_num是否大于1。如果运算符的个数大于1,则存在括号。函数将通过调用random函数随机生成pre和aft的值,表示括号的位置。
  4. 函数根据mark_num的值确定表达式长度,并将其赋值给length指针所指向的变量。
  5. 接下来,函数根据mark_num的值创建一个新的var类型的数组expre,用于存储表达式中的数字和运算符。
  6. 如果存在括号,函数将在expre数组的指定位置插入前括号和后括号。
  7. 然后,函数使用一个循环遍历expre数组,并根据expre[n].Symbol的值进行判断。如果expre[n].Symbol小于4,表示该位置需要插入数字或运算符。
  8. 如果需要插入数字,函数将设置expre[n].num_or_Symbol为0,并随机生成expre[n].Den、expre[n].numer和expre[n].num的值。
  9. 如果需要插入运算符,函数将设置expre[n].num_or_Symbol为1,并随机生成expre[n].Symbol的值。
  10. 最后,函数将expre数组的指针赋值给exp参数,并返回SUCCESS状态。

注释说明:

  • exp:指向var类型的指针的指针,用于存储创建的表达式。
  • size:一个整数,表示随机数的范围。
  • length:指向整数的指针,用于存储表达式的长度。
  • var:一个自定义的数据类型,包含数字的分子、分母、数值和符号等信息。
  • random:一个自定义的函数,用于生成指定范围内的随机数。
  • SUCCESS:一个自定义的状态值,表示函数执行成功。

测试运行

随机生成十道题目
image

image

测试题目结果
image

生成10000道数值范围为0~100的表达式
image

项目小结

结对感受

首先,我们认为团队合作是项目成功的关键。通过分工合作,我们各自发挥所长,共同解决问题。我们的工作效率得到提高,而且能够更好地交流和分享想法。这种团队合作有助于我们在项目中取得良好的进展。

其次,良好的沟通是结对项目的核心。在项目中,我们经常进行交流,及时了解彼此的工作进展和遇到的问题。通过沟通,我们能够更好地协调工作,避免误解和冲突。同时,我们还学会了如何更有效地进行沟通,使双方都能够更好地理解和支持对方。

同时,我们也遇到了一些问题和挑战。例如,在项目初期,我们对工具和技术不太熟悉,需要不断学习和探索。此外,由于时间紧迫,我们有时会感到压力较大。但是,我们通过相互鼓励、支持和团队协作,成功地克服了这些困难。

在教训方面,我们认识到在项目开始前进行充分的规划和准备非常重要。虽然我们在项目中遇到了一些未知的问题,但我们没有足够的时间进行规划和准备,这使得项目进展相对缓慢。因此,我们应该在项目开始前更加细致地进行规划和准备,以便更好地应对可能出现的问题。

闪光点或建议

我们想分享一些对彼此的看法和建议。我认为我的同学非常有才华和热情,他总是积极思考并主动解决问题。同时,他也非常细致和认真,能够很好地完成各项任务。我非常感谢他的帮助和支持。

他也对我的工作给予了肯定,认为我具备扎实的技术功底和良好的编程习惯。他也建议我要更加关注细节和整体规划,以免出现不必要的错误和延误。

总之,通过这个结对项目,我们不仅学到了很多知识和技能,还培养了良好的团队合作能力、沟通能力和解决问题的能力。我们将继续努力学习和进步,以便更好地应对未来的挑战和机遇。