全文共3179字,推荐阅读时间10~15分钟。

文章共分四个部分:

作业分析

第一次作业

第一次作业要求我们实现一个简单的幂函数求导工具,没有乘积和复合的情况。

UML图如下

  • 代码结构

第一次作业的代码结构并不复杂,从主类调用Poly构造多项式,然后在Term中建项。没有涉及到继承和多态,结构和逻辑都比较清晰。

  • 思路解析
  1. 主类中调用Poly进行构造,在构造的同时进行合法性检查。(由于保证输入正确,所以并没有影响。)

  2. 由于第一次作业只涉及简单的加减连接,所以可以直接使用split()函数进行拆解后建项。

  3. 求导按照公式进行即可,最后把结果存在一个多项式中输出。

  • 复杂度分析

从反馈结果可以看出,由于把合法性判断和构造融合在一起,造成了isLegal()方法和getTerm()方法的复杂度过高。在第一次作业中,其实这两个功能并非紧耦合,完全可以拆开分别实现。在其他方法中,还是比较好的体现了“高内聚低耦合”的设计思想,均没有较高的复杂度。

第二次作业

第一次作业要求我们实现一个的幂函数、三角函数混合的求导工具,出现了乘积的情况。

UML图如下

  • 代码结构

代码架构基本延续了第一次的设计风格,新增了RegExp的自定义工具类以及\(ax^bsin^c(x)cos^d(x)\)的指数存储类。

  • 思路解析
  1. 基本思路和第一次相同,但是新增使用正则表达式判断非法格式的功能。

  2. 对于每一个分割出来的项,先化简为前文提到的标准格式,之后再进行求导操作。

  3. 化简时构造了一个“循环熔断”,根据某一项的系数和指数的大小,构造出可以合并的两种可能项,但后在Poly中进行查找。这样的构造循环执行5次基本可以实现所有能够利用\(sin^2(x)+cos^2(x)=1\)合并的情况。(5~6次是10000组测试用例的实验结果,可以称为经验结论。)

  • 复杂度分析

从反馈结果可以看出,相比于第一次作业中判断非法和构造项的紧耦合情况,在单独提取出判断部分后,Term的构造方法复杂度直线下降。但是由于isLegal()部分没有做优化,导致复杂度相比于第一次有所上升。现在回看起来,isLegal()的复杂度完全可以通过异常来判断。只要保证每一层的预处理正确,进入下一层后,出现不能解析的情况就一定是WRONG FORMAT.(但是这种方法的逻辑复杂度很高,需要编程人员提前考虑到所有的特殊情况,避免误判。)

第三次作业

第三次作业要求我们实现一个含有幂函数、三角函数,并且支持多层嵌套的求导工具。

UML图如下

  • 代码结构

由于第三次作业使用了递归的思路来进行链式求导操作,导致了第三次作业的代码结构和前两次完全不同,并且引入了继承和多态。但是基本的逻辑框架没有变,依然是Poly->Term->Factor的解析层次,只是出现了例如Factor->Poly的逆构造情况。

  • 思路解析
  1. 将格式错误交给异常处理

  2. 使用多态来实现链式求导

  3. 输出时依然采用递归的思路,层层求导,层层返回。

  • 复杂度分析

从反馈结果可以看到,前两次作业复杂度最高的部分都出现在格式判断上,但因为改变了WRONG FORMAT的判断方式,这一次没有再出现了。但是构造方法却出现了比较高的复杂度——我想原因就出现在逆向构造上。由于逆向构造代码流程的复杂性,其实可以引入工厂模式,将每个构造(包括正向和负向)都有传入的参数来决定,这样可以封装对象的创建逻辑,降低复杂度。

评测相关

自己的bug

第一次作业

