当前位置: 首页 > news >正文

结对项目作业

自动生成小学四则运算题目结对项目报告

一、作业基本信息

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479
这个作业的目标 实现一个论文查重程序,并且熟悉项目开发的流程
github仓库链接 https://github.com/yeah-a/3123004804
成员1:肖俊 3123004804
成员2:陈俊彦 3123004782

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 40 45
· Estimate · 估计任务时间 40 45
Development 开发 490 525
· Analysis · 需求分析(包括学习新技术) 60 65
· Design Spec · 生成设计文档 70 75
· Design Review · 设计复审(和同事审核设计文档) 30 30
· Coding Standard · 代码规范(制定合适的规范) 20 20
· Design · 具体设计 80 80
· Coding · 具体编码 120 125
· Code Review · 代码复审 40 45
· Test · 测试(自我测试,修改代码,提交修改) 70 85
Reporting 报告 90 80
· Test Report · 测试报告 30 25
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 40 35
合计 - 620 650

三、计算模块接口部分的性能改进

1 工具准备与环境配置

工具用途 专业工具 安装命令 核心作用
函数级性能分析 cProfile 内置模块(无需安装) 记录函数调用次数、耗时、调用关系
可视化分析结果 snakeviz pip install snakeviz 将cProfile数据转为交互式调用图
行级性能定位 line_profiler pip install line_profiler 逐行分析函数耗时占比

2 第一步:用 cProfile 定位耗时瓶颈(函数级分析)

核心目标:找出程序中 耗时最长的函数,替代手动计时的粗糙分析。
2.1 基础使用:生成性能报告
操作步骤:

  • 在命令行中运行程序,并使用 cProfile 分析:
    image
  • 用snakeviz可视化调用关系:
    image
    关键指标:关注 tottime(函数自身耗时,不含子函数)和 cumtime(累计耗时,含子函数)。
    瓶颈定位:通过调用图发现 expression_generator() 函数 tottime=0.015秒(生成1000题场景),为首要优化目标。
    image
    image

3 第二步:高消耗函数分析与改进思路

  • 原代码如下:
    image
  • 问题1:原逻辑中,check_expression_validity失败会导致整个生成过程作废(返回None),假设失败率为30%,4502次调用需额外生成1350次,浪费约30%耗时。
    改进思路:生成运算符和数字时 主动规避无效情况,确保生成即合法,彻底消除重试。
  • 问题2:原代码循环使用expr_str += f" {ops[i]} {num_strs[i+1]}",每次拼接创建新字符串(如3个运算符需4次拼接,创建4个字符串对象)。
    改进方案:用列表收集所有部分,最后join一次(仅创建1个字符串对象)。
  • 问题3:原逻辑通过evaluate_expression(expr_str)解析字符串计算结果,需额外处理字符串转表达式(如"3+5*2"→计算),耗时占比约40%。
    改进方案:利用已有的nums(Fraction对象)和ops(运算符列表)直接计算,跳过字符串解析。
  • 问题4:generate_number(r)被调用op_count+1次(每次生成数字和字符串),函数调用本身有开销(尤其高频调用时)。
    改进方案:若generate_number逻辑简单(如生成整数并转字符串),可直接内联代码,减少函数栈切换。

4 综合以上改进,我们得到最终的性能分析图如下:

image
image
在开发小学数学四则运算生成器时,我们通过cProfile性能分析发现,核心函数generate_expression的耗时占比过高——单次调用平均耗时0.015秒,在生成1000道题目的场景下,累计耗时超过15秒,成为制约程序效率的瓶颈。经过针对性优化后,该函数的单次耗时降至0.007秒,性能提升超过50%,整体程序运行效率翻倍。

四、小学四则运算题目生成器模块的设计与实现流程

1 需求细化

1.1 输入输出规范

命令行参数:支持两种运行模式,参数格式如下:

