前言
本单元由三次作业组成,主要任务是表达式展开和化简,三次迭代内容分别为:
1.对一个变量只为x,包含加、减、乘和乘方运算且只有一层括号的单变量进行化简。
2.在第一次作业的基础上增加了指数函数、多层括号嵌套和自定义函数调用。
3.在第二次作业的基础上增加了求导运算,并且自定义函数定义中允许调用其他自定义函数。
第一次作业
UML类图
基本思路
万事开头难,本次作业是我三次迭代中耗时最长的一次(如果不算第二次迭代中两天的debug),主要的问题在于纠结选择什么样的架构便于代码的编写以及后两次迭代,最终选择借鉴一位学长的架构。
这种架构的核心要点在于递归下降的方法以及对最终化简的表达式通项表示。
递归下降方法的具体表现在于建立三个类,分别为表达式类(Expr)、项类(Term)和因子类(Factor),其中因子类我是通过接口的方式实现的,在其下一层又分别有常数因子(NumFactor)、变量因子(VarFactor)和表达式因子(ExprFactor)来实现这个接口。这样对于表达式的解析,我设立了三个类:Token,Lexer与Parser。Token定义了一系列词元,Lexer将字符串转化为词元,交给Parser进行递归下降解析。在Parser类中,有三个主要的方法:parseExpr(),parseTerm(),parseFactor(),分别对应表达式转化为项、项转化为因子以及因子转化为具体的哪一类因子。这样就实现了将复杂的表达式解析为简单的因子。
通过阅读本次的要求我们可以发现,最终化简后的表达式可以以这样的通项形式表示:,因此我新建了一个类(Poly)用于实现表达式的化简以及输出。其中我使用了HashMap<Integer, BigInteger>来储存表达式通项中的a与b,这种方法虽然能很好的完成本次作业,但其拓展性并不好,这为下一次的迭代开发埋下了隐患。
其他的一些细节包括对表达式进行预处理,除去表达式中的空白字符,并消去连续出现的正负号,先输出正项,再输出负项。
代码规模与复杂度分析
代码规模:
复杂度分析:
1.类复杂度:
2.方法复杂度:
可以发现,复杂度高的部分主要集中于表达式解析、化简和输出上。
第二次作业
UML类图
基本思路
本次作业相较于第一次作业主要调整的地方在于新添了指数因子,使用SelfDefine类对表达式预处理来解决自定义函数的调用以及对表达式化简和输出的方法进行了重构。
SelfDefine类主要由两种方法组成:addDefine(String string) 和 replaceDefine(String string)。第一种方法的功能是储存表达式。具体的实现方法是使用两个HashMap分别来储存函数名+函数内容和函数名+函数的多个变量。第二种方法用来替换自定义函数,输入是含有自定义函数的初始表达式,通过栈匹配括号的方法,替换掉自定义函数,输出处理后的表达式。
本次作业虽然新添了指数因子,但最终化简结果仍然可以用通项来表示: 。但是由于参数的增加,第一次作业中使用一个HashMap来储存的方法显然是不可行的。因此经过再三考虑,我决定使用Unit类(单项式类)+ Poly类(多项式类)的方法来实现,Unit类定义了三个变量:coe,index,content,分别对应通项中的a,b,g(x)。其中由于g(x)是一个表达式,故content的类型是Poly,而Poly类由ArrayList<Unit>组成,因此形成了这样一个互相嵌套的方式。
代码规模与复杂度分析
代码规模:
复杂度分析:
1.类复杂度:
2.方法复杂度:(只选取了部分)
复杂度高的部分仍然是表达式的解析、化简和输出。
第三次作业
UML类图
基本思路
由于第二次迭代选择了正确的架构,所以这次作业完成得非常轻松。相较于上一次,这次作业的改变在于新增了求导因子,并在单项式与多项式类中新增了求导方法。过程较为简单,就不赘述了。
代码规模与复杂度分析
代码规模:
复杂度分析:
1.类复杂度:
2.方法复杂度:(只选取了部分)
bug分析
非常幸运的是,这三次作业的强测本人均分在a房,且并没有被其他人hack出bug,但是在第二次作业的强测中错了一个点,主要的原因在于没有考虑到在多层括号嵌套背景下指数和系数均有可能超过int范围的问题,而仅仅将系数用BitInteger存储,将指数用int存储。修改方法为将指数也用BigInteger存储。
hack他人方面,我在第二次作业发现了两个人的bug,均是将 exp((-x)) 错误优化为 exp(-x)。发现他人bug的方法主要是将自己在完成作业过程中发现到的易错数据收集后hack他人。
优化
这一单元的作业我只保证了正确性,优化方面做得并不好,因此最后的性能分并不高。我只做了先输出正项,后输出负项的优化,像exp中提取最大公因数、将exp中括号化到最少的优化并没有做。现在想想至少exp中提取最大公因数的优化完全是可以完成的,这也是本单元的一点遗憾。
心得体会
1.掌握了递归下降的方法。从一开始完全不了解,到后来能够熟练使用并用其处理实际问题。
2.代码要有可扩展性。如果发现架构不具备可扩展性,及时重构。
未来方向
我认为性能分上可以适当调整,毕竟有时为了能够使输出最短会影响结果的美观。