BUAA 2024 OO UNIT1

22373244-袁耀武 学生 2024-03-22 23:14:41

前言

本单元的主要任务是表达式的解析与展开,hw1为单变量多项式的展开,hw2为含指数函数与自定义函数的展开,hw3新增求导要求,总的说来,从hw1迭代到hw2的难度最大,也是本人最痛的一次经历()。下面是我的具体思考

hw1

基于度量来分析自己的程序结构

类图

img

根据上图(图中类的方法属性放在了下文分析),我们可以看出笔者设计的框架有8个类,1个接口,其中:

Variable类有1属性:String var,2方法:

  • Variable(String var)构造方法
  • String toString()用于返回字符串var

Number类有1属性:BigInteger num,3方法:

  • Number (BigInteger num)构造方法
  • BigInteger getNum()用于get该类对象属性num
  • String toString()用于将num转化为String并return

Expr类有1属性:HashMap<Integer, BigInteger> termmap,6方法:

  • Expr()构造方法
  • HashMap<Integer, BigInteger> getmap()用于该类对象属性termmap
  • void initial(BigInteger a)用于初始化termmap(对1等初始化)
  • void initial1()另一种初始化termmap方法,(对x等初始化)
  • void addTerm(Term term)将term加入termmap
  • simplification(Expr expr)将expr树结构简化并转化为字符串输出到控制台

Term类有1属性HashMap<Integer, BigInteger> factormap,4方法:

  • Term()构造方法
  • HashMap<Integer, BigInteger> getmap()用于return该类对象属性factormap
  • void forinitial(int num)根据num初始化为1或-1,利用addfactor方法加入到factormap
  • void addFactor(Factor factor)用于将因子加入factormap中

Poly类有4方法:

  • HashMap<Integer, BigInteger> multiply(HashMap<Integer, BigInteger> x,HashMap<Integer, BigInteger> y)用于将l两个poly的hashmap相乘
  • HashMap<Integer, BigInteger> add(HashMap<Integer, BigInteger> x,HashMap<Integer, BigInteger> y)用于将两个poly的hashmap相加
  • HashMap<Integer, BigInteger> subtract(HashMap<Integer, BigInteger> x,HashMap<Integer, BigInteger> y)用于将两个poly的hashmap相减
  • HashMap<Integer, BigInteger> power(HashMap<Integer, BigInteger> x, int y)用于用于将某poly的hashmap实现自乘方运算

Lexer类有3属性:String input,int pos,String curToken,3方法:

  • Lexer(String expression)构造方法:根据expression字符串初始化input属性
  • void next()解析词法并获得当前curtoken
  • String getCurToken()返回当前curtoken

Parser类有1属性Lexer lexer,4方法:

  • Parser(Lexer lexer)构造器,初始化属性Lexer lexer
  • Expr parseExpr()用于解析表达式
  • Term parseTerm()用于解析项
  • Factor parseFactor()用于解析表达式因子或常量因子或变量因子

Main类有2方法:

  • static String MoreStandardized(String origin)用于对字符串origin预处理化简
  • static void main(String[] args)程序入口,用于构造lexer,构造parser,再parseExpr,最后简化并输出

值得注意的是,前四个类Expr,Number,Var,Term类均实现了接口,便于统一管理

分析维度

笔者分析插件采用的是MetricReloaded,主要从以下方面分析:

  • CogC:Cognitive Complexity即认知复杂度,CogC大的原因常有:出现很多break,多层嵌套结构
  • ev(G):维护复杂度,用于衡量程序的非结构化程度,显然程序的非结构化程度高,维护难度也就大,因此ev(G)高的代码,有时消除一个bug会引起另外一个bug
  • iv(G):设计复杂度,用于衡量模块与其他模块间的调用关系复杂度,也即耦合度,耦合度高的代码常常难以复用
  • v(G):测试难度,用于判定一个模块判定结构的复杂度,数量上表现为独立路径的条数
  • OCavg:Average Cyclomatic Complexity即平均圈复杂度,其较高说明存在复杂的控制流结构,可能需要重构以提高代码的可读性和可维护性
  • OCmax:Maximum Cyclomatic Complexity,最大圈复杂度,基本同上,用于识别代码中最复杂的函数
  • WMC:Weighted Methods per Class类的加权方法数,表示每个类中方法的数量,其较高说明可能需要拆分类或重构

