实现自动生成小学四则运算题目

发布时间 2023-09-28 20:32:52作者: 黄皓坤
这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade21-12
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade21-12/homework/13016
这个作业的目标 结对项目

GitHub链接

1.PSP表格

PSP2.1 Persional Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 140 60
.Estimate .估计这个任务需要多少时间 1200 1000
Development 开发 2400 2000
.Analysis .需求分析 1600 2000
Design Spec 生成设计文档 30 30
Design Review 设计复审 15 10
Coding Standard 代码规范(为目前的开发制定合适的规范) 20 10
Design 具体设计 10 10
Coding 具体编码 180 200
Code Review 代码复审 20 10
Test 测试(自我测试,修改代码,提交修改) 30 10
Reporting 报告 30 30
Test Repor 测试报告 40 40
Size Measurement 计算工作量 5 5
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 5 5
合计 5585 5360

2.效能分析

程序运行时间情况:

上图是程序在生成100道题时的运行时间,可以发现,程序在仅生成100题时便需要0.1秒,可预见的是若要生成需求为10000题时,用时将会达到10秒以上。对于一个简单的程序来讲,这样的结果还有很大的改进空间。

程序运行消耗内存情况:

上图为程序为改进时的内存消耗情况,发现在进行字符串拼接是出现内存消耗急剧上升,分析可知是由于使用了过多的“+”进行字符串拼接,于是修改为join操作进行字符串拼接,效果得到了改善。

3.代码设计

  • 生成数字
import random
from memory_profiler import profile
class N_S:  # 数字结构体
    def __init__(self,min,max):
        self.number_sign = 0  # 数的类型
        self.numerator = 0  # 分子
        self.denominator = 1  # 分母
        self.min=min
        self.max=max

    def generate_number_sign(self):  # 随机生成数的类型,类型值为1时分母默认为1
        self.number_sign = random.randint(1, 2)
        if self.number_sign==1: # 生成数为整数
            self.generate_numerator(self.min,self.max)
            num=str(self.numerator)
        elif self.number_sign==2: # 生成数为分数
            self.generate_numerator(self.min,self.max)
            self.generate_denominator(self.min,self.max)
            if self.numerator>=self.denominator: # 当生成的分数为假分数时,将其转化为带分数
                m=int(self.numerator/self.denominator)
                self.numerator -= m * self.denominator
                if self.numerator!=0:
                    num=str(m)+"'"+str(self.numerator)+'/'+str(self.denominator)
                else:
                    num=str(m)
            else:
                num=str(self.numerator)+'/'+str(self.denominator)
        else:
            return 0
        return num

    def generate_numerator(self, min, max):  # 随机生成分子的大小
        self.numerator = random.randint(min, max)

    def generate_denominator(self, min, max):  # 随机生成分母的大小
        self.denominator = random.randint(min, max)
  • 生成四则运算算式
from number import *
from cala import *
class formula:
    def __init__(self,max,min):
        self.symbol_number = 0
        self.max=max
        self.min=min
        self.symbol_number=random.randint(1,3) # 生成运算符号数量

    def generate_formula(self):
        self.bracket_sign = random.randint(0, 1) #是否添加括号
        ns=N_S(self.min,self.max)
        op=[]
        tree=[]
        formula=''
        # 生成运算符号
        for i in range(self.symbol_number):
            op_count = random.randint(0, 3)
            operation = ['+', '-', '*', '÷']
            op.append(operation[op_count])
        if self.bracket_sign==1:
            if self.symbol_number==1:
                self.bracket_sign==0
            else:
                loc=random.randint(0,self.symbol_number-1)
                if loc != 0 and loc + 1 != self.symbol_number:
                    op[loc - 1] = op[loc - 1] + '('
                    op[loc + 1] = ')' + op[loc + 1]
                elif loc == 0:
                    op[loc + 1] = ')' + op[loc + 1]
                    op.insert(loc, '(')
                elif loc + 1 == self.symbol_number:
                    op[loc - 1] = op[loc - 1] + '('
                    op.insert(loc + 1, ')')
        # 生成算式
        if self.bracket_sign==1:
            for i in op:
                if i=='(':
                    formula=formula+i
                else:
                    nums=ns.generate_number_sign()
                    formula=formula+nums+i
            if i!=')':
                nums = ns.generate_number_sign()
                formula = formula + nums
        else:
            for i in op:
                nums = ns.generate_number_sign()
                formula = formula + nums + i
            nums = ns.generate_number_sign()
            formula = formula + nums
        if '+' and '*' in op: #判断生成的算式是否重复,若式子中的符号和数字都相同,则将其判断为重复式子
            num,op=disassemble(formula)
            if [num,op] in tree:
                return False
            else:
                tree.append([num,op])
        return formula
  • 运算
