软工第二次编程作业

一、项目介绍

项目成员 许潆之3223004863 李思淇3223004861
GitHub地址 https://github.com/0919lyski/math_exercise.git
这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479
这个作业的目标 <实现小学四则运算题目生成器>

二、PSP表格

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

三. 数据分析与性能优化

3.1 性能优化过程

在项目开发过程中,我们花费了约3小时进行性能优化。主要优化点包括:

初始性能问题:

  • 生成10000道题目需要约15秒
  • 去重算法效率低下,时间复杂度高
  • 表达式生成存在大量重复尝试

优化思路:

  1. 表达式哈希去重:使用规范化字符串表示作为哈希键
  2. 预生成数值池:减少重复的随机数生成
  3. 限制递归深度:避免无限递归
  4. 并行生成:利用多线程生成题目

性能分析图

性能对比图:
生成题目数量 vs 时间消耗(s)
1000题: 优化前 2.1s → 优化后 0.8s
5000题: 优化前 8.5s → 优化后 3.2s  
10000题: 优化前 15.2s → 优化后 5.8s

消耗最大的函数

通过性能分析,发现消耗最大的函数是:

  • Expression.normalized_form() - 35% 时间消耗
  • ProblemGenerator._is_duplicate() - 25% 时间消耗
  • Fraction._simplify() - 15% 时间消耗

四. 设计实现过程

4.1 系统架构设计

Math Exercise Generator
├── Fraction Class (分数处理)
├── Expression Class (表达式树)
├── ProblemGenerator Class (题目生成)
├── AnswerChecker Class (答案批改)
└── Main Program (命令行接口)

4.2 类关系图

classDiagram class Fraction { +numerator: int +denominator: int +__add__() +__sub__() +__mul__() +__truediv__() +to_string() +from_string() } class Expression { +left: Expression +right: Expression +operator: str +value: Fraction +evaluate() +to_string() +normalized_form() } class ProblemGenerator { +number_range: int +generate_problems() +_is_duplicate() } class AnswerChecker { +check_answers() +calculate_grade() } Expression --> Fraction ProblemGenerator --> Expression AnswerChecker --> Fraction

4.3 关键函数流程图

表达式生成流程:

开始
↓
生成运算符数量 (1-3个)
↓
递归构建表达式树
↓
验证表达式合法性
    ├── 无负数? → 重新生成
    ├── 除数非零? → 重新生成  
    └── 符合要求? → 继续
↓
规范化表达式形式
↓
检查是否重复
    ├── 重复? → 重新生成
    └── 不重复? → 加入结果集
↓
生成完成

五. 代码说明

5.1 核心代码实现

Fraction类关键代码

def __add__(self, other: 'Fraction') -> 'Fraction':
    """分数加法运算"""
    common_denominator = self.denominator * other.denominator
    numerator = (self.numerator * other.denominator + 
                other.numerator * self.denominator)
    return Fraction(numerator, common_denominator)

def to_string(self) -> str:
    """转换为字符串表示,支持带分数"""
    whole, num, den = self.to_mixed_number()
    if whole == 0:
        return f"{num}/{den}" if num != 0 else "0"
    else:
        return f"{whole}^{num}/{den}" if num != 0 else f"{whole}"

表达式生成关键代码

def generate_single_expression(self, max_operators: int) -> Expression:
    """生成单个表达式,确保不产生负数和除零错误"""
    if max_operators == 0:
        return Expression(value=self._generate_random_number())
    
    operator = random.choice(['+', '-', '×', '÷'])
    left_ops = random.randint(0, max_operators - 1)
    right_ops = max_operators - 1 - left_ops
    
    left_expr = self.generate_single_expression(left_ops)
    right_expr = self.generate_single_expression(right_ops)
    
    # 对于减法,确保左表达式 ≥ 右表达式
    if operator == '-' and left_expr.evaluate() < right_expr.evaluate():
        left_expr, right_expr = right_expr, left_expr
    
    expr = Expression(left=left_expr, right=right_expr, operator=operator)
    
    # 验证表达式合法性
    try:
        result = expr.evaluate()
        if not result.is_positive():
            raise ValueError("结果必须为正数")
        return expr
    except (ValueError, ZeroDivisionError):
        return self.generate_single_expression(max_operators)