利用MetricReloaded分析本人设计的方法如图,可以发现以下问题:

  • simplification和parseTerm CogC复杂度高,前者在于该方法还实现了输出功能,后者在于有很多嵌套结构
  • parseTerm的iv(G)复杂度高,原因在于频繁调用其他类,耦合度高
  • parseTerm的v(G)复杂度高,原因在于该方法分支结构过多,一大堆if else
MethodCogCev(G)iv(G)v(G)
Expr.Expr()0111
Expr.addTerm(Term)1122
Expr.getmap()0111
Expr.initial(BigInteger)0111
Expr.initial1()0111
Expr.simplification(Expr)25189
Lexer.Lexer(String)0111
Lexer.getCurToken()0111
Lexer.next()9177
Main.MoreStandardized(String)0111
Main.main(String[])0111
Number.Number(BigInteger)0111
Number.getNum()0111
Number.toString()0111
Parser.Parser(Lexer)0111
Parser.parseExpr()2133
Parser.parseFactor()4223
Parser.parseTerm()231911
Poly.add(HashMap<Integer, BigInteger>, HashMap<Integer, BigInteger>)3133
Poly.multiply(HashMap<Integer, BigInteger>, HashMap<Integer, BigInteger>)6144
Poly.power(HashMap<Integer, BigInteger>, int)2233
Poly.subtract(HashMap<Integer, BigInteger>, HashMap<Integer, BigInteger>)3133
Term.Term()0111
Term.addFactor(Factor)5144
Term.forinitial(int)2122
Term.getmap()0111
Variable.Variable(String)0111
Variable.toString()0111
t.ma()0111

利用MetricReloaded分析本人设计的类如图,可以发现以下问题:

  • Parser,Poly类的OCavg较高,说明控制流结构复杂度较高
ClassOCavgOCmaxWMC
Expr2.5915
Lexer2.6768
Main112
Number113
Parser4.751219
Poly3.25413
Term248
Variable112
t111

架构分析

简而言之,笔者采用的架构是将解析建语法树与计算分开的架构,因为在解析完表达式建完语法树后,所有的计算都是先转化为poly结构,再利用poly的特性计算

再建语法树时,利用lexer和parser,以lexer.next方法逐个字符是识别并返回一个token语法单元,然后parser根据语法定义,采用递归性下降的方式,采用Expr-》Term-》factor-》(Number/Var/Expr)架构不断下降,直至最底层,将返回的token建树,那么,很容易发现,这样的语法树以一个Expr为根节点,每一个分叉均是Term,每一个Term分支均是factor,如果说factor是常量或变量,则作为叶节点返回,但如果是Expr,则该factor是一个子树的根节点,一直递归下去,只知道叶节点,由此,我们便得到一颗以Expr为根节点的树。

计算时,我们给Expr,Term等类都添加一个topoly的方法,如epxr转化为poly,则利用terms里每一项Term转化为poly相加得到,Term转化为poly,则用每一项factor转化为poly相乘得到,factor如果时Expr,调用expr的topoly,如果是Number或Var,则直接返回。

优点:

  • 可迭代性强,在第2,3次作业中均可采用该架构
  • 计算上分而治之,降低了耦合度

缺点:

  • 部分类方法过多,如将化简操作放在了一些类中,使得CogC高
  • 对深浅拷贝理解不到位,统一采用了浅拷贝和传引用,建议合理的new
  • 过多的new降低了性能

分析自己的bug

  • 在强测和互测中各出现一个bug,为同质bug,错误原因为TLE,具体说,就是程序在一些结构控制和性能优化出现了问题,计算方法不简洁导致超时

hw2

基于度量来分析自己的程序结构

类图

img

根据上图(这张图制作耗时最久),我们可以看出笔者设计的框架有12个类,1个接口,其中:

Var类有2属性:char name,BigInteger indexNumber,3方法:

  • Var(char name, String indexNumber)构造方法
  • Poly toPoly()用于将该对象转化为poly结构

Const类有1属性:BigInteger num,4方法:

  • Const(String num)构造方法
  • BigInteger getNum()用于get该类对象属性num
  • String toString()用于将该对象转化为String并return
  • Poly toPoly()用于将该对象转化为poly结构

