OO的奇妙冒险 ~OOP入门与字符串处理~
目录
- 总体分析
- 作业内容分析
- 作业内容总结
- 互测的收获
- 公测互测bug分析与总结
- 不太正经的个人自嗨
总体分析
公测
- 中测(基础与进阶):
- 其实在我看来,从完成作业的角度来说,中测的基础与进阶并没有任何区别,都不能挂,都不太难,都对得分没有什么影响。中测的样例总体来说非常善良,只要是测试过,几乎不会被中测阻拦。checkstyle的规则看似很多,但是在IDEA插件的支持下,见到黄色的warning直接改掉,总体来说我认为偏向于养成习惯性的举措,并不是扣分地方所在
- 强测(正确性):
- 在第一次作业之前,我十分畏惧强测的正确性,尤其是在经历了计组手动定点爆破+10万条随机都仅是中测的说法,但是,就前三次来看,强测的正确性并不严格。第一次作业比较简单,第二次作业我正则表达式根本就没有写对,面对这个十分巨大的问题,强测仅仅挂了我三组,我在发出强测不够强的想法的同时也暗自窃喜,而第三次作业的重点向面向对象转移 ,对于正确性的检查更加简单,在我连爆sin(- 9),sin(++1),sin(+++1),sin(++x)4个大型bug的情况下强测一组也没挂,互测又禁掉了WF的攻击,不得不说我并不完全认同的强测数据反而救了我一命
- 强测(性能):
- 第一次作业比较简单,第二次作业我进行了初步的贪心优化,然而偷鸡不成蚀把米,优化也出现了bug,我对第二次作业非常满意,但是对自己做出的结果非常不满意。第三次作业由于以下几个原因,我放弃了优化转战OS
- 自己能力太差
- 长度最短的L记为Lmin,就意味着1位dalao可以灭杀所有的人,dalao之下丝毫不优化和熬夜爆肝优化是同等的,我并没有直接挑战的信心和能力
- 性能分仅占5分
- OS等其他课程需要投入时间
- 第一次作业比较简单,第二次作业我进行了初步的贪心优化,然而偷鸡不成蚀把米,优化也出现了bug,我对第二次作业非常满意,但是对自己做出的结果非常不满意。第三次作业由于以下几个原因,我放弃了优化转战OS
互测:
- 第一次互测可以说是相当惨烈。在一个强测全部100分的房间内,8个人总计被找出了50个bug,4人无伤,3人轻伤,1人重伤,我认为原因有下:
- 第一次接触大规模测试,发生了\v惨案,爆栈惨案等bug
- 强测不够强,将一部分性能bug放了过去(例如x-x输出为空而不是0)
- 规则尚未完善,被hack1/x等价于被hack7/x
- 第二次互测由于作业做得很差,进入了C屋,战况同样十分惨烈。在C屋使用对拍器甚至评测机进行测试效果是非常显著地,一屋7个人人人有bug,基本就是大礼包互开的级别,我被查出了正则的巨型bug,不过考虑到客观因素(C屋里对拍器并不普及),仅被人hack3次,如果身处第一次互测的房间,应该是20次起步吧
- 第三次互测由于无法提交WF变得十分无聊,一个屋子的95.8824大眼瞪小眼,不优化的人bug反而更不容易出现,战绩0/0,0/9,不过屋里还是有人因为做了简单的优化而被人查出了bug,这次互测给我的教育是,制造SPJ时要考虑周全,先要判断这个输出是不是WF,再调用sympy进行验证,因为sympy识别的表达式并不一定满足作业要求的文法
作业内容分析
-
第一次作业比较简单
-
第二次作业正确性不太困难,但由于个人因素对正则表达式理解不深,检查不细,出现了十分荒谬的bug。优化方面,总体可以分为
- 纯第一次的策略
- 贪心合并
- 贪心合并+分拆
- 深搜+剪枝
- 我看不懂的算法
这几个层次。我采用的是贪心合并的层次,但由于是周二晚上心血来潮才开始优化,处理的很差,出现bug几乎是必然结果,也算作一个惨痛的教训
-
第三次作业比较特殊,具体体现在
- 面向过程+递归下降+简单的强测+没有WF的互测=轻松地95.8824
- 性能分计算方法+只有5分=不做优化去学习OS可能是更好的选择
在这些因素的考虑下,我选择了权衡所有的科目,进而去学习OS,从而放弃了优化任务。总的来说这次作业正确性的方法比较唯二,arraylist或树,而结合递归下降的方法,封装三种数据结构+Get类递归下降+Main类入口+预处理检查是一个简易可行的方案。面对助教所讲授的面向对象递归下降分析,我认为考虑可能的扩展性,这种封装其实并没有特别大的意义,直接在Get类里面设置Char[] origin与int mark,构造Getconst,GetF,GetT,GetE这四个方法肯能会更直接,更好实现。至于扩展方面,如果更换文法,直接继承了重写具体的某一个Getx方法即可
不过总的来说,我对第三次作业“trade-off”的结果比较满意,但对于作业的完成情况并不满意。本应是第一单元的收官之作,却在客观层面和各种时间投入策略的权衡上上变成了第一单元最简单的一次作业,对于个人来说有关OOP的收获并不如第二次上机或第二次作业的大(不过巩固了陌生的递归下降分析方法)
作业内容总结
- 三次作业的类图大同小异。总体来说思路很简单,就是封装三种数据结构,封装预处理和格式检查,封装Get类,最后留一个Main类当做入口。设计算不上优秀,但也能跑能用,没有给自己带来设计上造成的负担。为了阅读体验,图片就不放了(应该和大部分人都差不多)
复杂度检查方面
-
限于课程得分的标准,第一次作业中我并没有去试图一定要做出平衡,灵巧的设计,而是将重点放在了没有bug和最短优化上面。考虑到第一次作业相对简单的文法+对未知强测互测的恐惧+不了解正则的许多奇妙用法,我选择了直接手写有限状态机,几乎可以直接证明是符合题目要求文法的,因此没有被找出任何bug。不过,一个总共600行左右的工程,460多行给了一个类,复杂度可想而知的爆表了(最高类复杂度122)
-
第二次作业我开始尝试正则,尝试比较均衡的,比较OOP的方法。复杂度和许多人相同,爆了几个方法,爆了1个类(预处理与检查类),算是比较平庸的设计。但是从我内心来讲,不管设计如何,第二次作业都是我在第一单元对自己的情况最不满意的一次。
-
第三次作业我又喜闻乐见的爆红了,主要原因出在Get类里面的4个方法组成的递归下降分析。研讨课上昂神介绍了一种更OOP的,复杂度更低的写法,即将4种方法分开,每一种构造一个类。对于这种设计方法,我的观点有:
- 有效的降低的个别类的复杂度,比一般的设计方法更符合OO的思想
- 可扩展性有了一些提高,比如支持两种文法,比如更改某一层的文法
- 对于第三次作业来说并不太实用
之所以会有最后一条观点,主要是因为,冒着被强测互测挂掉而去优化工程结构和设计方法的,不是对分数毫不关心的我所不太能理解但是敬佩的人,就是优化做完对拍跑完嫌工程丑的大佬。后者显然不会等到研讨课才想出这种降低复杂度的方案。而对于更多的同学,比如我自己,连可行,鲁棒的优化方案都想不出来,或者干脆没有想,就更不用说美化工程了。不过,如果今后OO作业再次出现文法相关的字符串分析题(虽然我并不希望再出现了),这种设计方法就会派上用场了。
互测的收获
- 首先,互测最大的收获是提高了自己对评测机的认识
- 第一次接触评测机是在计组,在计组进入P5之后,手动测试就完全没有功效了,自动测试的需求应然而生。到了实现P7的时候,我完成了一个十分简陋的对拍器,仅仅是检查字符串,总共就是一个100行左右的C程序
- 为了应付第一次作业,我做出了一个“感知机”,顾名思义,就是只检查双方对于是不是WF的判断是否一致。到第三次作业,终于用python写出了一个差不多的判断程序。数据生成方面,从最开始的完全随机字符串(没有卵用),到马尔科夫矩阵生成的表达式,再到用Xeger生成+随机引入噪声,再到现在的Xeger部件+随机选取,我不仅了解到了很多“轮子”的使用方法,更明白了随机性的重要性
- 其次,是发现别人bug的同时,看到别人的代码
- 发现别人的bug比较简单,挂上评测机跑几个小时,有就有,没有就算,在评测机的帮助下,几乎不用过多的检查代码。不过,对于使用大型正则表达式的代码,直接对着正则表达式分析也是很有帮助的
- 第一次作业偶然查看同组Lancer的代码让我获益匪浅,因此第二三次中,我都会查看一下优化效果不错的,评测机扫不出来的dalao的代码,可以学习别人的思路,尤其是第二次作业公开展示的HDLdalao的代码,虽然并不能完全看懂,但总体模板还是很有启发性的
公测互测bug分析与总结
-
第一次作业作为OO的入门之战,不仅我自己主观来说最为重视,也客观上是我最认真的一次
-
公测相对简单,这也许是我OO12次作业唯一一次强测100了
-
互测比较神奇。我所在的互测房间,是第一次作业A组中最为惨烈的一个房间,总计找出了50个bug。然而,真正的bug没多少,因为
- 有两个人错了\v,总计被捅了大概20刀
- 有一个人没判定空串,被捅了6刀
- 有一个人对正则了解不深爆栈了,被捅了7刀
剩下的,其实也仅有两个功能性bug,还都是可以用对拍器直接扫出来的,都是输出格式的错误
-
100分的房间被捅了6个bug50刀,说明OO的强测更偏向于定档,而不是像计组一样真正的强测
-
-
第二次作业
- 总计被查出了6个bug,有5个是优化的锅,这给我的教训是,想优化要趁早。从周日摸到周二晚上再写优化的后果就是公测互测两爆炸
- 还有一个bug是自己不太该犯的真正的功能缺陷,来源是第一次作业与第二次作业思路不统一,在从手写自动机转到正则时,有了一个很大的疏忽:项与项之间必须要有符号。没有考虑这一点的后果便是,输入x x或者1926 0817都查不出来。过了中测之后没有检查下最基础的,指导书上给出的样例也是我出bug的一个原因
- 互测基本就不用提了,在对拍器的扫射下,一个最高分数不到80的房间是不可能有存活的人的。一个7人房互相伤害,场面十分华丽
-
第三次作业
-
由于前文提到的种种原因,我第三次作业摸了,不过还是出现了比第二次更严重的bug:我的预处理做了一些不该他管的事情,导致少判定了很多WF。不过,第三次作业也出现了一件十分真实的事情
- 我一直认为,强测出弱了,我也不太理解,第三次作业为何禁止提交WF的数据
- 我自己的程序有很多bug,但是“强”测没有测出来,互测能hack我的样例也交不上去
这两个事实同时存在,我竟一时不知道我改对这次作业的一些新制度表示赞同还是反对。
我最后的决定是,对这一次的新制度和比较水的强测表示反对,同时对自己得分能看的过去但是真有许多bug的作业表示不满
-
一些心得与自嗨
写到这里,看了看ppt上给定的几个方面,应该是都写完了,那接下来就是一些无聊的自嗨环节了
第三次作业由于卡在OS第二次Extra上,优化摸了。不过可能的96或97变成95.8824但换来了OS关键的第二次Extra,我还是很满意的。更何况是可能的96或97。事实上,即使第三次作业将性能极限放到3倍,还是起不到任何激励普遍优化的作用。在仅第一名即为Lmin的情况下,做出了堪比第二次作业的优化程度的人,可以1人杀1系。我身边很多做了初步优化的同学,到头来和我一点也没做是同一个分数。虽然表面上看我赚了,但是我并不非常认同这个结果。如果熬夜爆肝能到98+,思考四五个小时能到97+,而不是像我一样95.8824,优化应该会变得更有趣。
第一次作业的优化比较水,想到了的基本人人100分,就不过多说了
第二次作业的优化可以说是优之无果,弃之可惜。在相对明显的三元组优化方案大前提下,在20分优化分的激励下,很多同学,包括我自己还是做出了一定的优化。虽然到现在我也没能想出超越三元组极限的方法,不过三元组内部的优化层次确是很明显。
- 首先是最水的贪心+合并。说白了就是两两合并,合并后变短了就采纳,否则就丢弃,O(n^2^)扫一遍即可。这样的优化如果做对了的话,大概能有97左右的分数
- 其次是高档一些的贪心+合并+分拆。这一层级主要是考虑了形如cos(x)^1^+cos(x)^2^+...+cos(x)^10^+sin(x)^k^这样的结构。如果k比较小,如果cos那边比较全,那么直接把sink那一项变成常数+cos项即可。方法与合并差不多,直接贪心的选择拆或不拆即可。根据结果来看,这样做如果运气好的话,是有可能直接100的?
- 再往下是我只敢想不敢做,但是有dalao做出来的深搜+时间优化。直接暴力搜索合并与分拆应该是三元组范围内的最优解,但是一定T。所以有些dalao做了剪枝优化或者其他我不了解的黑科技优化,将深搜在时间范围内实现了。这一步正确的dalao好像基本都是100分。而像HDL大佬那样的寻求近似解,也是超越了贪心的一种解法。我猜测,只要越过了贪心的极限,第二次作业就能100
- 之后是一些过于高深或者无厘头或者不现实的优化方案。比如欧拉函数优化,我只听其名未见其人,以我的芝士水瓶无法理解。比如数值优化,就是形如1000000000000+sin(x)^m^*cos(x)^n^直接优化掉乘积项,这些优化方案适用范围太窄,也太不好实现。这一阶段的优化方案超越了三元组的极限,但是超越之后是升上天堂还是堕入悬崖就是未知了。
总结
总的来说,第一单元的重点我认为并不是OOP入门,而是测试入门,输入处理入门,鲁棒性接触与实践,优化入门等等内容。这些内容看似与OOP无关,但是当第二单元真正OOP任务来临之际,是没有时间多线程肝这几个内容的。通过第一单元解决了这些“闲杂”问题,或许并不OO,但是对于真正开始OOP的人来说,是必备的,但是之前大概率未接触过的工具。有了鲁棒的输入处理,有了时刻提醒程序保护自己,怀疑对方的思路,有了良好的对拍器来保证正确性,有了优化与实现解耦合的思想,才能在电梯出租车到来之际保护好自己吧
来源:oschina
链接:https://my.oschina.net/u/4271062/blog/3598837