BUAA - OO - Unit1

目录

 一、架构设计

1.1 第一次迭代

1.2 第二次迭代

​1.3 第三次迭代

1.4 可扩展性

二、优化策略

2.1 结果长度优化

2.2 运行时间优化

三、结构度量

四、Bug分析

五、Hack策略

5.1 数据生成

​5.2 答案检查

5.3 评测策略及外观

六、心得体会

七、未来方向


 一、架构设计

 1.1 第一次迭代

采用了递归向下的解析思路,解析完后转化成多项式Poly(),同时进行合并同类项的简化。最后输出化简后的多项式

1.2 第二次迭代

整体上新增了两个因子类 TrianFactor 和 FuncFactor

  • 由于 TrianFactor 的加入,使得单项式 Mono 不再像第一次作业那样形式简单统一,故对 Mono 进行了修改,为其添加了两个HashMap类型的成员变量,使其能够统一于以下形式:

  • 相应地,为了检查并合并同类项,需要增添一些方法,细节不再赘述

  • 对于新增的递归函数,我写了工具类 FuncHandle ,用来实现 获取定义、解析参数、替换参数、计算并储存函数表达式 等操作。工具类中的核心方法 computeFn(int n , String arg1 , String arg2) ,功能是得到 f{n}(arg1 , arg2)的展开式。思路是记忆化递归。

1.3 第三次迭代
  • 新增求导因子类 DeriveFactor 。我选择 将求导算子 dx() 视为一种因子,对其的处理与对 FuncFactor 的处理如出一辙,这使得我的结构非常清晰,写起来很舒适。然后我在 Expr、Term、各个 Factor 类里新增了求导方法 derive()

  • 另一个新增的自定义普通函数,对我的架构几乎没有任何影响,因为其地位与第二次迭代的递归函数f{0}f{1}相同,仅格式上略有差异。

1.4 可扩展性
  • 当新增其他种类的因子,如e指数、ln指数时,在最底层即他们自身的类里完成toPoly()toString即可。而 Poly 的基本计算单元是 Mono , 只需为 Mono 增添新的成员变量,使其适应统一形式,然后修改equal 方法即可

  • 当新增其他像函数、求导、简单积分等计算时,可以选择将其视为一种因子,与第二、三次迭代的处理方式相同。与函数类似的展开、字符串替换等需求,可交给工具类处理;与求导、积分类似的,从 Expr 到 Factor 自上而下地添加相关方法,向下调用即可

二、优化策略

2.1 结果长度优化
  • 合并同类项:

    Mono中重写equal()方法,逐个因子比较,由此判断是否为同类项

  • sin(-x) = - sin(x)

    cos(-x) = cos(x)

2.2 运行时间优化
  • 选择恰当的合并同类项的时机。Poly类的addMono()方法执行时,会向poly列表中添加mono,此时遍历poly列表,进行同类项的合并。我认为这是最佳的合并时机

  • Mono类的equal方法做好剪枝。这无疑是时间复杂度最高的地方,因为要遍历两个Mono列表的每一个因子,而且要调用三角函数的 “底数” Poly类型的 equal 方法,实际上是一个来回递归的过程,故剪枝变得十分有必要。简而言之就是选择适当顺序进行比较,若不同则立即返回False,避免接下来的冗杂比较。

  • 对递推函数的展开采用上文所述的记忆化递归的方式,不再赘述

三、结构度量

使用了IEDA的插件 MetricsReloaded 进行分析,可以看到,FuncHandleMonomialPolymial 的复杂度显著偏高

分析各个类的方法,找到导致复杂度偏高的原因

  • FuncHandle类的extractPara()方法,模拟了栈,并结合正则表达式对定义式的参数进行提取,复杂度较高

  • Monomial类和Polymial类的equal()方法,分支循环较多,导致复杂度偏高

四、Bug分析

本人在第二次作业中,提取递推表达式的参数时,先截掉了f{n-2}后面的函数表达式,然后进行了正则匹配,简单高效。然而在第三次作业中,我因为大意疏忽忘记了普通二元函数可出现在定义式的形参里,故没有修改方法。

尽管如此,强测依托助教的慈悲之心,我还是侥幸通过了所有数据点进入了A房,然后互测时被刀的体无完肤。

惨痛的互测结束后,我通过模拟栈来判断括号嵌套层数,进而解析函数的参数

五、Hack策略

为了便于对拍和互测,我用python写了评测机进行辅助

5.1 数据生成

采用递归向下的思路生成数据。为了便于 debug 和 适应互测要求,需要对数据进行限制,为此我设置了几个阈值控制数据范围

5.2 答案检查

我尝试过三种方式进行 check ,个人认为第三种效果最佳

  • 代入浮点数比较

    与本次作业类似地写一个带入数值计算结果的项目,将输入和输出分别带入浮点数计算,选择合适精度比较是否相等

    此方案简单迅速,但经过实践,误差较大。精度在7位-16位之间都会出现 AC 和 WA 互相误判的情况,所以此方案最不可取

  • 使用 python 里的 sympy

    此方案精度高,很少出错,而且几行便可写完。但是缺点也十分明显:运行速度太慢,使用UI界面后容易卡顿,故不算最佳

  • 利用Polyequal()方法与自己答案对拍。

    即修改第二次迭代的项目,删除函数相关内容(因为结果必不含 f ), 然后将自己答案和待测答案解析成多项式后调用equal比较是否相同。

    此方案效果最佳,兼具精度和效率。当然,需要保证自己代码的正确性,并且确保自己的化简程度更高(对于自动化随机评测来说,适度化简即可)

5.3 评测策略及外观
  • 外观上我利用pygame库实现了UI界面,使得体验上更加舒适

  • 首先是评测前进行阈值设定,控制生成数据的复杂度

  • 设有普通模式和群测模式两种,前者用来1v1对拍,后者应用于互测

  • 评测时可以采用随机生成数据和使用本地数据两种方式。本地数据主要是为了适应互测的Cost限制,对命中的随机数据作出修改再提交

  • 使用多线程,同时评测多人多组数据,节约时间

六、心得体会

从写迭代作业,到写评测机debug,再到互测、修复,可以说,我的一周充满了OO。我投入了很多精力,也收获了很多心得

  • 有一个好的整体架构便是成功了一半

  • 慎用正则,正则不是万能的

  • 利用好讨论区、研讨课、互测,学习他人的优点

  • debug时利用好Junit单元测试多和其他同学对拍根据规则构造更全面的数据、极端的数据

七、未来方向

可以考虑将自定义递推函数与自定义普通函数的迭代出现顺序互换,由简入难

其他都挺好的,感谢课程组老师和助教们的辛勤付出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值