作业3——结对项目
自动生成四则运算程序
属性 | 详细信息 |
---|---|
作业要求 | https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023/homework/13326 |
所属班级 | https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023 |
开发者 | 饶博勋(3123004152)杨超民(3123004161) |
Github仓库 | https://github.com/RaoHush/Cooperation-Project |
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 50 | 30 |
Estimate | 估计这个任务需要多少时间 | 10 | 6 |
Development | 开发 | 100 | 50 |
Analysis | 需求分析(包括学习新技术) | 30 | 20 |
Design Spec | 生成设计文档 | 15 | 10 |
Design Review | 设计复审(和同事审核设计文档) | 5 | 5 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 5 | 5 |
Design | 具体设计 | 24 | 16 |
Coding | 具体编码 | 180 | 200 |
Code Review | 代码复审 | 60 | 80 |
Test | 测试(自我测试,修改代码,提交修改) | 20 | 30 |
Reporting | 报告 | 10 | 15 |
Test Report | 测试报告 | 10 | 15 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 5 | 3 |
合计 | 534 | 495 |
设计实现过程
本程序用于通过给定参数随机生成四则运算题目和答案。以下是使用说明,各功能函数设计及分析。
使用说明
生成python main.py -n<生成题目数量> -r<数值范围>
检验python main.py -e<题目文件> -a<答案文件>
题目结果保存到Exercises.txt
答案结果保存到Answers.txt
正确率结果保存到Grade.txt
各函数设计与分析
真分数处理函数
def format_fraction(f):
if isinstance(f, fractions.Fraction):
if f.denominator == 1:
return str(f.numerator)
whole = f.numerator // f.denominator
remainder = f.numerator % f.denominator
if whole > 0:
return f"{whole}'{remainder}/{f.denominator}" if remainder else f"{whole}"
return f"{f.numerator}/{f.denominator}"
return str(f)
用于将假分数转换为真分数,统一格式
算式生成函数
def generate_number(r, non_zero=False):#生成安全数值
if random.choice([True, False]):
return random.randint(1 if non_zero else 0, r - 1)
else:
numerator = random.randint(1, r - 1)
denominator = random.randint(max(numerator + 1, 2), r)
if non_zero:
while denominator == 0:
denominator = random.randint(max(numerator + 1, 2), r)
return fractions.Fraction(numerator, denominator)
def generate_expression(r, ops_remaining=1):
if ops_remaining == 0:
num = generate_number(r)
return format_fraction(num), num
left_ops = random.randint(0, ops_remaining - 1)
right_ops = ops_remaining - 1 - left_ops
operator = random.choice(['+', '-', '×', '÷'])
# 递归生成子树
e1_str, e1_val = (generate_expression(r, left_ops) if left_ops > 0 else
(format_fraction(generate_number(r)), generate_number(r)))
e2_str, e2_val = (generate_expression(r, right_ops) if right_ops > 0 else
(format_fraction(generate_number(r)), generate_number(r)))
# 处理运算符约束
if operator == '-':
if e1_val < e2_val:
e1_str, e2_str = e2_str, e1_str
e1_val, e2_val = e2_val, e1_val
elif operator == '÷':
e2_str, e2_val = (format_fraction(generate_number(r, True)), generate_number(r, True))
while e2_val == 0:
e2_str, e2_val = (format_fraction(generate_number(r, True)), generate_number(r, True))
# 交换律规范化:确保加法/乘法的操作数按字典序排列
if operator in ['+', '×']:
if e1_str > e2_str: # 字典序比较
e1_str, e2_str = e2_str, e1_str
e1_val, e2_val = e2_val, e1_val
# 智能括号优化
op_priority = {'+': 1, '-': 1, '×': 2, '÷': 2}
e1_needs_parens = any(op in e1_str for op in ['+', '-']) and op_priority[operator] > 1
e2_needs_parens = any(op in e2_str for op in ['+', '-']) and op_priority[operator] > 1
if e1_needs_parens:
e1_str = f"({e1_str})"
if e2_needs_parens:
e2_str = f"({e2_str})"
expr_str = f"{e1_str} {operator} {e2_str}"
return expr_str, calculate_expression(expr_str)
def generate_problems(n, r):
exercises = []
answers = []
seen_expressions = set() # 用于记录已生成的表达式
for problem_num in range(1, n + 1):
for _ in range(100):
ops_count = random.randint(1, 3)
expr_str, answer = generate_expression(r, ops_count)
# 检查表达式是否重复
if expr_str in seen_expressions:
continue
seen_expressions.add(expr_str)
if answer.denominator != 0 and answer >= 0:
actual_ops = sum(expr_str.count(op) for op in ['+', '-', '×', '÷'])
if actual_ops == ops_count:
verified_answer = format_fraction(calculate_expression(expr_str))
if format_fraction(answer) == verified_answer:
exercises.append(f"{problem_num}. {expr_str} = ")
answers.append(f"{problem_num}. {verified_answer}")
break
else:
raise ValueError(f"生成失败,请增大-r值(当前r={r})")
with open("Exercises.txt", "w") as f:
f.write("\n".join(exercises))
with open("Answers.txt", "w") as f:
f.write("\n".join(answers))
generate_number
用于生成符合规范的数字
generate_expression
用于生成算式。
-1.使用递归方式生成算式,随机生成子树以随机运算符的个数;
-2.对有更多限制的除法和减法有单独的处理;
-3.随机生成括号;
-4.规范化算式,避免重复生成一样的算式。
generate_problems
用于生成问题,判断重复表达式,将算式与答案写入txt文件
generate_problems使用generate_expression,generate_expression使用generate_number
结果计算函数
def calculate_expression(expr):
try:
expr = expr.replace('×', '*').replace('÷', '/')
expr = re.sub(r"(\d+)'(\d+)/(\d+)", r"Fraction(\1*\3+\2,\3)", expr)
expr = re.sub(r"(\d+)/(\d+)", r"Fraction(\1,\2)", expr)
expr = re.sub(r"\b(\d+)\b", r"Fraction(\1,1)", expr)
for match in re.finditer(r"Fraction\(\d+,\s*(-?\d+)\)", expr):
denominator = int(match.group(1))
if denominator == 0:
raise ZeroDivisionError("Generated expression has division by zero.")
return eval(expr, {'__builtins__': None}, {'Fraction': fractions.Fraction})
except ZeroDivisionError:
raise
except Exception as e:
raise ValueError(f"Error calculating expression: {e}")
先处理计算符,再去除题号,检验是否有除以0的操作。
生成成绩函数
def grade_answers(exercise_file, answer_file):
with open(exercise_file) as f:
exercises = f.readlines()
with open(answer_file) as f:
answers = f.readlines()
correct, wrong = [], []
for ex, ans in zip(exercises, answers):
try:
# 解析题号
ex_num, ex_expr = ex.strip().split('.', 1)
ans_num, ans_val = ans.strip().split('.', 1)
if int(ex_num) != int(ans_num):
raise ValueError("题号不匹配")
# 计算正确答案
expr = ex_expr.split('=', 1)[0].strip()
correct_answer = format_fraction(calculate_expression(expr))
# 比对答案
if ans_val.strip() == correct_answer:
correct.append(int(ex_num))
else:
wrong.append(int(ex_num))
except Exception as e:
wrong.append(int(ex_num)) # 如果出错,也归类为错误答案
# 输出结果
with open("Grade.txt", "w") as f:
f.write(f"Correct: {len(correct)} ({', '.join(map(str, sorted(correct)))})\n")
f.write(f"Wrong: {len(wrong)} ({', '.join(map(str, sorted(wrong)))})\n")
解析题号,计算结果,对比答案,返回正确题目数量,错误题号。
性能分析
覆盖率
生成功能测试
校对功能测试
运行分析
测试运行
1.大量生成数据:可支持10000条算式生成;
2.重复性检测:当n数量过大,r范围过小时,有可能无法产生足够数量的算式(排列组合所有情况不够的),这时会提示调整r的取值;同时测试过查重。
3.除零检测:已在函数中规避。
项目小结
成果总结:
功能实现:成功开发四则运算题目自动生成与答案校验程序,可按用户指定数量和范围生成题目、计算答案,分别保存至文件,还能校验题目与答案文件并输出正确率。
经验总结
代码优化:使用集合记录已生成表达式避免重复,通过性能分析进一步优化代码,提高程序效率和稳定性。
错误处理:完善的错误处理机制是保证程序健壮性的关键,对可能出现的错误情况进行充分考虑并通过异常处理有效应对。
团队协作:沟通协作在设计和代码复审中发挥重要作用,能及时发现并修正潜在问题,提升项目质量。