小学生四则运算之做到晚上不用睡觉版(python实现)

1. Github地址及项目成员


2. PSP表格:



PSP2.1


Personal Software Process Stages


预估耗时(分钟)


实际耗时(分钟)


Planning


计划


20


20


· Estimate


· 估计这个任务需要多少时间


20


20


Development


开发


1560


1490


· Analysis


· 需求分析 (包括学习新技术)


70


60


· Design Spec


· 生成设计文档


60


60


· Design Review


· 设计复审 (和同事审核设计文档)


80


90


· Coding Standard


· 代码规范 (为目前的开发制定合适的规范)


20


20


· Design


· 具体设计


80


90


· Coding


· 具体编码


1000


905


· Code Review


· 代码复审


120


120


· Test


· 测试(自我测试,修改代码,提交修改)


130


145


Reporting


报告


150


150


· Test Report


· 测试报告


60


70


· Size Measurement


· 计算工作量


30


30


· Postmortem & Process Improvement Plan


· 事后总结, 并提出过程改进计划


60


50


合计


1730


1660

3. 效能分析


输入:-r为10,-n为10000(数字及分数分母范围为10以内,题目数目为10000道)

优化前:程序运行总时间为3.002s

分析及优化过程:

  • 观察性能分析结果发现程序耗时的地方在于随机生成运算数计算过程

  • 除此之外还发现存在冗余计算,同时fraction分数模块和IO读写所占时间较大。

因此,优化过程主要从以上几个方面着手。

优化后:程序运行总时间为1.727s

图1:优化前性能分析

图2:优化后性能分析

  在优化效能的过程中,我们经历了一段很长的讨论(纠结ing),并且在最后的时间内,我们在重新分析需求的时候发现了自己的一个很大的性能优化问题,就是我们在生成的过程中,就已经可以保证不会生成一样(即复合需求)算式,于是最后的查重表达式就变成了我们佐证的一个工具!于是我们又在写了一个测试类(测试是否会出现重复的题目),分别测试了10000道题目,和100W道题目的时候 产生的重复题目,结果如图所示

                                                                                 

                                    1.10000道题目                              2.100W道题目

  在经过试验几遍以及换另一台机器试验几次的多次试验之后,我们还是得到了上述结果,所以我们在此说我们的这种方法(算式二叉树)的方法时最为NB的方法之一,目前为止没有出现BUG!而且效率也挺高的。小学生不用睡觉系列!

4. 设计实现过程


