这个作业属于哪个课程 | 班级的链接 |
---|---|
这个作业要求在哪里 | 作业要求的链接 |
这个作业的目标 | 要求实现一个自动生成小学四则运算题目的命令行程序 |
一、合作成员
学号 | 姓名 | github仓库地址 |
---|---|---|
3122004819 | 陈善能 | https://github.com/alenbear/Elementary_arithmetic |
3122004351 | 李明佳 | https://github.com/alenbear/Elementary_arithmetic |
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
Estimate | 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1000 | 1140 |
Analysis | 需求分析 (包括学习新技术) | 30 | 60 |
Design Spec | 生成设计文档 | 50 | 40 |
Design Review | 设计复审 (和同事审核设计文档) | 20 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
Design | 具体设计 | 30 | 50 |
Coding | 具体编码 | 550 | 660 |
Code Review | 代码复审 | 30 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 300 | 240 |
Reporting | 报告 | 60 | 80 |
Test Report | 测试报告 | 30 | 40 |
Size Measurement | 计算工作量 | 15 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 15 | 20 |
Total | 合计 | 1090 | 1250 |
三、效能分析
3.1 性能分析图
3.2 程序中消耗的分析
从性能分析结果和代码逻辑来看,程序有以下四个潜在的缺点或改进空间:
3.2.1 Tkinter mainloop 占用大量时间
Tkinter 的 mainloop 占用了大量时间,因为它是 GUI 程序的事件循环。虽然这是 Tkinter 的正常行为,但在需要大量生成算式的场景下,GUI 的响应速度可能会下降。
3.2.2 复合表达式生成没有并行处理
generate_complex_expression 在批量生成时是线性处理的。如果一次生成大量题目,可能导致性能瓶颈。
3.2.3 Fraction 计算的潜在开销
Fraction 计算是高精度计算,但其性能可能不如简单的整数或浮点运算。如果数值范围过大,分数的计算速度会慢下来。
3.2.4 表达式格式化的冗余操作
在表达式生成过程中,字符串拼接和 format_fraction 的调用存在重复的计算。尤其是在每次生成表达式时都需要进行格式化输出,可能会增加一定的开销。
3.3 描述改进的思路
3.3.1 改进:
如果需要频繁生成大量题目,可以将题目生成部分与 GUI 主循环解耦,使用多线程或异步操作来避免阻塞主线程。例如,可以将生成问题的逻辑放在后台线程中执行,以确保 GUI 不被卡住。
3.3.2 改进:
可以考虑并行化处理,使用多线程或多进程来加速批量生成问题,尤其在生成大量题目时,可以显著提高效率
3.3.3 改进:
如果数值范围较大,可以考虑限制分子和分母的范围,或者只在生成小范围的分数时使用 Fraction,以减少分数处理的复杂度。
3.3.4 改进:
可以将格式化操作进行优化,例如在生成过程中缓存已经格式化的表达式,减少不必要的重复运算。
四、设计实现过程
4.1 主要函数的解析
4.1.1 generate_sub_expression(max_range)
该函数用于随机生成一个子表达式,即一个分数。它接收一个max_range参数,确保分子和分母都在1到max_range-1之间,并且分母不为零。它使用random.randint来生成分子和分母,并返回一个Fraction对象。
def generate_sub_expression(max_range):
numerator = random.randint(1, max_range - 1)
denominator = random.randint(1, max_range - 1)
while denominator == 0: # 确保分母不为零
denominator = random.randint(1, max_range - 1)
return Fraction(numerator, denominator)
4.1.2 format_fraction(fraction)
该函数接收generate_sub_expression函数产生的Fraction对象作为参数,并将其格式化为字符串形式的真分数或带分数。若分数为零,则直接返回字符串"0"。函数首先检查分子是否为负,然后处理分子大于或等于分母的情况(带分数),或者分子小于分母的情况(真分数)。最后,它返回一个格式化的字符串,表示这个分数。
def format_fraction(fraction):
if fraction.numerator == 0:
return "0" # 处理零的情况
negative = fraction.numerator < 0
abs_numerator = abs(fraction.numerator)
denominator = fraction.denominator
if abs_numerator >= denominator:
integer_part = abs_numerator // denominator
remainder = abs_numerator % denominator
if remainder == 0:
return f"{'-' if negative else ''}{integer_part}"
else:
return f"{'-' if negative else ''}{integer_part}'{remainder}/{denominator}"
else:
return f"{'-' if negative else ''}{abs_numerator}/{denominator}"
4.1.3 generate_questions(num_questions, max_range)
该函数用于生成指定数量的题目和答案。其循环调用generate_complex_expression函数,直到生成了足够数量的不同题目(确保没有重复的题目)。然后,它调用write_to_file函数将题目和答案写入文件,并返回生成的题目和答案列表。
def generate_questions(num_questions, max_range):
questions = []
answers = []
while len(questions) < num_questions:
question, answer = generate_complex_expression(max_range)
if question not in questions:
questions.append(question)
answers.append(answer)
write_to_file(questions, answers)
return questions, answers
4.1.4 generate_complex_expression(max_range)
该函数主要用于生成并计算一个包含随机子表达式和运算符的复杂数学表达式,通过确定子表达式的数量,并调用generate_sub_expression函数来生成子表达式。首先随机选择运算符(加、减、乘、除)来构建复合表达式。函数将表达式中的分数进行格式化,并替换运算符符号以更清晰地展示表达式。然后其将计算得出的表达式的最终数值结果转换为分数形式以便于展示,最后返回格式化后的表达式字符串和最终结果的分数形式,生成了既包含表达式又包含其计算结果的完整输出。
关键函数(详见5)
4.1.5 create_gui()
该函数使用Tkinter库创建的图形用户界面(GUI)应用程序,生成和显示四则运算题目及其答案,优化界面的同时提高交互体验。
def create_gui():
window = tk.Tk()
window.title("四则运算生成器")
window.geometry("400x400")
tk.Label(window, text="生成题目数量:").pack(pady=10)
num_entry = tk.Entry(window)
num_entry.pack()
tk.Label(window, text="数值范围(最大值):").pack(pady=10)
range_entry = tk.Entry(window)
range_entry.pack()
questions = []
answers = []
def show_questions():
num_questions = int(num_entry.get())
max_range = int(range_entry.get())
nonlocal questions, answers
questions, answers = generate_questions(num_questions, max_range)
question_window = tk.Toplevel(window)
question_window.title("生成的题目")
text_area = scrolledtext.ScrolledText(question_window, width=100, height=20)
text_area.pack()
for i, q in enumerate(questions):
text_area.insert(tk.END, f"{i + 1}. {q}\n")
def show_answers():
answer_window = tk.Toplevel(window)
answer_window.title("生成的答案")
text_area = scrolledtext.ScrolledText(answer_window, width=50, height=20)
text_area.pack()
for i, a in enumerate(answers):
text_area.insert(tk.END, f"{i + 1}. {a}\n")
tk.Button(window, text="显示题目", command=show_questions).pack(pady=10)
tk.Button(window, text="显示答案", command=show_answers).pack(pady=10)
window.mainloop()
4.1.6 write_to_file(questions, answers)
该函数将生成的题目和答案写入到两个文件中:Exercises.txt和Answers.txt。它遍历题目和答案的列表,将它们以编号的形式写入对应的文件中
`
def write_to_file(questions, answers):
with open('Exercises.txt', 'w', encoding='utf-8') as q_file, open('Answers.txt', 'w', encoding='utf-8') as a_file:
for i, (q, a) in enumerate(zip(questions, answers)):
q_file.write(f"{i + 1}. {q}\n")
a_file.write(f"{i + 1}. {a}\n")
`
4.2 函数之间的关系
五、代码说明
展示出项目关键代码,并解释思路与注释说明
generate_complex_expression(max_range):
生成一个包含随机子表达式和运算符的复合表达式,并计算其结果。
参数:
max_range (int): 子表达式中数字(分子和分母)的最大范围(包括边界)。
返回:
tuple: 包含两个元素的元组。
- 第一个元素是格式化后的复合表达式字符串。
- 第二个元素是表达式计算结果的混合分数表示。
def generate_complex_expression(max_range):
num_sub_expressions = random.randint(3, 4) # 随机生成子表达式的数量
expressions = [] # 存储生成的子表达式(Fraction对象)
results = [] #存储生成的结果
# 生成子表达式
for _ in range(num_sub_expressions):
sub_expr = generate_sub_expression(max_range)
expressions.append(sub_expr)
results.append(sub_expr)
combined_expression = [] # 存储最终的表达式
operators = [] # 存储运算符
# 构建表达式
for i in range(num_sub_expressions):
combined_expression.append(expressions[i]) # 添加子表达式
if i < num_sub_expressions - 1: # 不是最后一个元素
operator = random.choice(['+', '-', '*', '/'])
combined_expression.append(operator) # 添加运算符
operators.append(operator) # 存储运算符
# 计算结果,先乘除后加减
total_result = [] # 存储中间结果
current_value = combined_expression[0] # 初始化当前值为第一个子表达式
for i in range(1, len(combined_expression)):
if isinstance(combined_expression[i], str): # 如果是运算符
if combined_expression[i] in ['*', '/']: # 乘法和除法
if combined_expression[i] == '*':
current_value *= combined_expression[i + 1]
elif combined_expression[i] == '/':
current_value /= combined_expression[i + 1]
else: # 加法和减法
total_result.append(current_value) # 将当前值添加到结果列表
total_result.append(combined_expression[i]) # 添加运算符
current_value = combined_expression[i + 1] # 更新当前值
total_result.append(current_value) # 添加最后的值
# 计算加减法结果
final_result = total_result[0]
for i in range(1, len(total_result), 2): # 遍历运算符
if total_result[i] == '+':
final_result += total_result[i + 1]
elif total_result[i] == '-':
final_result -= total_result[i + 1]
# 格式化输出
formatted_expression = []
for item in combined_expression:
if isinstance(item, Fraction): # 元素是Fraction类型(即分数)
formatted_expression.append(format_fraction(item))
else:
formatted_expression.append(item)
final_expression = " ".join(map(str, formatted_expression)) # 使用map函数将formatted_expression列表中的每个元素转换为字符串,并使用" ".join()方法将它们连接成一个字符串
final_expression = final_expression.replace('*', '×').replace(' / ', ' ÷ ') #替换字符串中的'*'为'×',' / '为' ÷ ',使表达式更易于阅读
final_result_as_mixed = format_fraction(final_result)
return final_expression, final_result_as_mixed
六、测试运行
6.1 部分测试用例展示
6.1.1 生成题目数量或范围为空
6.1.2 输入类型非整数
6.1.3 大量题目生成
题目:
答案:
七、项目展示
用户界面:
主页展示
题目展示
答案展示
文档展示
题目文档
答案文档
八、项目小结
成功之处
-
功能实现:代码成功实现了随机生成包含分数和四则运算的复合表达式,并计算其结果的功能。
-
提供了GUI界面,GUI界面简洁明了,用户可以通过输入框和按钮轻松操作,允许用户输入生成题目的数量和数值范围,并展示生成的题目和答案。
-
分析性能测试结果,了解程序的实际运行情况,并据此制定优化策略,对代码进行了较充足的优化,提升程序性能,减少不必要的计算,减少资源消耗。
-
对代码进行了多次测试与调整,确保程序的健壮性同时增强代码可读性和可维护性。
不足之处
占用内存较多,可以减少不必要的数据复制,特别是在处理多条数据参数的庞大数据集时。可以使用适当的数据类型和大小,避免使用比实际需要更大的内存空间,及时释放不再使用的内存资源,避免内存泄漏。
亮点
数据结构层面上 充分利用了fractions.Fraction类来处理分数,这使得分数的表示和操作(如加减乘除)更加准确和便于处理,同时
将Fraction对象格式化为更易读的字符串形式,包括处理整数部分、假分数转换为带分数以及处理负数,大大提高了代码运行效率以及优化了存储空间。
算法优化层面上 在构建最终表达式时,代码通过维护一个combined_expression列表来跟踪所有的子表达式和运算符。然后,通过遍历这个列表,并使用format_fraction函数来格式化分数,最终生成了一个格式化的字符串表达式。这个过程不仅保证了表达式的准确性,还提高了代码效率,同时在在generate_questions函数中,通过检查新生成的题目是否已存在于questions列表中,来避免生成重复的题目,是典型的空间换时间的代码优化策略。
感受
在结对编程的过程中,我们共同面对了代码中的挑战,并相互学习、相互启发。通过讨论和协作,我们能够更快地找到问题的解决方案,并提高了代码的质量。同时,促进了我们之间的沟通和协作能力。
标签:结对,项目,window,生成,range,questions,expression,表达式 From: https://www.cnblogs.com/lmjisgenius/p/18433670