软工作业3:Python实现自动生成小学四则运算题目

发布时间 2023-09-28 23:50:56作者: Osidal
课程 软件工程
要求 结对项目
目标 实现一个自动生成小学四则运算题目的命令行程序

团队组成

姓名 学号 GitHub
于杨 3221004940 GitHub

PS:另一个队员不是计科12班的同学

Part1 PSP表格

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

Part2 效能分析

image
从分析报告结果中我们可以得到:
整个过程一共有13715个函数调用被监控,其中13462个是原生调用(即不涉及递归调用)。总共执行的时间为0.040秒

  • ncalls表示函数调用的次数(有两个数值表示有递归调用,总调用次数/原生调用次数)
  • tottime是函数内部调用时间(不包括他自己调用的其他函数的时间)
  • percall等于 tottime/ncalls
  • cumtime累积调用时间,与tottime相反,它包含了自己内部调用函数的时间
  • 最后一列,文件名,行号,函数名

image
总结:程序生成10道题目的总执行时间仅为0.04s时间较短,效果良好;在其中程序消耗最大的函数是生成关系式的函数,原因是因为只是使用了简单的迭代方式进行,方法的复杂程度太高,没有好的表现效果,占用太多内存

Part3 实现过程

graph TD 基础需求-->自动生成小学四则运算题目 基础需求-->使用命令行参数控制生成题目的个数等数据 基础需求-->结果应为真分数 基础需求-->运算符个数不超过3个 基础需求-->一次运行生成的题目不能重复
graph LR 四则运算--> A(generate_expression函数) A-->递归生成数学表达式 四则运算--> evaluate_expression函数 evaluate_expression函数-->定义一个函数用于评估表达式 四则运算--> generate_exercises函数-->对题目进行详细检查是否有逻辑上的问题 四则运算-->save_to_file函数-->将运行结果包括题目和答案写进文件中

Part4 代码说明

这部分代码实现了表达式的自动生成,使用简单的迭代的方式生成,可以通过修改参数实现对题目难度的调整,也就是调整运算符号的数目。

def generate_expression(min_value, max_value, max_operators):
    # 递归生成数学表达式
    if max_operators == 0 or (min_value == 1 and max_value == 1):
        #如果不需要再添加运算符或者已经达到最小值1直接返回一个随机整数
        return str(random.randint(min_value, max_value))
    operator = random.choice(operators) #随机选择一个运算符
    if operator == '+':
        e1 = generate_expression(min_value, max_value, max_operators - 1)
        e2 = generate_expression(min_value, max_value, max_operators - 1)
    elif operator == '-':
        e1 = generate_expression(min_value, max_value, max_operators - 1)
        e2 = generate_expression(min_value, max_value, max_operators - 1)
    elif operator == '*':
        e1 = generate_expression(min_value, max_value, max_operators - 1)
        e2 = generate_expression(min_value, max_value, max_operators - 1)
    else:
        e1 = generate_expression(min_value, max_value, max_operators - 1)
        e2 = generate_expression(min_value, max_value, max_operators - 1)
    return f"({e1} {operator} {e2})"#返回拼接好的表达式

这部分代码定义一个函数,用于评估表达式,主要是为了避免分母出现0的情况,会是得结果出现逻辑错误。

def evaluate_expression(expression):
    try:
        # 使用eval函数计算表达式的值,并使用limit_denominator函数将其限制为最简分数
        return Fraction(eval(expression)).limit_denominator()
    except ZeroDivisionError:
        # 避免除以零的错误
        return None

这部分代码是使用遍历的方法对答案和正确答案之间进行对比,得到最后结论出现错误的个数和错误的题目。

def check_answers(exercise_file, generated_answer_file, reference_answer_file):
    # 读取题目文件和生成的答案文件
    with open(exercise_file, 'r') as ex_file, open(generated_answer_file, 'r') as gen_ans_file:
        exercises = ex_file.readlines()
        generated_answers = gen_ans_file.readlines()

    # 读取参考答案文件
    with open(reference_answer_file, 'r') as ref_ans_file:
        reference_answers = ref_ans_file.readlines()

    correct_indices = []  # 存储正确答案的索引
    wrong_indices = []  # 存储错误答案的索引

    # 遍历题目文件和生成的答案文件,检查是否正确
    for i, (exercise, generated_answer) in enumerate(zip(exercises, generated_answers), start=1):
        exercise = exercise.strip()
        generated_answer = generated_answer.strip()

        # 如果参考答案文件的长度小于题目文件,则将参考答案设置为N/A
        if i <= len(reference_answers):
            reference_answer = reference_answers[i - 1].strip()
        else:
            reference_answer = "N/A"

        # 如果生成的答案和参考答案相同,则将该题的索引添加到正确索引中
        if generated_answer == reference_answer:
            correct_indices.append(i)
        else:
            # 如果生成的答案和参考答案不同,则将该题的索引添加到错误索引中
            wrong_indices.append(i)

    # 返回正确索引和错误索引
    return correct_indices, wrong_indices

Part5 测试运行

image
image
通过上述测试案例可以看出来计算结果正确,但是还含有问题是不能做到将分母上的1去除掉,最后的结果保留了分数形式,还需有再进行优化和调整
image
image
答案检查功能也正常,可以输出错误题目的个数和具体题目,经过检验和比对是正确的。

part6 尝试

我们尝试开发可视化界面,是得程序的可用性更强。经过实验后可以实现简洁清晰的界面,但是该尝试还有缺点,该程序在文件的读取和记录方面没有呈现好的表现,后期还可以进行优化。例如:

  • 可以选择不同难度类型的题库
  • 可以选定本次测试的题目数量和最后输出该次测试的得分
  • 建立错题本等等
    该程序的优化方向还有很多,可以进一步探索。
    image
    image
    image

Part7 项目小结

合作过程中的优点:

  • 明确的分工和责任:我们在项目开始时明确定义了每个人的角色和职责。这有助于避免冲突和混淆,并使我们能够高效地完成任务。

  • 良好的沟通:良好的沟通是合作的关键。我们建立了一个开放的沟通渠道,定期讨论进展、问题和解决方案。这有助于保持团队协作的流畅性。

  • 相互尊重:我们尊重彼此的观点和意见,充分利用每个人的优势。这鼓励了创新和更好的决策。

取得的成果:

  • 项目管理经验:我们学会了规划和管理项目,包括任务分配、时间管理和问题解决。这将有助于我们未来的合作项目。

  • 团队协作:我们增强了团队协作和沟通的能力,这对于与他人一起工作至关重要。

  • 时间管理:我们发现在合作项目中,合理分配时间非常重要。

结对感受:

结对项目使我们完成项目时更有动力,相互支持和鼓励。当一个人遇到困难时,另一个人总是在身边提供帮助。我们共享了技术经验,相互学习了解决问题的方法。这种知识共享丰富了我们的技能库。通过项目,我们不仅在技术上成长,还在团队协作、沟通和问题解决方面成长。这是一次宝贵的经历。