设计思路和小组讨论:

  在阅读题目和分析需求之后,我们组将此次的项目分为三个部分,一是生成运算式并查重;二是计算以及保存题目及答案;最后是记录用户输入答案并且评分。

  通过argparse模块获用户输入参数-n,-r等参数设置来运算范围、生成题目数目和是否产生负数等。

  前期讨论过程中,我们设想使用简单随机填充数字和操作符来生成算术表达式,以及检测答案不一致这种方法来查重,但是效率较低,也不方便后续做扩展。因此,我们又经过了讨论,决定使用二叉树随机生成运算式。在结点Node类里面设置表达式的结构,二叉树Tree类逐层生成含操作符和操作数的运算树(父节点为操作符,叶子节点为操作数,如下图3)。通过赋予运算符优先级op_priority匹配加减乘除的运算过程,并且结合操作符优先级生成对应的中缀表达式。之后通过后缀表达式来生成查重表达式,来检测运算式是否重复(查重思路参考链接:https://www.cnblogs.com/wxrqforever/p/8679118.html)。

图3:表达式二叉树

  接着,结合操作符优先级计算结果,通过FileUtils类将结果保存到exercise.txt文档以及answer.txt文档,最后在控制台中进行答题之后获取成绩grade。

类之间的调用以及各方法解释:

 类说明:

  • calc_cmd.py:主程序类,调用其他类运行。

  • calc_error.py:异常类,定义生成中以及整个运行过程中产生的一些异常

  • calc_util.py:计算类,用于计算算式,并返回相关的异常,进行评分和收集答案

  • expre_tree.py:定义算式二叉树以及结点类的生成方法以及属性等

  • FileUtils.py:文件类,用于储存题目exercise.txt,answer.txt,grade.txt文件

  • FormatUtils.py:生成表达式类如(后缀表达式以及查重表达式),并且将分数转换成真分数形式

  • unit_text.py:测试用类

5. 代码说明


主类中的主函数方法,通过调用各个类的参数进行提交

 1 parser = argparse.ArgumentParser(description="四则运算")
 2 parser.add_argument(‘-n‘, dest=‘number‘, type=int, default=1, help=‘number of generated questions‘)
 3 parser.add_argument(‘-r‘, dest=‘range‘, type=int, default=10, help=‘range of values‘)
 4 parser.add_argument(‘-e‘, dest=‘exercise‘, type=str, help=‘formula expression file‘)
 5 parser.add_argument(‘-a‘, dest=‘answer‘, type=str, help=‘answer expression file‘)
 6 parser.add_argument(‘-g‘, dest=‘grade‘, type=str, help=‘grade file‘)
 7 parser.add_argument(‘-m‘, dest=‘minus‘, default=False, action=‘store_true‘,
 8                     help=‘produce formulas with negative numbers‘)
 9 args = parser.parse_args()
10
11
12 if __name__ == ‘__main__‘:
13     if args.range is None:
14         print("请输入‘-r‘参数控制题目中数值(自然数、真分数和真分数分母)的范围")
15     if args.exercise is None:
16         args.exercise = os.path.join(os.getcwd(), ‘Exercises.txt‘)
17     if args.answer is None:
18         args.answer = os.path.join(os.getcwd(), ‘Answer.txt‘)
19     if args.grade is None:
20         args.grade = os.path.join(os.getcwd(), ‘Grade.txt‘)
21     print("欢迎进入答题模式......(输入‘exit‘可退出程序)")
22     t = Tree()
23     u_answer = list()  # 用户答案
24     formula, s_answer = t.generate_formula(args.range, args.number, args.minus)  # 随机生成表达式
25     FileUtils.write_file(formula, s_answer, args.exercise, args.answer)  # 保存题目文件
26     for i in range(args.number):
27         print(formula[i], end=‘‘)
28         answer = input()  # 获取用户输入的答案
29         if answer == ‘exit‘:
30             print(‘退出程序成功!‘)
31             sys.exit()
32         u_answer.append(answer)
33     correct, wrong = CalculatorUtils.grading(u_answer, s_answer)  # 统计答题结果
34     print("答题结果:")
35     print(correct)
36     print(wrong)
37     FileUtils.write_grade_file(args.grade, correct, wrong)  # 保存答题结果

自定义异常类,包括不为负数、分母不超过某个值,以及运算重复的异常类对象,方便以后做扩展

class NegativeError(Exception):
    """自定义表达式不为负数的异常类"""
    def __init__(self):
        super(NegativeError, self).__init__()  # 初始化父类

    def __str__(self):
        return

class DifficultError(Exception):
    """自定义分母不能超过某个值的异常类"""
    def __init__(self):
        super(DifficultError, self).__init__()  # 初始化父类

    def __str__(self):
        return

class DuplicateError(Exception):
    """自定义异常类"""
    def __init__(self):
        super(DuplicateError, self).__init__()  # 初始化父类

    def __str__(self):
        return

随机生成四则运算表达式

def generate_formula(self, num_range, number, negative):
    """随机生成式子"""
    num = 0
    degree = random.randrange(3, 4)  # 随机设置操作数的个数
    while num < number:
        empty_node = [self.root]
        for _ in range(degree):
            ‘‘‘生成操作符号节点‘‘‘
            node = random.choice(empty_node)
            empty_node.remove(node)
            node.operator = random.choices(self.op_list, cum_weights=self.op_weight)[0]
            # node.operator = random.choices(self.op_list)[0]
            node.type = 2

            # 每生成一个操作符号节点,生成两个空节点
            node.left = Node()
            node.right = Node()
            empty_node.append(node.left)
            empty_node.append(node.right)

        for node in empty_node:
            ‘‘‘将所有空结点变为数字结点‘‘‘
            node.type = 1
            # 设置真分数的比重 1为整数 0为分数
            num_type = random.choices(self.type_list, self.num_weight)[0]
            if num_type == 1:
                # 生成一个整数
                node.number = random.randint(1, num_range)
            else:
                # 生成一个真分数
                node.number = Fraction(random.randint(1, num_range), random.randint(1, num_range))
        try:
            # self.root.show_node()  # 获取生成的二叉树结构
            self.root.get_answer(negative)  # 计算答案
            if self.root.number.denominator > 99:  # 分母超过99抛出异常
                raise DifficultError()

            self.pre_formula = self.root.get_formula()  # 获取前缀表达式
            self.post_formula = FormatUtils.get_result_formula(self.pre_formula)  # 获取后缀表达式
            self.check_formula = FormatUtils.get_check_formula(self.post_formula)  # 获取查重表达式

            # 进行查重
            if not Tree.duplicate_check(self.check_formula, self.result_formula):
                # 返回false 则表明没有重复
                self.result_formula.append(self.check_formula)
            else:
                raise DuplicateError

            output = FormatUtils.standard_output(self.pre_formula)  # 格式化前缀表达式
            if isinstance(self.root.number, Fraction):
                answer = FormatUtils.standard_format(self.root.number)  # 格式化答案
            else:
                answer = self.root.number
            # print(output, answer)
            self.formula.append(output)
            self.answer.append(answer)
        except ZeroDivisionError:
            # print("除数为零,删除该式子")
            continue
        except NegativeError:
            # print("出现负数,删除该式子")
            continue
        except DifficultError:
            # print("题目较难,删除该式子")
            continue
        except DuplicateError:
            # print("题目重复,删除该式子")
            continue
        else:
            num += 1
    return self.formula, self.answer

计算类,可以进行操作数的运算,即生成式子之后的运算结果保存以及抛出相对应的一个异常对象

还包括了返回一个后缀表达式,和最后的评分格式。

 1 class CalculatorUtils:
 2
 3     @staticmethod
 4     def eval_formula(operator, a, b, negative=False):
 5         """计算简单的加减乘除, 同时抛出不符合题目要求的异常"""
 6         answer = 0
 7         if operator == "+":
 8             answer = a + b
 9         elif operator == "-":
10             if a < b and negative is False:
11                 raise NegativeError()  # 抛出结果为负数的异常对象
12             else:
13                 answer = a - b
14         elif operator == "*":
15             answer = a * b
16         elif operator == "/":
17             if b > 99:
18                 raise DifficultError()  # 抛出题目较难的异常对象(分母大于100)
19             else:
20                 answer = a / b
21                 # 如果答案为浮点数,则转换为分数形式
22                 if isinstance(answer, float):
23                     answer = Fraction(a) / Fraction(b)
24         return answer
25
26     @staticmethod
27     def get_answer(formula_list, negative):
28         """计算后缀表达式的结果"""
29         num_list = list()
30         for i in range(len(formula_list)):
31             if isinstance(formula_list[i], int) or isinstance(formula_list[i], Fraction):
32                 num_list.append(formula_list[i])
33             else:
34                 b = num_list.pop()
35                 a = num_list.pop()
36                 res = CalculatorUtils.eval_formula(formula_list[i], a, b, negative)
37                 num_list.append(res)
38         return num_list.pop()
39
40     @staticmethod
41     def grading(user_ans, ans_list):
42         """评分,同时返回要求的评分输出格式"""
43         correct = list()
44         wrong = list()
45         length = len(user_ans)
46         for i, u, ans in zip(range(1, length + 1), user_ans, ans_list):
47             if u == ans:
48                 correct.append(i)
49             else:
50                 wrong.append(i)
51         return correct, wrong

6. 测试运行


分别测试生成前缀表达式:

测试生成后缀表达式:

测试生成查重表达式:

测试生成运算式

测试答题情况及保存文件

7. 项目总结



  在经过了整个项目的洗礼之后,作为项目实现的其中一员,整个项目下来学习到了新的知识,新的语言结构以及数据结构,大佬带着小弟闯荡四方,我出谋划策(瞎说了很多想法),贡献了策略,代码量大部分都是进怀写的,我负责的是一些检测和修改,整个项目下来,我发现了很多知识点上的漏洞,但是也学习到了很多东西,比如说进怀使用的这种二叉树的结构,使我对二叉树有了新的理解,以及对于中缀表达式和后缀表达式的理解也更加深刻。无他,就想猛的夸赞一下队友,太给力了。我们队的这次项目,选择的算法简直无敌,太给劲了。本次项目的缺点也许就是没有一个比较完整的GUI界面,使用的是命令行窗口,但是麻雀虽小,算法和数据结构都不小。但是前期开发经验的不足也是我们的代码冗余度更高,特别是我的原因对需求的分析有一些失误,但是后面知道了自己的错误并且及时修改了意见,希望下次能更好的完成任务,并且能多写写代码。感谢队友!

原文地址:https://www.cnblogs.com/jezing/p/11689225.html

时间: 2024-10-10 10:08:49

小学生四则运算之做到晚上不用睡觉版(python实现)的相关文章

java小学生四则运算带面板版 但我不知道为什么同类变量却进不了动作监听中去

---恢复内容开始--- 1 package yun; 2 import java.util.*; 3 import java.awt.*; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 7 import javax.swing.*; 8 public class number extends JFrame{ 9 10 /** 11 * 作者:范铭祥 12 * 功能:一个简单的小学生四

两人结对完成升级版小学生四则运算

小学生四则运算程序之前是使用c语言编写的,如今根据界面要求,故切换到Java编程环境,借助Eclipse开发工具 主要的4个拓展方向: 1.程序可以设置皮肤功能,可以改变界面的颜色即可. 2.程序可以出单个整数阶乘的题目:如:4!=24 3.程序可以设置答题时间,时间设置为整数,单位为秒,最大不能超过120秒,若超过了答题时间未答题,则提示:时间已到,不能答题. 4.可以出表达式里含有负整数(负整数最小不小于-100)的题目,且负数需要带括号,用户输入的结果不用带括号. 以下是代码展示: 1 i

201571030316/201571030314《小学生四则运算练习软件》结对项目报告

github代码地址:https://github.com/mqqgd/Experiment2 小伙伴儿的博客地址链接:http://www.cnblogs.com/mjuan/p/8715700.html 我的学号:201571030316                  小伴儿的学号:201571030314 一.需求分析    本次实验采用结对编程方式,设计开发一个小学生四则运算练习软件,使之具有以下功能: 由计算机从题库文件中随机选择20道加减乘除混合算式,用户输入算式答案,程序检查答

小学生四则运算

package yun; import java.util.Random; import java.util.*; public class number { /** * 作者:范铭祥 * 功能:一个简单的小学生四则运算生成系统 */ public static void main(String[] args) { int max=4; int min=1; Random random = new Random(); //System.out.println(s); boolean pan=tr

自动生成小学生四则运算(C语言)

我写的这个自动生成小学生四则运算的代码是根据我在百度上看到的一篇博客改的,地址为http://www.cnblogs.com/ys1101/p/4368103.html.它的功能不够完整,只有整数的加.减.乘.除,我在此基础上增加了真分数的加.减.乘.除,及统计分数.我把我写的代码放在了GitHub上,地址为https://github.com/cygoodboy/helloworld.

自动生成小学生四则运算(皮!)

自动生成小学生四则运算(皮!) coding 地址 https://coding.net/u/smile12231/p/Demo/git a·需求分析 在这个家长希望自己的小孩能够赢在起跑线的社会,通常寻找很多的练习给小学生做,所以我们就来编写一个四则运算的软件,你懂我意思吧!这个软件能够 ①丶根据用户想要的出题量生成题目 ②丶生成的题目包括整数和分数的加减乘除 ③丶能够自动判断答案是否正确并给出此次的正确率 ④丶使用 -n 参数控制生成题目的个数,例如执行下面命令将生成10个题目 b丶功能设计

安卓小学生四则运算

安卓Activity: package com.example.count_number; import android.os.Bundle;import android.app.Activity;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText; public class MainActivit

【小学生四则运算】程序第3次冲刺(结项成果发布)

茅山光大大组员:林庆光.卢盛添.黄雄俊.陈梓仪.经过三个阶段的协作努力.通力合作,[小学生四则运算]APP程序已经实现了当初设想的全部功能:用户注册与登录(包括找回密码,记住密码功能),用户注册完成同时成为一个游戏账号(可以进行升级,增加金币功能),其中,程序的核心功能包括加法.减法.乘法.除法.以及混合运算的练习题. APP截图展示: 注册界面 登录界面 首页功能展示 答题界面 答题提示框 显示用户信息 商城金币系统 目前APP的功能已经基本定型,接下来就按照杜老师的要求,尝试对APP进行推广

结对子实验——小学生四则运算

实验开始时间:4月7日~4月9日 本次实验的组员分别是:郑泽成http://www.cnblogs.com/Oliver-zzc/,李天麟http://www.cnblogs.com/talent-demonic/: 1.代码是在Eclipse环境下开发的 2.在这次实验中我负责了写四则运算的算术代码和检查算法 同伴负责面板的设计和监听事件的实现 3.实现扩展方向有:用户在第一次答题时,需要用户输入用户名:程序可以设置答题时间,时间设置为整数,单位为秒:答题结束可以显示用户答错的题目个数和答对的