科学计算器

科学计算器

完整代码及测试数据

完整代码及测试数据

问题简介

完成一个科学计算器,相比简易计算器增加了一些三角函数和幂函数以及指数函数的计算。完成一个可以使用的科学计算器,需要满足支持下面的运算符。

运算符解释运算符解释
+1+1=2开方. √4=2
-2-1=1 or -1-2=-3(左括号
*2 * 2 = 4)右括号
/4 / 2 = 2!阶乘。3!=123=6
lnln(e)=1,以e为底的对数log以2为底的对数。log(4)=2
sinsin(30)=0.5, 正弦函数,输入是角度数。arcsinarcsin(0.5)=30, 反正弦函数,输出是角度数
coscos(90)=0, 余弦函数,输入是角度数。arccosarcsin(0)=90, 反余弦函数,输出是角度数
tantan(45)=1,正切函数,输入是角度数。arctanarcsin(1)=45, 反正切函数,输出是角度数
^指数运算。2^(3)=8; 4^(0.5)=2; 4^(-1/2=0.5

Example

序号inputoutput解释
v11+1+1+14符号优先级相同,先算前面再算后面
v22-1*20先算乘除,再算加减
v3(2-1)*22先算括号里面的数
v4(2-1*2+14/2)+√910先算括号,再算其他,开方的优先级比乘除高
v5(2+3)4+√9(1/2+((1+1)*2))33.5同v4
v6-2-3-5第一个’-‘是表示负数,第二个’-'是运算符
v7-2+(-3)*5-17第一个’-‘是表示负数,括号里面的第二个’-'也表示负数
v8√9*√9+√912开方的优先级高于乘除,乘除的优先级高于加减
v9sin(2^6+26)1sin函数是一个单值运算函数,先算括号,根据优先级进行计算
v102^664指数运算
v114^(-1/2)0.5指数元素
v122^log(8)8指数运算和对数运算
v13ln(e*e)2以e为底的对数运算
v14(5!-8^(1/2))*e318.505阶乘和指数运算的组合
v15-232(-1)+22^3+3!259多种运算的组合
v162+arcsin(log(2.0))+5+2^(2)+(-log(2))100多种元素的组合

解法

维护两个栈,分别是符号栈和数值栈。

  1. 首先遍历字符串,进行相应的操作;
  2. 遍历完字符串后,还需要进行相应的操作以清空符号栈,最后保证数值栈剩余最后一个数字为答案。

步骤和之前简易计算器相同,参照之前的流程图,这里不再重复。

有以下几个地方需要注意:

处理负号

所有的负号都可以转换成减法操作,识别出来是负号之后,在负号前补0即可。如何当前的’-'是负号,有两个条件:

  1. ’-'在输入表达式的第一位(stack_num为空)
  2. ’-'之前的字符不是数字(且不能是‘)’和‘!’),可以想到,(-8)需要补0,但8^2-9、(8)-9、8!-9不需要补0;
Input: -2-3          --->    Format_input: 0-2-3
 Input: 3+(-2)*(-1)   --->    Format_input: 3+(0-2)*(0-1)

符号之间的优先级

'+': {'+':-1, '-':-1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'-': {'+':-1, '-':-1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'*': {'+': 1, '-': 1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'/': {'+': 1, '-': 1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'√': {'+': 1, '-': 1, '*': 1, '/': 1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'(': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')': 0, '!':-1, '^': 1, 'ln': 1, 'log': 1, 'sin': 1, 'cos': 1, 'tan': 1, 'arcsin': 1, 'arccos': 1, 'arctan': 1},
')': {'+':-1, '-':-1, '*':-1, '/':-1, '√':-1, '(':0, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'!': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln': 1, 'log': 1, 'sin': 1, 'cos': 1, 'tan': 1, 'arcsin': 1, 'arccos': 1, 'arctan': 1},
'^': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!': 1, '^':-1, 'ln':-1, 'log':-1, 'sin': 1, 'cos': 1, 'tan': 1, 'arcsin': 1, 'arccos': 1, 'arctan': 1},
'ln': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1}, 
'log': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1}, 
'sin': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1}, 
'cos': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},                   
'tan': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
'arcsin': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},  
'arccos': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},  
'arctan': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},  
  1. ):除了(以外,全部为-1,必须把前面一个括号内的内容全部算完;
  2. !:优先级高,除了碰到!和),都入栈;
  3. :`2^sin(x)`,入栈;sin(x)^8,sin(x)先算;
  4. sin/cos/tan/arcsin/arccos/arctan/ln/log:一样;

分割运算符和数字

  1. 定义一个数字字典,数字是num_str=set("0123456789.e"),不在num_str的字符都是op字符。
  2. 如果char不是数字则一直累加,直到char出现在所有op中,则是一个op,执行该操作符相应的操作。

sin/cos/tan/arcsin/arccos/arctan

注意math.sin()的输入是弧度,math.arcsin()的输出是角度。

e如何处理,数字如何处理?

  1. 如果是:2e2,直接用float(2e2)转换;
  2. 如果是单独的e表示math.e

代码