在第一次作业中,由于将求导公式:

  • \((Cx^n)'=Cnx^{n-1}\)
  • \((C)'=0\)

误认为前者在\(n=0\)时和后者是完全等价关系,造成存求导结果时指数为-1和0的项的混合,导致了系数计算错误。粗略地看会认为是数学问题,但其实是对Java容器的equals()方法不熟悉,在放置时应该考虑到原项和求导结果的分离。

第二次作业

在第二次作业中,由于对正则的理解存在误区:

str.replaceAll("++|+++","+");

对于存在相互包含关系的正则,在匹配时和顺序没有关系,而是采用最优匹配原则。这就直接导致了在出现以下类型的式子时:

\(+++x\)

会得到:

\(++x\)

而不是:

\(x\)

因为我并没有采用大正则直接提取项的方式,所以在预处理中就会出现很多难以预料的情况。(其中大多数都是特殊数据的问题)这也是一种常见的“各有损益”的情况,大正则有较高的代码复杂度,但是预处理就对逻辑判断提出了更高的要求,所以OO教我的这一堂“取舍”课,算是很深刻了。

第三次作业

第三次作业中,由于使用了递归思想进行多项式的拆解,所以解析每一层时,符号的省略问题就是重点。在递归时,我少考虑了Factor->Poly的逆向构造情况,在解决\(-(-(x-x))\)时就会造成第二层减号被错误解析。

在反思的过程中,我发现导致这个错误的原因主要在于:我在重写toString()方法时,都是只保证了在debug模式时的预览框中自己能够看懂即可,无意地丢掉了很多有用信息,也正是因为丢掉了-号才导致我不能够找到递归层次的缺失。

Hack所用的策略

主要有两种方式:

  • 使用完成作业时有意义的样例进行测试
  • 利用评测机自己生成数据

三次作业中,发现的Bug基本都出现在优化部分,具有代表性的几种可以列举如下

  • 1的省略忽视了123等以1开头的数据,造成结果为23.
  • -1)不能随意省略,因为-1可能作为指数而不是系数。
  • \((-(-(x-x)))\)等多层嵌套的情况会因为没有优化而一味递归造成memory error

评测机简要介绍

这是评测机的working directory

  • center:存放评测的核心控制代码,用于组织编译->运行->反馈功能

  • data:存放自动生成的数据

  • download_data:存放测试中出现问题的数据,可以用于回归测试。

  • factory:存放数据生成代码

  • output:存放各个测试代码的输出

  • result:存放各个测试代码的结果

  • ruler:存放标程

  • src:存放源码

  • filter.py:用于格式化数据以提交

在一开始开发评测机时,由于浮点运算对性能的高要求,就考虑过适配服务器端的评测机,所以并没有采用.bat批处理文件。最后选择了单纯地使用python进行编译->运行->验证->反馈的评测过程,同时使用ssh和服务器端进行交互。(之后在CentOS/Ubuntu上实测可以进行作业处理)

在整个第一单元的学习过程中,除了OO作业教会我的迭代思想之外,根据需求不停优化评测机对我自己的OOP工程能力其实也是一个不可忽视的方面,甚至还能同时学习python的OO模式。

考虑到评测机的迭代问题,我把验证逻辑单独封装在一个包中,每一次更新只需要根据作业的数据要求修改修改验证逻辑中的数据生成单元即可。

我最开始开发评测机时主要是为了测试自己的问题,之后因为互测的存在才想着增加评测机的回归测试、对比测试等功能,随着OO课程的学习,预感还会有更多的花样能被加进我的小小评测机里。

重构策略

在三次作业的完成过程中,我都是先确定好架构再开始完成的,所以没有遇到需要从头开始重构的情况。

但是值得一提的是,出于扩展性,本来是想要在第二次作业就实现链式求导的,奈何链式求导化简复杂度相比于基本形式的化简高了许多,因此最后没有采用链式求导来完成第二次作业,如果选择了可能就会重构了吧。

初体验感受

总的来说,OO的初体验还算开心和满足,每一周我都能问心无愧地说尽了自己的最大努力,每一次都想要做到自己能够到达的最好程度,尽管每次都会不多不少有一个事后看起来无比明显的Bug。

如果说前两次的Bug让我积累到了学术上的、技术上的经验,让我对Java语言本身和评测机开发都有了不一样的理解之外。那么第三次的Bug是在教我一些人生路上的道理。

为什么这么说呢?在完成作业后,我会和朋友们一起讨论优化,然后就提到了一种-的处理方式,听懂了后我觉得很有道理,就直接改了提交了。因为修改部分逻辑也不复杂,所以我自认为不会影响正确性,因此之后朋友提出让我帮忙用评测机测试时,我也就毫不犹豫地答应了。之后一直到周六的截止日期前,我都在帮忙找Bug.

可是谁能料到?周日我下载下来互测room中其他同学的代码后,和自己的代码一起测试,一测,就出问题了……

这能怪谁呢,忙是我自己答应要帮的,提交按钮也是我自己按的。无比明显的键盘失误就摆在我眼前,但是已经没有办法改变了。

到最后公布结果,看到这个Bug给我带来的损失很大,但是我却一下子释然了——这就是人生路上总会遇到的境况,谁都有为了别人忘记自己的时候,这种行为从来没有好坏和对错,日子过得心安和快乐才是最重要的,何况更重要的是,我自己的评测机能把自己的问题给找出来,这才是最大的收获啊!

一周前的我也很难想象,OO居然能给我上这样一堂和课程内容本身没有什么关联的课。

谢谢讨论区里的老师、助教、同学,是大家让我真正领会了一句很久以前看见的话:

一个人可以走得很快,但一群人才能走得更远。

Unit1-窝窝初体验的更多相关文章

  1. Nginx unit 源码安装初体验

    Nginx unit 源码安装初体验 上次介绍了从yum的安装方法(https://www.cnblogs.com/wang-li/p/9684040.html),这次将介绍源码安装,目前最新版为1. ...

  2. .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验

    不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...

  3. Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验

    Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...

  4. Spring之初体验

                                     Spring之初体验 Spring是一个轻量级的Java Web开发框架,以IoC(Inverse of Control 控制反转)和 ...

  5. Xamarin.iOS开发初体验

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAA+CAIAAAA5/WfHAAAJrklEQVR4nO2c/VdTRxrH+wfdU84pW0

  6. 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...

  7. 【Knockout.js 学习体验之旅】(1)ko初体验

    前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...

  8. 在同一个硬盘上安装多个 Linux 发行版及 Fedora 21 、Fedora 22 初体验

    在同一个硬盘上安装多个 Linux 发行版 以前对多个 Linux 发行版的折腾主要是在虚拟机上完成.我的桌面电脑性能比较强大,玩玩虚拟机没啥问题,但是笔记本电脑就不行了.要在我的笔记本电脑上折腾多个 ...

  9. 百度EChart3初体验

    由于项目需要在首页搞一个订单数量的走势图,经过多方查找,体验,感觉ECharts不错,封装的很细,我们只需要看自己需要那种类型的图表,搞定好自己的json数据就OK.至于说如何体现出来,官网的教程很详 ...

  10. Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验

    Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出E ...

随机推荐

  1. Spring 中的事件处理

    Spring 中的事件处理 Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期.当加载 beans 时,ApplicationContext 发布某些 ...

  2. Spring 注入内部 Beans

    注入内部 Beans inner beans 是在其他 bean 的范围内定义的 bean. 下面是一个基于setter注入的内部 bean 进行配置的配置文件 Beans.xml 文件: <? ...

  3. 计蒜客 - Fantastic Graph

    题目链接:https://nanti.jisuanke.com/t/31447 知识点: 最大流 题目大意: 给定一个二分图,左边有 $N$ 个点,右边有 $M$ 个点,给出 $K$ 条边.问是否能从 ...

  4. LibreOJ #515 贪心只能过样例

    题目链接:https://loj.ac/problem/515 知识点: DP.bitset类 解题思路: DP部分不难想到:从 a 到 b 遍历,然后在已有的状态上加上遍历得到的数字的平方,难点在于 ...

  5. html5做webAPP界面适配总结

    一.px em rem px像素(Pixel).相对长度单位.像素px是相对于显示器屏幕分辨率而言的. em是相对长度单位.相对于当前对象内文本的字体尺寸.如当前对行内文本的字体尺寸未被人为设置,则相 ...

  6. 开箱即用,Knative 给您极致的容器 Serverless 体验

    作者 | 冬岛  阿里巴巴技术专家 导读:托管 Knative 开箱即用,您不需要为这些常驻实例付出任何成本.结合 SLB 云产品提供 Gateway 的能力以及基于突发性能型实例的保留规格功能,极大 ...

  7. Kubectl exec 的工作原理解读

    对于经常和 Kubernetes 打交道的 YAML 工程师来说,最常用的命令就是 kubectl exec 了,通过它可以直接在容器内执行命令来调试应用程序.如果你不满足于只是用用而已,想了解 ku ...

  8. [注]一条牛B的游戏推送要具备哪些条件?

    旁白:推送内容写的好,可以给游戏带来很大的收益,但如果写的很糟糕,就可能是在提醒用户还有一个该卸载的软件没卸载.那么如何写出一个优秀的推送内容呢? 总结:推送文字八字原则 从运营的角度来讲,我们需要找 ...

  9. [JavaWeb基础] 004.用JSP + SERVLET 进行简单的增加删除修改

    上一次的文章,我们讲解了如何用JAVA访问MySql数据库,对数据进行增加删除修改查询.那么这次我们把具体的页面的数据库操作结合在一起,进行一次简单的学生信息操作案例. 首先我们创建一个专门用于学生管 ...

  10. Burpsuite代理socks流量

    一 设置sock代理 二 设置浏览器代理 三 设置burpsuite代理 四 浏览器访问验证 总结:增加取证难度,隐藏你自己ip,别光着屁股跑了O-O!