Expr类有1属性:ArrayList terms,BigInteger indexNumber,5方法:

  • Expr()构造方法

  • void addTerm(Term term)将term加入termmap

  • void setIndexNumber(BigInteger indexNumber)用于初始化indexnumber

  • Poly toPoly()转化为poly结构

  • string toString()用于将该对象转化为String并return

Term类有2属性ArrayList factors,BigInteger coe,4方法:

  • Term(char sign)构造方法
  • void addFactor(Factor factor)用于将因子加入factors中
  • void inverse(char sign)根据sign将系数的符号反转
  • void mergeConst()用于将常量因子融合
  • Poly toPoly()转化为poly结构
  • string toString()用于将该对象转化为String并return

Poly类有1属性:HashMap<Unit, BigInteger> unitList,4方法:

  • public Poly()构造器,初始化
  • void setMark()用于辅助判断两个poly的unit是否可合并
  • boolean isFactor()用于判断当前unitlist每个unit的所有因子是否是单因子
  • void addUnit(Unit unit)用于将unit加入unitlist
  • Poly addPoly(Poly poly)用于将两个poly相加得到新poly
  • Poly mulPoly(Poly poly)用于将两个poly相乘得到新poly
  • Poly powPoly(BigInteger indexNumber)用于将某个poly自乘方indexnumber得到新poly
  • boolean equals(Object o)判断两个poly的对应unit是否除系数外相同
  • int hashCode()返回poly对象的hash值
  • string toString()用于将该对象转化为String并return

Exp类有2属性:Factor factor,BigInteger indexNumber,3个方法:

  • Exp(Factor factor, String indexNumber)构造器
  • Poly toPoly()转化为poly结构
  • string toString()用于将该对象转化为String并return

Func类有2属性:String funcExpr,Expr expr,4方法:

  • Func(String name, ArrayList paramList)构造器
  • Expr parse(String funcExpr)用于解析String funcExpr
  • string toString()用于将该对象转化为String并return
  • Poly toPoly()转化为poly结构

Unit类有4属性:BigInteger coe,HashMap<String, BigInteger> vars,HashMap<Poly, BigInteger> expMap,BigInteger mark,多方法:

  • int size()返回vars+expmap元素数量
  • boolean isFactor()判断是否为单因子
  • Unit mulUnit(Unit unit)实现两个unit相乘
  • boolean equals(Object o)判断两unit是否除系数外都相等

FuncDefiner类有3属性:static HashMap<String, String> func,HashMap<String, ArrayList> params,HashMap<String, String> varMap,3方法:

  • static void addFunc(String input)用于改变函数定义式,将x换为u等
  • static String simplifyExpression(String expression)用于将调用函数产生的如3^2转化为常量9
  • static String callFunc(String name, ArrayList actualParam)用于调用函数将形参替换为实参

Lexer类有3属性:String input,int pos,5方法:

  • Lexer(String expression)构造方法:根据expression字符串初始化input属性
  • Character peek()返回当前位置字符
  • String getConst()返回当前const
  • String getString(int cnt)返回当前string
  • getChar()返回当前char

Parser类有1属性Lexer lexer,多方法:

  • Parser(Lexer lexer)构造器,初始化属性Lexer lexer
  • Expr parseExpr()用于解析表达式
  • Term parseTerm()用于解析项
  • Factor parseFactor()用于解析表达式因子或常量因子或变量因子

Main类有5方法:

  • static String MoreStandardized(String origin)用于对字符串origin预处理化简
  • static void main(String[] args)程序入口,用于构造lexer,构造parser,再parseExpr,最后简化并输出
  • static String coeMulFac(String expression)用于实现将如exp((2+x))^2==exp((4+2*x))
  • static String simplifyExpression(String expression)实现对exp((2+2*x))==exp(1+x)^2
  • static String simplifySubExpression(String subExpression)将提取出的最大公因子,如exp((2+2x))里的2+2x化为1+x^2,之后将 ^前面的当作poly,后面的当作最大公因子提取出来

值得注意的是,前5个类Expr,Number,Var,Term类均实现了接口,便于统一管理

分析维度

同hw1,笔者分析插件采用的是MetricReloaded,这里就不重复介绍了

