OO第一单元作业总结——表达式求导
OO第一单元作业总结
第一次作业
基于度量分析代码结构
基本算法
第一次作业是简单多项式导函数求解,不需要对输入数据的合法性进行判定,
基本思想是用 (coeff, expo)表示二元组 coeff*x**expo,而多项式中的每一项都可以以二元组的形式存储,这样做的好处在于多项式的每一项存储形式规范单一,求导规则随之变得很简单,再加上由于不需要考虑输入数据的合法性与否、化简相对简单,使得实现很快。
当然缺点也很明显,可拓展性很差,只能支持简单的多项式,下一次迭代必须得重构了。
UML类图:

耦合度分析:

结合代码具体分析,本次作业写了四个类,主类;Polycomputer,用于解析字符串生成表达式Poly对象;Poly类,采用ArrayList存储表达式,并且把对Poly的各种数据管理都放在这个类里了,如添加新的项、求导、化简等等,Term类,表示项。
耦合度分析显示,整个程序的基本复杂度最小,说明程序的结构化程度整体较好;
而方法smpAndPrint()和 PersePoly()干了较多的工作,导致圈复杂度(v(G))和模块设计复杂度(iv(G))都比较大。这说明smpAndPrint()模块设计的不够好,导致模块间频繁调度,该模块也承载了较多的功能,控制语句较多。
仔细想来这两个方法的实现确实不够好,由于Poly类用List存储表达式,导致了化简的时候不得不创建一个新的 HashMap,将化简后的表达式用HashMap表示,这样放不到任何一个已有的类中,只能够顺带打印输出,这样做不仅消耗了内存资源,还极大地限制了可拓展性,例如我们就不能对单独的项进行操作,也不能单纯地进行化简处理。若Poly类中采用HashMap的存储方式,就可以很好地解耦。
公测
强测测出一个bug,当表达式化简后有系数为0的项时输出可能为“0”而不加任何连接符,得分也只有91分了,于是深刻认识到强测的重要性。
问题所在类和方法为Poly类的smpAndPrint(),由于该方法设计得不好,导致打印的时候没有特判0,出错了。
互测
我方被hack了6次,都在攻击这个点呜呜呜;
互测策略
先是采用了手动构造数据进行测试,黑盒测试,没有自己观察被测程序的代码设计结构,但是我认为这次作业比较简单,大家写的大同小异,于是在边界条件、组合模式、化简上着重测试,例如系数或指数为0的情况,hack成功5次,最后对对方代码进行简单地查看,发现非同质bug应该是3个;
之后,由讨论区的帖子的启发构造出了简陋的评测机,反而一个bug也没测出来。
第二次作业
基于度量分析代码结构
基本算法
第二次作业增加了简单的正余弦函数,和一些组合规则,并且需要对数据的合法性进行判断;
是重构的一周~本次作业和上一次作业需要很大的改动,二元组是不行了,数据的输入也得进行判断;但是我发现每一项经过化简都可以表示成a*x**b*sin(x)**c*cos(x)**d的形式,于是采用了四元组(a,b,c,d), 并且吸取了上一次的教训,表达式类Expression采用HashMap<(b,c,d),a>的容器存储表达式,这样有利于合并同类项,也有利于进一步的化简。对于输入数据合法性的处理,则是通过大正则判断,但是解析字符串提取项的时候则是更有针对性的小正则。
本次作业动笔的时候还没有想好应该怎么化简,完成基本功能后,出于对实现难度和正确性与性能之间的权衡考量我决定只依据sin(x)**2+cos(x)**2=1进行化简。
思路如下:实现在Expression类中实现simplify方法重新创建一个hashmap<(a,b,c+d),List<c>>,其中(a,b,c+d)为新加入的矢量类SmpVector,(a,b,c+d)完全相等的项,意味着有希望进行合并化简,于是将有希望的项放在一个容器里,分别处理每个List,使得c1和c2相差2的可以合并,这样就能够实现化简啦。
UML类图:

耦合度分析:

从度量分析来看,Homework2虽然实现了很多类,但是设计结构还是比较清晰的,总的来说各个类还算是各司其职,唯一不足的是Expression的重写的toString方法和DealString的StringParse方法,可能写的太复杂了?应该每一项实现toString然后在Expression中调用的。DealString太大了。
公测
有了上一次强测错一个强测分降到90、ROOM降到B屋的惨痛教训,在中测提交前我就好好测了测,对Python语法不够熟练,不太会用Exger和Sympy库,这次没来得及造评测机。
全程手测,自己构造测试数据,构造策略就是先是构造基本数据,确保基本功能正常实现;之后考虑边界条件、数据合法性、尽可能多的组合规则等等构造一些复杂的数据,尤其是针对数据的合法性以及边界条件,几乎每一次的构造数据都会涉及边界情况,如系数指数计算化简之后为0,合并之后相消等等。这样构造测出了不少bug,专门花了一个晚上,所幸,中测和强测就全AC了,但还是被同学hack了。由于只做了比较基础的优化,性能分获得了14.00021/20.
我觉得对拍还是很重要的,可惜我的python全忘了。
互测
被HACK:这次ROOM互测大家都很礼貌,见好就收,不故意提交同质数据。还是被hack了一个bug,是在toString打印的时候,特判符号应该在每一项都特判,然而我只有表达式的第一项才特判。bug确实出在了圈复杂度和模块设计复杂度高的地方ORZ。
HACK:
手动评测,感觉这屋都是大佬,代码风格都不错,手动测试了一下午一无所获,悻悻离开。
周日晚上再测,发现Saber我给漏掉了,手动构造奇异的数据测了两小时竟然找出了5个bug(后来发现至少三个非同质)。突然感觉手动构造其实优势也很明显,如果是自动评测可能不一定测的出来,毕竟有的数据我也是根据他的反馈结果一步步构造出来的,还挺刁钻。
第三次作业
基于度量分析代码结构
这次作业增加了表达式因子的数据,组合方式多种多样,支持嵌套,求导规则变多,对数据合法性的检查要求也高了,感觉难度大了很多,我差点以为要无效作业了。
又是重构的一周~上次决定用四元组的时候就意识到了第三次作业必然又要重构了,虽然试图对架构进行了更高的抽象,但不知道怎么实现,于是无奈只能用四元组。恰好这次课讲了继承和接口,突然意识到其实我之前思路实现的障碍完全可以用接口来解决。
本次作业的思路如下:采纳了指导书的建议,将 sin(x) cos(x) 幂函数因子 常数因子 这些基础因子和加法``乘法 嵌套 这些基础运算视为不同的类,然后就可以实现表达式树的构建。他们都实现了共同的接口Function,支持求导takeDerivative和 toString。在这里我采用的是二叉树实现,也就是基础运算都只支持两项运算。
这样总的思路就比较清晰了,构建二叉树的时候解析字符串从下至上开始构建;输出表达式树只需要调用表达式的toString方法即可递归实现。
我认为这里处理字符串的时候需要小心,由于有表达式因子我们无法继续使用大正则判断提取字符串了。这里我将最外层的括号换成[],[]里面的内容全部换成E,这样就可以使用正则表达式提取字串,也能初步判断表达式的合法性;为了进一步解析子表达式可以将之前的内容替换回来,递归判断即可。所以二叉树的构建和输入数据合法性的检验都是在递归中一步步进行的。
UML类图:

耦合度分析:

这次作业主要是求稳,但求作业有效、强测能够全部AC。由于还有时间于是还是忍不住进行了简单地优化。化简主要是针对运算符.例如对于*,若两个操作数是常数a,b ,则返回常数a*b ;另外还有一些关于0 或+1或-1 的特判......等等这样实现简单易行,并且由于是递归,其实化简也会递归地进行下去,不仅能够减轻递归的负担,防止TLE,而且化简的效果也很好.
当然,后来稍微想了想,如果表达式树是多叉树,即加法和乘法可以有多个操作数,采用上述思路化简的效果可能会更棒.可惜当时已经写好了基本架构,难以大改,只能作罢.
公测
在debug的时候全程手测,由于时间来不及再加上对python语法不熟悉,没有评测机,手测是无奈之举.对复杂的程序对拍真的是一个很好的选择,由于括号太多稍微复杂一点的输入就会得到括号很多的输出,难以肉眼判断正确与否,手动构造数据真的风险很大.所幸的是可能这次算法比较简单,很多都交给递归自动完成,公测没有出现bug,性能得分17.0578/20.
互测
被HACK: 本次作业没有被有效HACK;
HACK: 由于没有评测机,只能够手测,构造一些简单的数据. 为此,我着重从合法性的层面进行测试,即构造一些数据,使得输出应该是合法的表达式,但是他却输出WF或者不合法的表达式. 通过这种方式hack成功了3次.
对比和心得体会
观摩了四位大佬的优秀代码,我发现林宇菁同学实现了我的改良方案,即多叉树。最让我印象深刻的是他的代码结构组织得非常清晰,每个方法不超过五十行,递归在同一个类的不同方法中调用进行;此外他的化简方法也让我印象深刻,他是先构建后化简,我是边构建边化简,不能说孰优孰劣吧,可能我的方法在资源消耗上占优,他的方法在代码组织上占优,解耦得很好。总之,相比之下,我在组织方面、架构设计方面还有很多可以学习。
本单元的学习让我对面向对象思想有了更深的理解和体会,总的来说能看出来我的三次作业都是在不断尝试使用面向对象的思想的,强测的平均分也在稳步提升. 这一单元学到了很多有用的知识, 但仅凭课上的学习是不够的,还有很多设计思想需要学习消化. 另外,这次作业也有一些遗憾,比如虽然在第三次作业我尝试使用工厂模式,但好像没有起到应有的效果;手测和对拍都很重要,我应该好好掌握如何实现对拍. . . . . .
但总的来说是不错的, 加油 ~
OO第一单元作业总结——表达式求导的更多相关文章
- OO第一单元总结(表达式求导)
写在前边:第一次接触面向对象语言,编程思想仍然不可避免的有以前面向过程的影子.从第一次作业的完全面向过程,到第二次学会剥离各个类互不影响到第三次作业的先构思面向对象的基本程序架构再编程.虽然程序有些地 ...
- OO第一单元总结__多项式求导问题
作业一.含幂函数的简单多项式的求导 (1)基于度量的程序结构分析 1. 统计信息图: 2. 结构信息图: 3. 复杂度分析 基本复杂度(Essential Complexity (ev(G)).模块设 ...
- 【OO学习】OO第一单元作业总结
OO第一单元作业总结 在第一单元作业中,我们只做了一件事情:求导,对多项式求导,对带三角函数的表达式求导,对有括号嵌套的表达式求导.作业难度依次递增,让我们熟悉面向对象编程方法,开始从面向过程向面向对 ...
- 【作业1.0】OO第一单元作业总结
OO第一单元作业已全部完成,为了使这一单元的作业能够收获更多一点,我回忆起我曾经在计算机组成课设中,经常我们会写一些实验报告,经常以此对实验内容反思总结.在我们开始下一单元的作业之前,我在此对OO第一 ...
- OO第一单元作业总结
oo第一单元的作业是对多项式的求导.下面就是对三次作业分别进行分析. 第一次作业 分析 第一次作业相对来讲比较简单,甚至不用面向对象的思想都能十分轻松的完成(实际上自己就没有使用),包含的内容只有常数 ...
- 北航OO第一单元作业总结(1.1~1.3)
经过了三次作业之后,OO第一单元告一段落,作为一个蒟蒻,我初步了解了面向对象的编程思想,并将所学内容用于实践. 一.第一次作业 1.架构分析 本次作业需要完成的任务为简单多项式导函数的求解.表达式仅支 ...
- OO第一单元作业小结
前言 第一单元的主题是表达式求导,第一次作业是只带有常数和幂函数的求导,第二次作业加入了正余弦函数,第三次作业又加入了表达式嵌套,难度逐渐提升.总体来说前两次作业还易于应对,而第三次作业做得相对有些艰 ...
- OO第一单元作业总结之初识面向对象
第一个单元的三次作业均为求导,循序渐进的让我们掌握如何构造类和方法,让整个代码是面向对象的设计而不是面向过程的设计.如果第一次作业和第二次作业你只是简单的对过程着手架构类,到了第三次作业就会变得格外麻 ...
- 2019北航OO第一单元作业总结
一.前三次作业内容分析总结 前言 前三次作业,我提交了三次,但是有效作业只有两次,最后一次作业没能实现多项式求导的基本功能因此无疾而终,反思留给后文再续,首先我介绍一下这三次作业,三次作业围绕着多项式 ...
随机推荐
- 【Saas-export项目】--项目整合(spring整合MVC)
转: [Saas-export项目]--项目整合(spring整合MVC) 文章目录 Spring整合SpringMVC(export_web_manager子工程) (1)log4j.propert ...
- 【Azure 服务总线】Azure Service Bus中私信(DLQ - Dead Letter Queue)如何快速清理
在博文ServiceBus 队列中死信(DLQ - Dead Letter Queue)问题一文中,介绍了服务总线产生私信的原因及可以通过代码的方式来清楚私信队列中的消息,避免长期占用空间(因为私信中 ...
- Pytorch1.7报错 Output 0 of UnbindBackward is a view and is being modified inplace
utils里内容改成 if scale_each is True: for idx, _ in enumerate([jj for jj in tensor]): t = tensor[idx] # ...
- Java 中为什么要设计包装类
尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...
- CVE-2016-5734-phpmyadmin-4.0.x-4.6.2-代码执行
参考 https://www.jianshu.com/p/8e44cb1b5b5b 漏洞原因 phpMyAdmin是一套开源的.基于Web的MySQL数据库管理工具.在其查找并替换字符串功能中,将用户 ...
- Hi3559AV100 NNIE开发(4)mobilefacenet.cfg参数配置挖坑解决与SVP_NNIE_Cnn实现分析
前面随笔给出了NNIE开发的基本知识,下面几篇随笔将着重于Mobilefacenet NNIE开发,实现mobilefacenet.wk的chip版本,并在Hi3559AV100上实现mobilefa ...
- 数据库Redis(一)
Redis数据库的特点: Redis数据库属于nosql数据库的一种,其存储于内存中(非硬盘),修改较为方便. 而Redis数据库的存储方式是使用{key:value}方式存储,类似python基础中 ...
- dfs求连通块
递归 递归是什么?绝大部分人都会说:自己调用自己,刚开始我也是这样理解递归的.确实没错,递归的确是自己调用自己.递归简单的应用:编写一个能计算斐波那契数列的函数,也就是这样: int fb(int n ...
- CSS网页的布局
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...
- 五个问题,三大策略,手把手教你定制App性能监控方案
作者:友盟+U-APM团队 Why? 为什么要做应用性能监控? 首先,我们要知道应用性能监控具体指什么?以及目的: 监控是一套完整的"监视+报警"的系统.对于像我们这样的App开发 ...