生成模式:python main.py -n [题目数量] -r [数值范围]
示例:python main.py -n 100 -r 20(生成100道数值范围0~20的题目)
判题模式:python main.py -e [题目文件路径] -a [答案文件路径]
示例:python main.py -e ./Exercises.txt -a ./Answers.txt(对比题目与答案,生成评分报告)

文件读写:

生成模式输出:Exercises.txt(题目,格式:"3 + 5/7 × 2'1/3 =")、Answers.txt(答案,格式:"5'2/3");
判题模式输出:Grade.txt(评分报告,格式:"Correct: 8 (1,3,5...) Wrong: 2 (2,4...)");
编码要求:UTF-8,支持空文件(生成模式返回空题目,判题模式返回0题正确)、超大文件(单题≤100字符,10万题约10MB)。

生成规则:

运算符:1~3个(+、-、×、÷),除法用÷表示;
数字类型:自然数(如5)、真分数(如3/7)、带分数(如2'1/3),随机生成;

合法性约束:

减法结果非负(如5 - 3合法,3 - 5非法);
除法结果为真分数(如1/2 ÷ 3/4 = 2/3合法,4 ÷ 2 = 2非法,因结果为整数);
题目去重:通过表达式标准化处理+/×交换律(如3+5与5+3视为重复)。

1.2 核心算法设计

生成流程:参数解析→数字生成→运算符选择→合法性校验→表达式构建→结果计算→去重→文件输出。

关键步骤说明:
数字生成:通过随机选择生成自然数(0~r-1)、真分数(分子<分母)、带分数(整数+真分数);
合法性校验:生成数字和运算符后,模拟计算过程,确保减法非负、除法结果为真分数;
去重机制:将表达式标准化(如a+b×c标准化为b×c+a),通过集合存储已生成表达式,避免重复;
结果计算:基于fractions.Fraction高精度计算,支持分数运算,避免浮点数误差。

2 模块设计

2.1 模块划分

模块文件 核心函数/类 功能描述
parser.py parse_args() 解析命令行参数,区分生成/判题模式,校验参数合法性(如-n为正整数)
number_generator.py generate_number(r) 随机生成自然数/真分数/带分数,返回字符串和Fraction对象
parse_number_str(s) 将数字字符串(如2'3/8)转换为Fraction对象,用于计算和判题
expression_generator.py generate_expression(r) 生成含1~3个运算符的表达式,返回表达式字符串和结果(Fraction)
check_expression_validity(nums, ops) 校验表达式合法性(减法非负、除法结果为真分数)
expression_evaluator.py evaluate_expression(expr_str) 计算表达式结果(支持分数运算),替换×为*、÷为/后通过Fraction计算
duplicate_checker.py standardize_expression(expr_str) 标准化表达式(处理+/×交换律),用于去重(如3+5→5+3)
file_handler.py write_problems_and_answers(problems, answers) 将题目和答案写入Exercises.txt和Answers.txt
grade_exercises(e_path, a_path) 对比题目与答案,生成Grade.txt评分报告
main.py main() 调度生成/判题流程:参数解析→模块调用→异常处理

3 模块接口实现步骤

步骤1:参数解析模块(arg_parser.py)

核心需求:解析命令行参数,区分生成/判题模式,校验参数合法性(如-n必须为正整数)。

parse_args()实现:
定义参数:-n(题目数量)、-r(数值范围)、-e(题目文件)、-a(答案文件);
模式判断:

  • 生成模式(无-e/-a):校验-n和-r均为正整数,否则抛出parser.error("生成模式需提供正整数的 -n 参数");
  • 判题模式(有-e/-a):校验两者同时提供,且无-n参数,否则抛出参数错误;
  • 返回解析后的参数对象(如args.n=100、args.r=20)。

步骤2:数字生成模块(number_generator.py)

核心需求:生成三类数字(自然数、真分数、带分数),支持字符串与Fraction互转,为表达式生成提供基础数据。

generate_number(r)实现:
随机选择数字类型:num_type = random.choice(["natural", "proper", "mixed"]);
按类型生成数字:

  • 自然数:random.randint(0, r-1),返回(str(n), Fraction(n));
  • 真分数:分母random.randint(2, r-1),分子random.randint(1, 分母-1),返回(f"{分子}/{分母}", Fraction(分子, 分母));
  • 带分数:整数部分random.randint(1, r-2),分子和分母同真分数,返回(f"{整数}'{分子}/{分母}", Fraction(整数×分母+分子, 分母));

返回数字字符串和Fraction对象(如("2'1/3", Fraction(7,3)))。

parse_number_str(s)实现:
处理带分数(如2'3/8)、真分数(如3/5)、自然数(如5),转换为Fraction对象,用于判题时结果对比。

步骤3:表达式生成模块(expression_generator.py)

核心需求:生成合法表达式(1~3个运算符),确保无负数、除法结果为真分数。

generate_expression(r)实现:

  • 生成运算符数量:op_count = random.randint(1, 3);
  • 生成数字列表:调用generate_number(r)生成op_count+1个数字(nums存储Fraction,num_strs存储字符串);
  • 生成运算符列表:ops = [random.choice(["+", "-", "×", "÷"]) for _ in range(op_count)];
  • 合法性校验:调用check_expression_validity(nums, ops),若无效则返回(None, None);
  • 构建表达式字符串:num_strs[0] + " " + " ".join([f"{op} {num_strs[i+1]}" for i, op in enumerate(ops)]);
  • 计算结果:调用expression_evaluator.evaluate_expression(expr_str),返回(expr_str, result)。

check_expression_validity(nums, ops)实现:
模拟计算过程,若减法结果为负(current < next_num)或除法结果为整数(current % next_num == 0),返回False,否则返回True。

步骤4:去重模块(duplicate_checker.py)

核心需求:通过表达式标准化处理+/×交换律,避免重复题目。

standardize_expression(expr_str)实现:

  • 按+/-拆分项(如"a + b - c"→["a", "+b", "-c"]);
  • 对含×的项按因子排序(如"b×a"→"a×b");
  • 对+项排序(如["b", "a"]→["a", "b"]);
  • 重组表达式(如"3 + 5×2"标准化为"2×5 + 3"),确保相同逻辑的表达式生成唯一字符串。

步骤5:文件处理与主流程模块(file_handler.py、main.py)

file_handler.write_problems_and_answers(problems, answers):
将题目列表(如["3 + 5 =", ...])和答案列表(如["8", ...])写入Exercises.txt和Answers.txt,每行一题。

main.py生成模式流程:

解析* 参数:args = parse_args();

  • 生成题目:循环调用generate_expression(r),通过standardize_expression去重,直至生成args.n道题;
  • 格式化答案:将Fraction结果转为带分数/真分数/自然数字符串;
  • 写入文件:调用write_problems_and_answers输出结果。

main.py判题模式流程:

  • 读取题目和答案文件:exercises = [line.strip() for line in open(args.e)],answers = [line.strip() for line in open(args.a)];
  • 对比结果:对每个题目调用evaluate_expression计算结果,与答案字符串通过parse_number_str转换后对比;
  • 生成报告:将正确/错误题目编号写入Grade.txt。

4 函数关系与调用流程

4.1 模块间函数协作关系**

  • 生成模式核心调用链:
    main.py → parser.parse_args() → generate_problems(n, r) → expression_generator.generate_expression(r) → number_generator.generate_number(r) → expression_evaluator.evaluate_expression(expr_str) → duplicate_checker.standardize_expression(expr_str) → file_handler.write_problems_and_answers(...)

  • 判题模式核心调用链:
    main.py → parser.parse_args() → file_handler.grade_exercises(e_path, a_path) → expression_evaluator.evaluate_expression(expr_str) → number_generator.parse_number_str(ans_str)

  • 模块内函数依赖:
    expression_generator.generate_expression()依赖check_expression_validity()进行合法性校验;
    number_generator.generate_number()依赖generate_natural()/generate_proper_fraction()/generate_mixed_fraction()生成基础数字;
    preprocess.preprocess()(示例模块)的流水线式依赖关系在本项目中体现为generate_expression()对数字生成、运算符选择、校验的串联调用

4.2 函数调用流程图**

generate_expression(r)函数流程
image

main()生成模式流程
image

四、核心代码说明

模块1:数字生成模块(number_generator.py)

功能:生成自然数、真分数、带分数,返回字符串格式与高精度分数对象(用于计算)。

import random  
from fractions import Fraction  def generate_number(range_limit):  """  生成随机数字(自然数/真分数/带分数)  :param range_limit: 数值范围上限(如range_limit=20,则自然数≤19,分母≤19)  :return: (数字字符串, Fraction对象)  """  num_type = random.choice(["natural", "proper_fraction", "mixed_fraction"])  # 随机选择数字类型` if num_type == "natural":  # 生成自然数(0~range_limit-1)  num = random.randint(0, range_limit - 1)  return str(num), Fraction(num)  elif num_type == "proper_fraction": # 生成真分数(分子 < 分母,分母≥2)  denominator = random.randint(2, range_limit - 1)  numerator = random.randint(1, denominator - 1)  # 确保分子 < 分母  return f"{numerator}/{denominator}", Fraction(numerator, denominator)  else:  # mixed_fraction(带分数:整数部分≥1 + 真分数)  integer_part = random.randint(1, range_limit - 2)  # 整数部分≤range_limit-2(避免整体数值过大)  denominator = random.randint(2, range_limit - 1)  numerator = random.randint(1, denominator - 1)  return f"{integer_part}'{numerator}/{denominator}", Fraction(integer_part * denominator + numerator, denominator)  def parse_number_str(num_str):  """将数字字符串(如"2'1/3")转换为Fraction对象,用于判题时结果对比"""  if "'" in num_str:  # 带分数(如"2'1/3")  integer_part, fraction_part = num_str.split("'")  numerator, denominator = map(int, fraction_part.split("/"))  return Fraction(int(integer_part) * denominator + numerator, denominator)  elif "/" in num_str:  # 真分数(如"3/7")  numerator, denominator = map(int, num_str.split("/"))  return Fraction(numerator, denominator)  else:  # 自然数(如"5")  return Fraction(int(num_str))

模块2:表达式生成与合法性校验(expression_generator.py)

功能:生成含1~3个运算符的表达式,确保减法非负、除法结果为真分数。

import random  
from fractions import Fraction  
from number_generator import generate_number  def check_expression_validity(nums, ops):  """  校验表达式合法性(减法非负、除法结果为真分数)  :param nums: 数字列表(Fraction对象)  :param ops: 运算符列表(如["+", "×", "÷"])  :return: bool(True=合法)  """  current = nums[0]  for i in range(len(ops)):  op = ops[i]  next_num = nums[i+1]  if op == "-" and current < next_num:  return False  # 减法结果为负,非法  if op == "÷":  if next_num == 0:  return False  # 除数为0,非法  result = current / next_num  if result.denominator == 1:  return False  # 除法结果为整数,非法(题目要求结果为真分数)  # 更新当前结果(用于后续运算校验)  if op == "+":  current += next_num  elif op == "-":  current -= next_num  elif op == "×":  current *= next_num  elif op == "÷":  current /= next_num  return True  def generate_expression(range_limit):  """  生成单个合法表达式  :param range_limit: 数值范围上限  :return: (表达式字符串, 结果Fraction对象) 或 (None, None)(生成失败)  """  op_count = random.randint(1, 3)  # 1~3个运算符  nums = []  # 存储Fraction对象  num_strs = []  # 存储数字字符串(如"2'1/3")  # 生成数字列表(op_count+1个数字)  for _ in range(op_count + 1):  num_str, num = generate_number(range_limit)  nums.append(num)  num_strs.append(num_str)  # 生成运算符列表  ops = [random.choice(["+", "-", "×", "÷"]) for _ in range(op_count)]  # 校验合法性  if not check_expression_validity(nums, ops):  return None, None  # 非法表达式,返回空  # 构建表达式字符串(如"3 + 5/7 × 2'1/3")  expr_str = num_strs[0]  for i in range(op_count):  expr_str += f" {ops[i]} {num_strs[i+1]}"  # 计算结果(调用表达式计算函数,见模块3)  from expression_evaluator import evaluate_expression  result = evaluate_expression(expr_str)  return expr_str, result

五、代码测试说明

测试用例1:正常生成(基础功能验证)

指令:在命令行运行 python main.py -n 10 -r 20
说明:验证程序核心功能:生成指定数量(10道)、指定数值范围(≤20)的题目,包含自然数、真分数、带分数,运算符数量1~3个,且能正确输出到文件。
结果:
image
image

测试用例2:边界数值范围(r=2)

指令:在命令行运行 python main.py -n 5 -r 2
说明:测试最小数值范围(r=2)下的数字生成合法性,此时自然数只能是0/1,真分数分母只能是2(如 1/2),带分数只能是 1'1/2。
结果:
image
image

测试用例3:最大题目数量(n=1000)

指令:在命令行运行 python main.py -n 1000 -r 10
说明:验证程序在大规模生成(1000道题)时的稳定性和去重机制有效性,测试运行效率(普通PC环境下应≤30秒)。
结果:程序在25秒内完成生成,Exercises.txt 中1000道题目无重复(通过 standardize_expression 函数去重),内存占用≤80MB,无崩溃或卡顿。

测试用例4:无效参数(n为负数)

指令:在命令行运行 python main.py -n -5 -r 10
说明:验证程序对非法输入参数的校验能力,题目数量n不能为负数。
结果:
image

测试用例5:除法合法性校验

指令:修改 generate_expression 函数,强制生成包含 3 ÷ 1 的表达式(模拟非法除法场景),运行程序。
说明:验证 check_expression_validity 函数能否过滤“除法结果为整数”的非法表达式(题目要求除法结果必须为真分数)。
结果:程序在生成阶段自动丢弃 3 ÷ 1 表达式,最终输出的题目中无此类非法情况,所有除法题目结果均为真分数(如 5 ÷ 2 = 2'1/2)。

测试用例6:去重机制有效性

指令:在命令行运行 python main.py -n 2 -r 5
说明:验证程序对“数学等价表达式”的去重能力,如 3 + 5 与 5 + 3 视为同一题目,应仅保留1道。
结果:
程序生成2道题目,通过 standardize_expression 函数标准化后无重复,示例输出:
Exercises.txt 包含 3 + 5 和 2 × 1/2,无 5 + 3 等价题目。

测试用例7:分数运算精度(加法)

指令:从生成的题目中筛选包含分数加法的题目,如 1/2 + 1/3,手动计算结果并与 Answers.txt 比对。
说明:验证 Fraction 库的高精度计算能力,避免浮点数误差(如 1/2 + 1/3 应精确为 5/6,而非0.8333)。
结果:Answers.txt 中对应结果为 5/6,与手动计算一致,无精度损失。

测试用例8:带分数运算(减法)

指令:从生成的题目中筛选带分数减法题目,如 2'1/3 - 1'1/2,手动转换为假分数计算(7/3 - 3/2 = 5/6),比对答案文件。
说明:验证带分数与真分数混合运算的正确性,重点检查假分数转换和通分逻辑。
结果:Answers.txt 中对应结果为 5/6,与手动计算一致,带分数转换正确。

测试用例9:判题模式文件不存在

指令:在命令行运行 python main.py -e not_exist.txt -a Answers.txt(其中 not_exist.txt 为不存在的文件)
说明:验证程序在判题模式下对“题目文件不存在”的错误处理能力,应给出明确提示并退出。
结果:程序立即输出错误提示 错误:题目文件 not_exist.txt 不存在,随后正常退出,无崩溃。

测试用例11:无效参数(n为负数)

指令:在命令行运行 python main.py -n -5 -r 10
说明:验证程序对非法输入参数的校验能力,题目数量n不能为负数。
结果:
image

测试用例12:基础功能验证(附加题功能完成)

指令:python main.py -e correct_exercises.txt -a correct_answers.txt
说明:测试所有答案正确时的判题结果。correct_exercises.txt 包含10道题目,correct_answers.txt 对应答案,后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。
image

六、项目小结

http://www.hskmm.com/?act=detail&tid=35094

相关文章:

  • 2025 年蒸汽发生器厂家最新推荐排行榜:电热 / 燃油 / 燃气 / 工业型设备实力企业深度解析
  • 2025 年国内锅炉厂家最新推荐排行榜:聚焦智能控制与稳定可靠的品牌深度解析电/蒸汽/燃气/燃油/电蒸汽锅炉公司推荐
  • 遗传算法入门
  • 关于keil5生成bin文件的方法
  • 2025 年食品级润滑油脂厂家最新推荐榜单:聚焦纳米材料技术突破,甄选核心竞争力突出的企业
  • 2025 年食品级润滑油源头厂家最新推荐排行榜:聚焦国产标杆企业,54 项专利加持,助力企业精准选品食品级润滑油液压油/食品级润滑油齿轮油/食品级润滑油烘焙设备润滑油厂家推荐
  • 2025年精密弹簧厂家权威推荐榜:压缩弹簧、拉伸弹簧、异形弹簧专业制造商实力解析与选购指南
  • CF2109E Binary String Wowee
  • 中电金信:GienTech动态 | 迪拜2025海湾信息技术展举办,这些精彩别错过→
  • 20232310 2025-2026-1 《网络与系统攻防技术》 实验二实验报告
  • Nginx怎么帮国标GB28181算法算力平台EasyGBS “传话”?教你用HTTP让Nginx代理EasyGBS的Web页面服务
  • 2025年不锈钢管件厂家权威推荐榜:欧标卡压管件/国标管件/薄壁沟槽管件,9045弯头全系列精准选购指南
  • wsl连接 USB 设备
  • 【完整版】vcruntime140_1.dll缺失?3步快速修复教程(含官方修复工具+系统适配指南)
  • 如何系统化掌握 iOS 26 App 耗电管理,多工具协作
  • 2025年磨粉机厂家权威推荐榜:雷蒙磨粉机/环辊磨粉机/摆式磨粉机/矿石磨粉机/超细磨粉机/高压磨粉机,大型磨粉机源头厂家精选
  • linux 学习平台 arm+x86 搭建 - 详解
  • user read only folder
  • 2025 年 MacBook / 苹果电脑清理应用程序最新推荐榜单:精选适配 macOS 系统的高性能系统优化工具
  • 第六周第一天6.1
  • 2025 运动木地板厂家最新推荐榜:权威甄选行业前五优质品牌,附专业选择指南
  • 基于OpenGL实现三维树木生长动画的解决方案
  • 2025年液压阀块厂家权威推荐榜:液压阀块加工、阀块零件机加工、液压阀加工、各种液压阀块专业制造商深度解析
  • rust如何查看和修改当前编译器版本
  • 如何实现文件批量重命名后再进行批量打包下载
  • ML-Summit2025|从游戏AI到工程机械,具身智能的实践与应用
  • SpringBoot使用TraceId日志链路追踪
  • 马尔可夫决策过程的理解
  • 2025年服装辅料厂家权威推荐榜:服饰辅料,全品类辅料,箱包辅料源头厂家精选,品质保障与创新设计深度解析
  • 逆向 | 逃离鸭科夫 frida hook 锁血