利用MetricReloaded分析本人设计的方法如图,可以发现以下问题:

  • findGreatestCommonFactor和parseFactor ev(G)复杂度高,在于方法长而复杂

  • Unit.toString,simplifySubExpression的iv(G),v(G)复杂度高,原因在于频繁调用其他类,耦合度高

MethodCogCev(G)iv(G)v(G)
MainClass.coeMulFac(String)3011010
MainClass.findGreatestCommonFactor(List)11658
MainClass.main(String[])1122
MainClass.simplifyExpression(String)4133
MainClass.simplifySubExpression(String)3711212
expr.Const.Const(String)0111
expr.Const.getNum()0111
expr.Const.toPoly()0111
expr.Const.toString()0111
expr.Exp.Exp(Factor, String)0111
expr.Exp.toPoly()3233
expr.Exp.toString()1122
expr.Expr.Expr()0111
expr.Expr.addTerm(Term)0111
expr.Expr.setIndexNumber(BigInteger)0111
expr.Expr.toPoly()2233
expr.Expr.toString()1122
expr.Func.Func(String, ArrayList)0111
expr.Func.parse(String)0111
expr.Func.toPoly()0111
expr.Func.toString()0111
expr.Poly.Poly()0111
expr.Poly.addPoly(Poly)2133
expr.Poly.addUnit(Unit)2122
expr.Poly.equals(Object)3324
expr.Poly.hashCode()0111
expr.Poly.isFactor()3333
expr.Poly.mulPoly(Poly)3133
expr.Poly.powPoly(BigInteger)3133
expr.Poly.setMark()2133
expr.Poly.toString()3334
expr.Term.Term(char)2112
expr.Term.addFactor(Factor)0111
expr.Term.inverse(char)1122
expr.Term.mergeConst()3133
expr.Term.toPoly()1122
expr.Term.toString()6155
expr.Unit.Unit(BigInteger)0111
expr.Unit.addExp(Poly, BigInteger)2133
expr.Unit.addVar(String, BigInteger)2133
expr.Unit.equals(Object)3324
expr.Unit.getCoe()0111
expr.Unit.hashCode()0111
expr.Unit.isFactor()4434
expr.Unit.mulUnit(Unit)4155
expr.Unit.setCoe(BigInteger)0111
expr.Unit.setMark()0111
expr.Unit.size()0111
expr.Unit.toString()2231111
expr.Var.Var(char, String)0111
expr.Var.toPoly()0111
expr.Var.toString()0111
parser.FuncDefiner.addFunc(String)8177
parser.FuncDefiner.callFunc(String, ArrayList)1122
parser.FuncDefiner.simplifyExpression(String)1122
parser.Lexer.Lexer(String)0111
parser.Lexer.getChar()0111
parser.Lexer.getConst()2133
parser.Lexer.getString(int)0111
parser.Lexer.peek()2222
parser.Parser.Parser(Lexer)0111
parser.Parser.parseConst()4244
parser.Parser.parseExp()1212
parser.Parser.parseExpr()2133
parser.Parser.parseFactor()851010
parser.Parser.parseFunc()1122
parser.Parser.parseIndexNumber()5244
parser.Parser.parseTerm()5166
parser.Parser.parseVar()0111

利用MetricReloaded分析本人设计的类如图,可以发现以下问题:

  • Main类的OCavg较高,说明控制流结构复杂度较高
ClassOCavgOCmaxWMC
MainClass6.81234
expr.Const114
expr.Exp236
expr.Expr1.638
expr.Func114
expr.Poly2.6426
expr.Term2.5515
expr.Unit2.921135
expr.Var113
parser.FuncDefiner359
parser.Lexer1.427

架构分析

先介绍下基本形式:

举例                                   通用形式
因子:
Const:      3                  (num)
Var:        x^2                (name^indexnumber)
Expr:       (x+1)^2             ((term1+term2+...+termn)^indexnumber)
Exp:        exp((x+1))^2       (exp(factor)^indexnumber) 
Func:       f(x)----->x+1       (String  funcExpr对应树结构expr)
项:
term:       2*x*x              (coe*factor1*factor2*...*factorn)
表达式:
Expr:       exp((x+1))^2       (exp(factor)^indexnumber) 
单元:
unit      2*x^2*exp((x+1))         (coe*(var^index)*exp(poly))^indexnumber
多项式:
poly:      (2*x^2*exp((x+1))+3*x^3*exp((2*x+1)))    (unit+unit)

