zoukankan      html  css  js  c++  java
  • 四则运算

    Github项目地址:

    https://github.com/SwallowQAQ/caculate

    psp 表格

     

    解题思路:

        首先明确四则运算是先乘除后加减,如果有括号,先计算括号内的,再计算括号外的。一道四则运算的算式不一定有四种符号,一般指由两个或两个以上运算符号及括号,把多数合并成一个数的运算。

        所以要考虑的问题有:是否要生成括号,生成几个运算符,计算数值的范围,计算结果不能为负数,除数不能为0。

        这道题中,为了便于测试,我设定数值范围是0到20的数字,如果需要可以修改,考虑题目里可能会出现分数的情况,可以设定计算几道题和可以设定运算符数量,时间问题,这次的题目中未考虑括号。

    函数设计:

        函数主要分成三个,主函数main(),随机生成表达式expression(n),计算表达式js(s)。

        获取用户需要的设定通过主函数完成,包括循环生成表达式和计算得分,生成表达式是通过列表保留每个数字和运算符,再转换成字符串显示给用户,在将这个列表通过计算表达式的函数将列表转换成可用eval()函数直接计算的字符串。

    代码说明:

    from fractions import *
    import random
    def main():#主函数
        print('输入题目的数量:')
        n = int(input())
        print('输入运算符的数量(建议2或3):')
        fn = int(input())
        print("本次共 {} 题,满分 100 分".format(n))
        flag=0
        for i in range(n):
            while 1:
                ex = expression(fn)
                an=eval(js(ex))
                s=str(an)
                if '.' in s:
                    continue
                if an>=0:
                    if '/'in s:
                        if an >1:
                            continue
                    a=''.join(ex)
                    print(a)
                    if eval(input())==an:
                        flag+=1
                        print('回答正确!')
                    else:
                        print("回答错误!,正确答案是{}".format(an))
                    break
        print("共答对{}题,本次得分:{}".format(flag,round(100/n*flag)))
    def expression(n):#生成表达式
        f = ['+','-','*','÷','/']
        fl = []
        ex = []
        pren = 0
        pre = ''
        for i in range(n+1):
            if pre == '÷':
                pren = random.randint(1, 20)
                pre = f[random.randint(0, 2)]
            elif pre == '/':
                pren = random.randint(1, 20)
                pre = f[random.randint(0, 2)]
                ex.append(str(pren))
                ex.append(pre)
                pren = random.randint(1, 20)
                pre = f[random.randint(0, 2)]
            else:
                pren = random.randint(1, 20)
                pre = f[random.randint(0, 4)]
            ex.append(str(pren))
            ex.append(pre)
        ex[-1] = '='
        return ex
    def js(s):#返回可计算字符串
        f = ['+', '-', '*', '÷', '/']
        l=len(s)
        jg=''
        for i in range(0,int(l/2)-1):
    
            if s[2*i+1] in f[:4]:
                if i==0:
                    jg=jg+s[0]
                if s[2*i+3] =='/':
                    if s[2*i+1] =='÷':
                        jg = jg + '/'
                    else:
                        jg=jg+s[2*i+1]
                else:
                    if s[2*i+1] =='÷':
                        jg = jg + '/'+ s[2*i+2]
                    else:
                        jg = jg +s[2*i+1]+ s[2*i+2]
            else:
                jg=jg+'Fraction('+s[2*i]+','+s[2*i+2]+')'
        return str(jg)
    main()

    运行结果:

    说明:/是表示分子和分母的分割线,÷才是表示除法。

    代码测试:

        expression(n)函数主要是生成表达式,因此不能直接测试,但生成的表达式需要进行修改成可计算的表达式,因此两个函数都是不能直接测试的,但是可以测试生成的表达式经过修改是否可以直接计算,如果出现修改后的表达式为(2+3)2,这种情况用eval是会出错的,在写代码时候我是有一边测试的,一些基本的错误改过来了,现在写一个测试代码,测试1w个例子,代码如下:

    def test():
        for i in range(10000):
            ex=expression(2)
            a=js(ex)
            b=eval(a)

    运行结果:

        也就是表明表达式都是可以计算的,每道题是否合法是用主函数控制的,当结果是负数或者为假分数或者为小数的时候,重新生成表达式。

        其实这样的代码是有一些问题的,比如我测试的时候发现有3-8+8这样的例子,计算结果不为负数,但是其实算不合法的,因为按顺序计算3-8已经出现负数了,因此该加入一个新的函数,用于测试可计算的表达式是否合法,以及将表达式计算结果是否合理也放在该函数中判断,再返回合理的表达式。这时主函数也应修改,修改代码不作展示。

    性能分析:

        性能分析使用pycharm中提供的profile工具测试,如下图

        思路:在当前代码下,点击proifle这个工具的时候,是和普通的运行函数一样的,但是当程序结束会附带一个分析表,而测试结果和设定的题目数量等有关系,如果按照一般的流程,等待用户输入的时间应该是最大的,这里我将函数代码修改一下,将问题数量设为10000,运算符为2,将需要用户输入的地方修改成100。

    运行结果如下

    也就是说这10000道题中有147道题的结果为100,具体是哪些就不看了,接下来看看性能统计表,如下所示

        由表可以看出,eval和expression和js函数都调用了30160次,也就是每次生成一个表达式时候都需要做一次计算,并且可以知道大约生成3次表达式,才有一条的计算结果是符合要求的。

        看到调用次数最多的是生成随机数的函数,这个和运算符数量还有式子个数有关,最少要生成((2*运算符数量+1)*式子数量),但这里直接跟调用expression函数有关。

        js这个函数花的时间是较少的,但eval和expression两个函数都花比较多的时间,eval是不可避免的,每一个式子都必须要计算一次,才能得出结果,因此可以考虑修改一下expression函数,让他的返回结果是合法的。降低调用expression函数的次数,eval的次数也减少,总时间就会减少了。

    个人总结

    知识总结:

    1. Fraction()-->完成分数的表示和计算,并且用分数表示出来。
    2. eval()-->可以自动进行四则混合运算(带括号),但是本次题目没有加入括号运算
    3. 代码测试-->可以发现程序隐藏的错误。
    4. 性能测试-->能够很方便地看到程序的运行时间及效率,对于以后的修改可以更加专业且有针对性。

    个人感悟:

        题目看起来很简单,但是分析起来会发现有特别多的情况,如果只是随机生成数字运算会比较简单,结果有要求又会变得麻烦一些,加上括号可能更复杂,个人练习比较少,写个不是很复杂的代码也花很长时间,又加上对各种函数不熟悉,做起来更慢,需要多练习才行。以后有机会再将这个四则运算功能写更完善一些。

  • 相关阅读:
    LeetCode "Jump Game"
    LeetCode "Pow(x,n)"
    LeetCode "Reverse Linked List II"
    LeetCode "Unique Binary Search Trees II"
    LeetCode "Combination Sum II"
    LeetCode "Divide Two Integers"
    LeetCode "First Missing Positive"
    LeetCode "Clone Graph"
    LeetCode "Decode Ways"
    LeetCode "Combinations"
  • 原文地址:https://www.cnblogs.com/Swalllow/p/8878971.html
Copyright © 2011-2022 走看看