import fractions
#运算函数
def calculate(num1, num2, op):
    result = 0
    if op == '+':
        result = num1 + num2
    elif op == '-':
        result = num1 - num2
    elif op == '*':
        result = num1 * num2
    elif op == '÷':
        if num2 == 0 :
            return -1
        elif num1%num2!=0:
            return fractions.Fraction(num1,num2)
        result = int(num1 / num2)
    return result

#将传入的算式字符串分解为数字和运算符两份数组
def disassemble(ss):
    s=list(ss)
    num=[]
    op=[]
    for i in range(len(s)):
        if s[i] in "+-*÷()":
            op.append(s[i])
    ss = ss.replace(')+', '.')
    ss = ss.replace(')-', '.')
    ss = ss.replace(')*', '.')
    ss = ss.replace(')÷', '.')
    ss = ss.replace('+(', '.')
    ss = ss.replace('-(', '.')
    ss = ss.replace('*(', '.')
    ss = ss.replace('÷(', '.')
    ss = ss.replace('+' ,'.')
    ss = ss.replace('-', '.')
    ss = ss.replace('*', '.')
    ss = ss.replace('÷', '.')
    ss = ss.replace('(', '.')
    ss = ss.replace(')', '.')
    ss=ss.split('.')

    for i in range(len(ss)):
        if '/' in ss[i]:
            if "'" in ss[i]:
                mix=list(ss[i])
                n=fractions.Fraction(int(mix[0])*int(mix[4])+int(mix[2]),int(mix[4]))
            else:
                n=fractions.Fraction(ss[i])
        elif ss[i]=='(' or ss[i]==')' or ss[i]=='':
            continue
        else:
            n=int(ss[i])
        num.append(n)
    return num,op
def cala(ss):
    num, op = disassemble(ss)
    result = 0
    if '(' in op:
        i = op.index(('('))
        result += calculate(num[i], num[i + 1], op[i+1])
        op.pop(i + 1)
        op.remove('(')
        op.remove(')')
        num[i:i + 2] = []
        num.insert(i, result)
    while len(op) != 0:
        result = 0
        if '*' in op and ('+' or '-') in op:# 若算式中既有加法又有乘法,则要先算乘法
            i = op.index(('*'))
            result += calculate(num[i], num[i + 1], op[i])
            op.remove('*')
            num[i:i + 2] = []
            num.insert(i, result)
        elif '÷' in op and ('+' or '-') in op:
            i = op.index(('÷'))
            result += calculate(num[i], num[i + 1], op[i])
            op.remove('÷')
            num[i:i + 2] = []
            num.insert(i, result)
        else:
            result += calculate(num[0], num[1], op[0])
            op.remove(op[0])
            num[0:2] = []
            num.insert(0, result)
    return num[0]
  • 批改
def save_answer(formula):
    f=open('answer.txt','w')
    f.write(formula)
    f.close()
def save_question(formula):
    f = open('question.txt', 'w')
    f.write(formula)
    f.close()
def check():
    f1=open('answer.txt','r')
    f2=open('grade.txt','r')
    line1=f1.readlines()
    line2=f2.readlines()
    correct=[]
    count_corrent=0
    count_wrong=0
    wrong=[]
    for i in range(len(line1)):
        if line2[i]==line1[i]:
            correct.append(i+1)
            count_corrent+=1
        else:
            wrong.append(i+1)
            count_wrong+=1
    correct=str(correct)
    wrong=str(wrong)
    return correct,wrong,count_corrent,count_wrong

运行结果展示

  • 首先生成界面,需在文本框内填写上限、下限和生成题目的数量,如不填写,则默认值分别为9、1、1;

  • 输入所需数据后,点击确认即可生成题目

  • 点击下方导出题目按钮,可将当前生成的题目写道question.txt文件中

    同时,该题目对应的答案也会写在answer.txt文件中

  • 修改grade.txt中的内容,点击界面中的批改按钮即可批改

测试

在自己手写运算和各部分代码得出的运算结果对比基本可以确定程序的运行结果是正确的。在连续生成100题目10次时,代码未出现报错情况,运行时间大致相等,基本能够满足题目要求。但在生成题目数量较多时速度慢的问题依然没有解决,且生成重复题目的需求实际上并没有完善,后续仍需改进。