简而言之,笔者采用的架构是将解析建语法树与计算分开的架构,因为在解析完表达式建完语法树后,所有的计算都是先转化为poly结构,再利用poly的特性计算

建语法树时,基于迭代性好,思路和hw1差不多,但额外多了对exp,func的解析

计算时,给Expr,Term等类都添加一个topoly的方法,如:

  • Const topoly:new unit,并将num传入作为系数,然后将new unit加入poly的unitlist即可

  • Var topoly:先创建一个系数为1的unit,然后调用unit的addvar即可

  • Expr topoly:分expr indexnumber为0和不为0两种,后者实现:将expr每个term topoly然后add,最后调用poly.powPoly(indexNumber)即可

  • Exp topoly:主要是调用factor.toPoly()方法

输出时,给每个类添加toString方法:

具体思路是利用因子,项,表达式的基本形式结合对应属性,构建空Biginteger,然后append即可,由于比较简单,我就不一一讲解

优点:

  • 可迭代性强
  • 解析,计算,输出上分而治之,降低了耦合度

缺点:

  • 过多的new降低了性能

分析自己的bug

  • 在强测和互测中出现多个bug,但为同质bug,错误原因为优化时错误,具体说,就是程序再得到相对长的表达式后优化时没有考虑到数据的正负,+-,等等情况导致正确性丢失,可见,优化方法一定要保证正确在添加,需经过充分的测试

hw3

基于度量来分析自己的程序结构

类图

img

根据上图,我们可以看出笔者设计的框架有15个类,2个接口,其中:

Var类有2属性:char name,BigInteger indexNumber,3方法:

  • Var(char name, String indexNumber)构造方法
  • Poly differential()用于返回求导的结果
  • Poly toPoly()用于将该对象转化为poly结构

Const类有1属性:BigInteger num,4方法:

  • Const(String num)构造方法
  • BigInteger getNum()用于get该类对象属性num
  • String toString()用于将该对象转化为String并return
  • Poly toPoly()用于将该对象转化为poly结构

Expr类有2属性:ArrayList terms,BigInteger indexNumber,5方法:

  • Expr()构造方法

  • void addTerm(Term term)将term加入termmap

  • void setIndexNumber(BigInteger indexNumber)用于初始化indexnumber

  • Poly toPoly()转化为poly结构

  • string toString()用于将该对象转化为String并return

Term类有2属性ArrayList factors,BigInteger coe,4方法:

  • Term(char sign)构造方法
  • void addFactor(Factor factor)用于将因子加入factors中
  • void inverse(char sign)根据sign将系数的符号反转
  • void mergeConst()用于将常量因子融合
  • Poly toPoly()转化为poly结构
  • string toString()用于将该对象转化为String并return

Poly类有1属性:HashMap<Unit, BigInteger> unitList,4方法:

  • public Poly()构造器,初始化
  • void setMark()用于辅助判断两个poly的unit是否可合并
  • boolean isFactor()用于判断当前unitlist每个unit的所有因子是否是单因子
  • void addUnit(Unit unit)用于将unit加入unitlist
  • Poly addPoly(Poly poly)用于将两个poly相加得到新poly
  • Poly mulPoly(Poly poly)用于将两个poly相乘得到新poly
  • Poly powPoly(BigInteger indexNumber)用于将某个poly自乘方indexnumber得到新poly
  • boolean equals(Object o)判断两个poly的对应unit是否除系数外相同
  • int hashCode()返回poly对象的hash值
  • string toString()用于将该对象转化为String并return

Exp类有2属性:Factor factor,BigInteger indexNumber,3个方法:

  • Exp(Factor factor, String indexNumber)构造器
  • Poly toPoly()转化为poly结构
  • string toString()用于将该对象转化为String并return

Func类有2属性:String funcExpr,Expr expr,4方法:

  • Func(String name, ArrayList paramList)构造器
  • Expr parse(String funcExpr)用于解析String funcExpr
  • string toString()用于将该对象转化为String并return
  • Poly toPoly()转化为poly结构

Unit类有4属性:BigInteger coe,HashMap<String, BigInteger> vars,HashMap<Poly, BigInteger> expMap,BigInteger mark,多方法:

  • int size()返回vars+expmap元素数量
  • boolean isFactor()判断是否为单因子
  • Unit mulUnit(Unit unit)实现两个unit相乘
  • boolean equals(Object o)判断两unit是否除系数外都相等

