科学计算器
完整代码及测试数据
问题简介
完成一个科学计算器,相比简易计算器增加了一些三角函数和幂函数以及指数函数的计算。完成一个可以使用的科学计算器,需要满足支持下面的运算符。
运算符 | 解释 | 运算符 | 解释 |
---|---|---|---|
+ | 1+1=2 | √ | 开方. √4=2 |
- | 2-1=1 or -1-2=-3 | ( | 左括号 |
* | 2 * 2 = 4 | ) | 右括号 |
/ | 4 / 2 = 2 | ! | 阶乘。3!=123=6 |
ln | ln(e)=1,以e为底的对数 | log | 以2为底的对数。log(4)=2 |
sin | sin(30)=0.5, 正弦函数,输入是角度数。 | arcsin | arcsin(0.5)=30, 反正弦函数,输出是角度数 |
cos | cos(90)=0, 余弦函数,输入是角度数。 | arccos | arcsin(0)=90, 反余弦函数,输出是角度数 |
tan | tan(45)=1,正切函数,输入是角度数。 | arctan | arcsin(1)=45, 反正切函数,输出是角度数 |
^ | 指数运算。2^(3)=8; 4^(0.5)=2; 4^(-1/2=0.5 |
Example
序号 | input | output | 解释 |
---|---|---|---|
v1 | 1+1+1+1 | 4 | 符号优先级相同,先算前面再算后面 |
v2 | 2-1*2 | 0 | 先算乘除,再算加减 |
v3 | (2-1)*2 | 2 | 先算括号里面的数 |
v4 | (2-1*2+14/2)+√9 | 10 | 先算括号,再算其他,开方的优先级比乘除高 |
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+√9 | 12 | 开方的优先级高于乘除,乘除的优先级高于加减 |
v9 | sin(2^6+26) | 1 | sin函数是一个单值运算函数,先算括号,根据优先级进行计算 |
v10 | 2^6 | 64 | 指数运算 |
v11 | 4^(-1/2) | 0.5 | 指数元素 |
v12 | 2^log(8) | 8 | 指数运算和对数运算 |
v13 | ln(e*e) | 2 | 以e为底的对数运算 |
v14 | (5!-8^(1/2))*e | 318.505 | 阶乘和指数运算的组合 |
v15 | -232(-1)+22^3+3! | 259 | 多种运算的组合 |
v16 | 2+arcsin(log(2.0))+5+2^(2)+(-log(2)) | 100 | 多种元素的组合 |
解法
维护两个栈,分别是符号栈和数值栈。
- 首先遍历字符串,进行相应的操作;
- 遍历完字符串后,还需要进行相应的操作以清空符号栈,最后保证数值栈剩余最后一个数字为答案。
步骤和之前简易计算器相同,参照之前的流程图,这里不再重复。
有以下几个地方需要注意:
处理负号
所有的负号都可以转换成减法操作,识别出来是负号之后,在负号前补0即可。如何当前的’-'是负号,有两个条件:
- ’-'在输入表达式的第一位(stack_num为空)
- ’-'之前的字符不是数字(且不能是‘)’和‘!’),可以想到,
(-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,必须把前面一个括号内的内容全部算完;
- !:优先级高,除了碰到!和),都入栈;
- :`2^sin(x)`,入栈;
sin(x)^8
,sin(x)先算; - sin/cos/tan/arcsin/arccos/arctan/ln/log:一样;
分割运算符和数字
- 定义一个数字字典,数字是
num_str=set("0123456789.e")
,不在num_str的字符都是op字符。 - 如果char不是数字则一直累加,直到char出现在所有op中,则是一个op,执行该操作符相应的操作。
sin/cos/tan/arcsin/arccos/arctan
注意math.sin()的输入是弧度,math.arcsin()的输出是角度。
e如何处理,数字如何处理?
- 如果是:
2e2
,直接用float(2e2)
转换; - 如果是单独的
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]