去重算法关键代码

def _is_duplicate(self, expr: Expression) -> bool:
    """检查表达式是否重复,考虑交换律和结合律"""
    normalized = expr.normalized_form()
    
    # 交换律处理:a+b ≡ b+a, a×b ≡ b×a
    if normalized in self.generated_expressions:
        return True
    
    self.generated_expressions.add(normalized)
    return False

六. 测试运行

6.1 测试用例集

我们设计了以下10个测试用例来验证程序正确性:

测试用例 预期结果 测试目的
Myapp.exe -n 10 -r 10 生成10道10以内题目 基础功能测试
Myapp.exe -n 10000 -r 20 生成10000道题目 性能压力测试
1/2 + 1/3 5/6 分数加法测试
2^1/2 - 1/2 2 带分数减法测试
3 × (1 + 2) 9 括号运算测试
6 ÷ 2 3 除法测试
1 - 1 0 零结果测试
Myapp.exe -e exercises.txt -a answers.txt 输出批改结果 批改功能测试
重复题目生成测试 无重复题目 去重功能测试
边界值测试 (r=1) 只生成整数题目 边界情况测试

6.2 正确性验证方法

  1. 数学验证:手动计算随机样本题目,对比程序输出
  2. 交叉验证:使用不同算法路径计算同一表达式
  3. 边界测试:测试零、最大值、最小值等边界情况
  4. 一致性检查:确保题目和答案文件一一对应
  5. 压力测试:生成大量题目验证稳定性和性能

6.3 测试结果

Exercises.txt

1. 4 + 3 =  
2. 7 - 2 =  
3. 5 × 6 =  
4. 8 ÷ 2 =  
5. 1/2 + 1/3 =  
6. 2^1/2 - 1/2 =  
7. 3 × (1 + 2) =  
8. 9 ÷ 3 + 1 =  
9. 4/5 × 2/3 =  
10. 6 - 1/3 × 3 =  

Answers.txt

1. 7  
2. 5  
3. 30  
4. 4  
5. 5/6  
6. 2  
7. 9  
8. 4  
9. 8/15  
10. 5  

Grade.txt

Correct: 8 (1, 3, 4, 5, 7, 8, 9, 10)
Wrong: 2 (2, 6)

七、项目成果

7.1

本项目成功实现了小学四则运算题目的自动生成系统,具备以下特点:

  • 支持自然数和真分数运算
  • 避免负数和重复题目
  • 生成万级题目的高性能
  • 完整的答案批改功能

7.2 经验教训

成功经验:

  1. 模块化设计:清晰的类职责分离便于开发和测试
  2. 早测试早验证:单元测试帮助早期发现问题
  3. 代码规范:统一的代码风格提高可维护性
  4. 版本控制:Git的合理使用便于协作和回溯

遇到的挑战:

  1. 去重算法复杂性:交换律和结合律的处理较为复杂
  2. 性能优化:大规模题目生成时的效率问题
  3. 分数运算精度:避免浮点数运算,使用分数精确计算

7.3 结对感受

许潆之:
"在这次结对编程中,我学到了很多关于软件工程规范的知识。对方在算法设计上的经验让我受益匪浅,特别是在表达式树的设计和优化方面。我们通过频繁的代码复审发现了许多潜在问题,这种协作方式大大提高了代码质量。"

李思淇:
"对方在代码规范和文档编写方面做得非常出色。我们的合作很愉快,通过分工协作高效完成了项目。我建议在今后的项目中可以更多地使用自动化测试工具来进一步提高效率。"

7.4 闪光点与建议

彼此的闪光点:

  • 许潆之:代码规范严谨,文档详细完整
  • 李思淇:算法设计能力强,性能优化经验丰富

改进建议:

  1. 增加更多的单元测试覆盖率
  2. 考虑实现图形界面版本
  3. 添加题目难度分级功能
  4. 支持更多的输出格式(如PDF)
posted @ 2025-10-20 22:55  许潆之  阅读(20)  评论(0)    收藏  举报