FuncDefiner类有3属性:static HashMap<String, String> func,HashMap<String, ArrayList> params,HashMap<String, String> varMap,3方法:

  • static void addFunc(String input)用于改变函数定义式,将x换为u等
  • static String simplifyExpression(String expression)用于将调用函数产生的如3^2转化为常量9
  • static String callFunc(String name, ArrayList actualParam)用于调用函数将形参替换为实参

Lexer类有3属性:String input,int pos,5方法:

  • Lexer(String expression)构造方法:根据expression字符串初始化input属性
  • Character peek()返回当前位置字符
  • String getConst()返回当前const
  • String getString(int cnt)返回当前string
  • getChar()返回当前char

Parser类有1属性Lexer lexer,多方法:

  • Parser(Lexer lexer)构造器,初始化属性Lexer lexer
  • Expr parseExpr()用于解析表达式
  • Term parseTerm()用于解析项
  • Factor parseFactor()用于解析表达式因子或常量因子或变量因子

Main类有5方法:

  • static String MoreStandardized(String origin)用于对字符串origin预处理化简
  • static void main(String[] args)程序入口,用于构造lexer,构造parser,再parseExpr,最后简化并输出
  • static String coeMulFac(String expression)用于实现将如exp((2+x))^2==exp((4+2*x))
  • static String simplifyExpression(String expression)实现对exp((2+2*x))==exp(1+x)^2
  • static String simplifySubExpression(String subExpression)将提取出的最大公因子,如exp((2+2x))里的2+2x化为1+x^2,之后将 ^前面的当作poly,后面的当作最大公因子提取出来

不难发现,本人hw3各个类的属性和方法和hw2的额差别不大,是指新增了部分类和求导方法,hw2设计的类和方法在hw3依然可以用,这体现了好的迭代性的重要性

值得注意的是,前5个类Expr,Number,Var,Term等类均实现了接口Factor,2个类实现了接口Derivative,便于统一管理

分析维度

利用MetricReloaded分析本人设计的方法如图,可以发现以下问题:

  • findGreatestCommonFactor ev(G)复杂度高,在于方法长而复杂

  • Unit.toString,simplifySubExpression和parseFactor的iv(G),v(G)复杂度高,原因在于频繁调用其他类,耦合度高

MethodCogCev(G)iv(G)v(G)
Deri.Deri(Expr)0111
Deri.toPolynomial()0111
Deri.toString()0111
Exp.Exp(Factor, String)0111
Exp.Exp(Poly, String)0111
Exp.differential()5144
Exp.toPolynomial()4344
Exp.toString()4233
Expr.Expr()0111
Expr.addTerm(Term)0111
Expr.setIndexNumber(BigInteger)0111
Expr.toPolynomial()2233
Expr.toString()1122
Func.Func(String, ArrayList)0111
Func.parse(String)0111
Func.toPolynomial()0111
Func.toString()0111
FuncDefiner.addFunc(String)12199
FuncDefiner.callFunction(String, ArrayList)1122
FuncDefiner.simplifyExpression(String)1122
Lexer.Lexer(String)0111
Lexer.nextChar()0111
Lexer.nextNumber()2133
Lexer.nextString(int)0111
Lexer.peek()2222
Main.MoreStandardized(String)0111
Main.coeMulFac(String)15166
Main.f(List, List)13166
Main.ff(String, List, List)29199
Main.findGreatestCommonFactor(List)11658
Main.main(String[])1122
Main.simplifyExpression(String)4133
Main.simplifySubExpression(String)11166
Number.Number(String)0111
Number.getNumber()0111
Number.toPolynomial()0111
Number.toString()0111
Parser.Parser(Lexer)0111
Parser.parseDeri()0111
Parser.parseExpr()2133
Parser.parseFactor()1471213
Parser.parseFunc()1122
Parser.parseIndexNumber()5244
Parser.parseTerm()4155
Poly.Poly()0111
Poly.addPoly(Poly)2133
Poly.addUnit(Unit)2122
Poly.differential()1122
Poly.equals(Object)3324
Poly.getUnitlist()0111
Poly.hashCode()0111
Poly.isFactor()7444
Poly.mulPoly(Poly)3133
Poly.powPoly(BigInteger)3133
Poly.setMark()2133
Poly.toString()3334
Term.Term(char)2112
Term.addFactor(Factor)0111
Term.mergeNumber()3133
Term.reverse(char)1122
Term.toPolynomial()1122
Term.toString()6155
Unit.Unit(BigInteger)0111
Unit.addExp(Poly, BigInteger)2133
Unit.addVar(String, BigInteger)2133
Unit.differential()8166
Unit.equals(Object)3324
Unit.getCoe()0111
Unit.getExpmap()0111
Unit.getSize()0111
Unit.getVars()0111
Unit.hashCode()0111
Unit.isFactor()4434
Unit.mulUnit(Unit)4155
Unit.setCoe(BigInteger)0111
Unit.setExpmap(HashMap<Poly, BigInteger>)0111
Unit.setMark()0111
Unit.setVars(HashMap<String, BigInteger>)0111
Unit.toString()2231111
Var.Var(char, String)0111
Var.differential()0111
Var.toPolynomial()1222
Var.toString()0111