import math
class AdvCalculator(object):
    def __init__(self):
        self.stack_op=[]
        self.stack_num=[]
        self.num_str=set("0123456789.e")
        self.op_priority={    '+': {'+':-1, '-':-1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                              '-': {'+':-1, '-':-1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                              '*': {'+': 1, '-': 1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                              '/': {'+': 1, '-': 1, '*':-1, '/':-1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                              '√': {'+': 1, '-': 1, '*': 1, '/': 1, '√':-1, '(':1, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                              '(': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')': 0, '!':-1, '^': 1, 'ln': 1, 'log': 1, 'sin': 1, 'cos': 1, 'tan': 1, 'arcsin': 1, 'arccos': 1, 'arctan': 1},
                              ')': {'+':-1, '-':-1, '*':-1, '/':-1, '√':-1, '(':0, ')':-1, '!':-1, '^':-1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                              '!': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln': 1, 'log': 1, 'sin': 1, 'cos': 1, 'tan': 1, 'arcsin': 1, 'arccos': 1, 'arctan': 1},
                              '^': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!': 1, '^':-1, 'ln':-1, 'log':-1, 'sin': 1, 'cos': 1, 'tan': 1, 'arcsin': 1, 'arccos': 1, 'arctan': 1},
                             'ln': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1}, 
                            'log': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1}, 
                            'sin': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1}, 
                            'cos': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},                   
                            'tan': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},
                         'arcsin': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},  
                         'arccos': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},  
                         'arctan': {'+': 1, '-': 1, '*': 1, '/': 1, '√': 1, '(':1, ')':-1, '!':-1, '^': 1, 'ln':-1, 'log':-1, 'sin':-1, 'cos':-1, 'tan':-1, 'arcsin':-1, 'arccos':-1, 'arctan':-1},  
}
        self.op_str=set(self.op_priority.keys())
        self.op_element={'+':2,'-':2,'*':2,'/':2,'√':1,'(':0,')':0,'!':1, '^':2, 'ln':1, 'log':1, 'sin':1, 'cos':1, 'tan':1, 'arcsin':1, 'arccos':1, 'arctan':1}
    
    # 系统会默认调用这个函数进行评测,你必须实验这个函数
    # 输入一个计算表达式,例如2+3*√4,返回的结果是8
    # 返回计算后的结构
    def run_operator(self):
        op=self.stack_op.pop()
        if self.op_element[op]==2:#二元运算
            a=self.stack_num.pop()
            b=self.stack_num.pop()
            # print(str(b)+op+str(a))
            if op=='*':self.stack_num.append(b*a)
            elif op=='+':self.stack_num.append(b+a)
            elif op=='-':self.stack_num.append(b-a)
            elif op=='/':self.stack_num.append(b/a)
            elif op=='^':self.stack_num.append(b**a)
        elif self.op_element[op]==1:
            a=self.stack_num.pop()
            if op=='√':
                # print(op+str(a))
                self.stack_num.append(a**0.5)
            elif op=='!':
                ans=1
                while a:
                    ans*=(a)
                    a-=1
                self.stack_num.append(ans)
            elif op=='sin':
                #注意a是角度,需要转成弧度
                self.stack_num.append(math.sin(a/180*math.pi))
            elif op=='cos':
                self.stack_num.append(math.cos(a/180*math.pi))
            elif op=='tan':
                self.stack_num.append(math.tan(a/180*math.pi))
            elif op=='ln':
                self.stack_num.append(math.log(a))
            elif op=='log':
                self.stack_num.append(math.log2(a))
            elif op=='arcsin':
                self.stack_num.append(math.asin(a)/math.pi*180)
            elif op=='arccos':
                self.stack_num.append(math.acos(a)/math.pi*180)
            elif op=='arctan':
                self.stack_num.append(math.atan(a)/math.pi*180)
    def solver(self, input):
        self.stack_op.clear()
        self.stack_num.clear()
        i=0
        num=''
        last_char=''
        for i in range(len(input)):
            #注意处理大于9的数字,1 4
            # print(self.stack_num,self.stack_op)
            char=input[i]
            if char in self.num_str:
                num+=char
            else:
                if num !='':
                    if num=='e':num=math.e
                    self.stack_num.append(float(num))
                    num=''
                    last_char=''
                last_char+=char
                if last_char not in self.op_str:
                    continue
                # elif char=='-' and last_char!=')':
                    
                    
                #判断运算符的优先级
                # print('---',last_char,self.stack_op)
                # op=char
                op=''
                while self.stack_op and self.op_priority[last_char][self.stack_op[-1]]<=0:#优先级相同或更小
                    if self.op_priority[last_char][self.stack_op[-1]]<0:
                        #弹出符号,先进行处理,处理完再压栈
                        self.run_operator()
                    else:
                        op=self.stack_op.pop()
                        break
                else:
                    if last_char=='-':
                        #需要加0的情况
                        #(-9)
                        #-9
                        if (not self.stack_num) or (input[i-1] in self.op_str and input[i-1]!=')' and input[i-1]!='!'):
                        #if not self.stack_num and (not self.stack_op or self.stack_op[-1]=='(' ):
                            self.stack_num.append(0)
                    self.stack_op.append(last_char)
                last_char=''
            #last_char=char
        if num!='':
            if num=='e':num=math.e
            self.stack_num.append(float(num))
        # print(self.stack_num,self.stack_op)
        while self.stack_op:
            self.run_operator()
        return self.stack_num[0] 

参考资料

AlgorithmRunning - 科学计算器 (qq.com)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值