利用MetricReloaded分析本人设计的类如图,可以发现以下问题:

  • Main,FuncDefiner类的OCavg较高,说明控制流结构复杂度较高
ClassOCavgOCmaxWMC
Deri113
Exp2.6413
Expr1.638
Func114
FuncDefiner3.67711
Lexer1.427
Main5940
Number114
Parser3821
Poly2.5430
Term2.5515
Unit2.651145
Var1.2525

架构分析

和hw2的架构基本一致,多了求导部分,新增Deri类实现expr的求导功能,返回求导后的poly结构,新增Derivative接口管理Var,Exp类,实现var和exp的求导

优点:

  • 可迭代性强

缺点:

  • 控制流结构复杂

分析自己的bug

  • 强测和互测均发现了一个bug,为同质bug,主要原因是笔者在设计优化函数的时候错误将exp((-x))^2里的2合并成了exp((2*-x))正确性虽然没问题,但由于不是我心中的标准形式,在首项的符号没有被放在第0位导致之后之后没有正确隐藏-,之后又经过在每个-前面添加+的操作并以+为分隔符的操作,成功将2*与-x错误分割,之后输出到字符串result中导致错误
  • 关于优化成为劣化的问题,主要是我虽然把合适的公因子(保证找出的是正数)找出,并提出到exp外面以缩短输出长度,但由于我在最终输出的时候忘记把+-替换为-从而导致优化未必成为优化(哭死

分析自己发现别人程序bug所采用的策略

  • 本人初期保证正确率时出现的bug对应的自己编造的样例
  • 优化时引入的新bug 对应的自己编造的样例
  • 结合特殊样例如(x-x)^0
  • 将被测代码跑评测机,并简化错误样例输入以满足合法性

互测

hw1:

本人互测时成功hack了别人多次,举个hack的输入样例:

0
(x ^ +004 + -9 + -9 + x ^ +004 - 7 + x ^ 7 - x ^ +0 - -3 - -14)^+2

错误原因为房友没有正确处理 x ^ +0

hw2:

本人互测时成功hack了别人2次,举个hack的输入样例:

0
99999999999999999999999-99999999999999999999999

错误原因为房友用int而不是Biginteger

hw3

本人互测时成功hack了别人0次,不得不感叹群友太强了

分析自己进行的优化

hw1:将计算方式有项间两两相乘改为利用hashmap实现运算,速度和性能上提升

hw2:实现如exp((10+10x))优化为exp((1+x))^10

感想体会

  • 一个好框架能让我避免重构的痛苦,先设计再编码能避免我们头脑混乱
  • 学会了解析与计算分开进行的思想,降低耦合度
  • 经历这个痛并快乐的过程,也让我认识到自己的不足,需要学习的地方还有很多
  • 学会读题,题目中的信息几乎都是有效信息,如果错过可能导致bug

未来方向

建议:

  • 互测提交基础分数可以稍微提高,然后诸多限制也让我很难debug
  • 建议迭代难度平滑一点,hw1到2难度过大,2到3则难度小了许多
  • 加强基础知识的讲解
  • 建议性能分不能仅仅以长度为唯一标准,可以考虑加入运行时间等作为另外标准
...全文
44 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