2020北航OO第三单元总结

本单元要求是根据JML规格完善代码,初看是一个简单的代码照搬实现的东西,但最后才发现由于CPU时间的限制,还考察了大量优化策略及数据结构中关于图的知识,是一次非常注重细节构思的一单元,我借此机会学习并巩固了好几个图的算法,并了解了java各类容器的查插删改的效率。

一、JML理论基础

JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言,它相当于一种接口,正确的JML可以给实现人员清楚的代码要求,并且针对已有代码书写JML也可以提高代码的可维护性、可读性。经我使用看来,它有如下几种关键要素组成:

  1. 位于类一开始的//@annotation的属性定义,它规定了类必含的属性(当然也可以由其它形式所展现),在之后的方法规格中会用到这些定义

  2. 变式invariant,要求在所有可见状态(凡是会修改成员变量(包括静态成员变量和非静态成员变量)的方法执行期间,对象的状态都不是可见状态)下都必须满足的特性

  3. 状态变化约束constraint,对前序可见状态和当前可见状态的关系进行约束

  4. pure关键词,表示某些纯粹访问性的方法,即不会对对象的状态进行任何改变,也不需要提供输入参数,这样的方法无需描述前置条件,也不会有任何副作用,且执行一定会正常结束

  5. 前置条件(pre-condition),用requires关键词表示,定义调用者调用方法前必须满足的语句

  6. 后置条件(post-condition),用ensures关键词来表示,定义调用者调用方法后必须满足的语句

  7. 副作用约束子句,使用关键词assignable或者modifiable,表示调用方法后可以改变的变量

  8. 异常处理行为exceptional_behavior,用signals (Exception e) expr表示满足expr条件即抛出Exception异常,用signals only Exception表示满足前置条件即抛出Exception异常

其中,在规格表示时,大多数语句含义与代码类似,只需注意一下几个关键词的意含义:

  1. \nothing,指空集;everything,指全集,通常用在assinable语句

  2. \result,表方法示返回结果

  3. \old(expr)表达式,表示一个表达式expr在相应方法执行前的取值

  4. 推理操作符:expr1==>expr2,即离散中的→

  5. \forall表达式:全称量词修饰的表达式

  6. \exists表达式:存在量词修饰的表达式

  7. \sum表达式:返回给定范围内的表达式的和

二、JML应用工具链

  1. OpenJML:类似于对代码的检查,可以对规格进行语法的检查

  2. JMLUnitNG/JMLUnit: 针对类自动生成测试样例并进行测试

  3. JMLdoc : 类似 Javadoc,可以快速生成 JML 文档的相关文件

三、SMT Solver

下载了openjml,想要运行结果出现了一堆警告和error,类似于下面这种问题,\result都会被报错,但自己写的Demo里只有很简单的一个add方法,完全摸不着头脑,而且openjml也不能检测forall、exsit等语句,只能放弃

public class Demo {
   //@ ensures \result = a + b
   int add(int a, int b) {
       return a + b;
  }
}

四、JMLUnitNG

经过一些奇奇怪怪的操作,我终于配置成功了jmluintng

我先对Person.java进行了测试,主要是先补齐new一个新对象时<>里的变量类型,并加上包名,其它内容并没有改动,可以看出它产生的测试用例都是比较边缘的情况,如0、null、MAX_VALUE等,最后只贴出来出现fail的部分,集中在isLinked函数,原因是输入为null的情况,这应该是不会在使用中出现的一种情况

之后我又进一步对Group.java进行了测试,结果如下

最后失败的仍是null的情况。。。

我感觉到,这其实是一种很好地检验方法正确性工具,尤其体现在它对边缘情况进行测试的方面,但可能不是很适合比较复杂的情况(如我们的图算法)

五、架构设计

先放UML图

看起来有点点复杂,但我在原有架构的基础上,其实只增加了两个类EdgeBcc,一个代表图中的边,一个代表一个双连通块,通过增加这两个类,可以进行更好的双连通分量与最短路径的查询。至于优化的问题,由于都是在TLE之后才意识到需要进行优化,所以放在下部分讲

六、bug及其修复

这次作业的bug主要出现在算法的问题上,由于一开始都是机械地按照规格重现代码,所以导致很多点TLE了,最后bug修复完的算法如下:

  1. 采用hashmap进行存储查找,arraylist太慢,并对hashmap进行适当的初始化容量操作(最后的TLE就死在这上面)

  2. getValueSumgetRelationSum使用缓存机制,提前设置变量,在addRelation和addToGroup时进行即使修改

  3. blockSum进行缓存,并对每个person进行block的标号,在addPerson时使blockSum++,在addRelation时合并block,并改变blockSum(这里在优化过程中出现了一个bug,是因为没有区分好block的标号及blockSum,使得后加进来的人也会被打上之前block的标号)

  4. 通过每个id的block号,可直接判断isCircle

  5. queryMinPath方法使用堆优化的Dijikstra,对于堆优化,采用了java有序队列PriorityQueue来存储,使Edge实现了Comparable的接口,并且每次添加的时候不用进行是否已经访问过的查询操作,因为他永远都是poll出最小值。这里也出过一个小bug,是由于我将每个id初始的路径值设为了1001(value[0,1000]),但忽视了value是一个累加的过程。。最后改为Integer.MAX_VALUE,也是由于自己测试不充分的缘故,这其实只需要将每条边的value值设大一点就可以了

  6. 由于会有大量连续测最短路径的操作,我每查询一次就将查询结果以<id, values>的形式储存起来,如果有addRelation的改动就将相关节点删去,这样也可以减少大量重复的dijisktra查询操作(个人以为qmp是这次的优化重点)

  7. queryStrongLinkedTarjan算法,这个就不多说了,因为大家应该都用了

最后MyNetWork的各个方法复杂度分析如下

MyNetwork.addGroup(Group) 2.0 1.0 2.0
MyNetwork.addPerson(Person) 2.0 1.0 2.0
MyNetwork.addRelation(int,int,int) 4.0 6.0 9.0
MyNetwork.addtoGroup(int,int) 4.0 2.0 5.0
MyNetwork.borrowFrom(int,int,int) 3.0 2.0 4.0
MyNetwork.compareAge(int,int) 2.0 2.0 3.0
MyNetwork.compareName(int,int) 2.0 2.0 3.0
MyNetwork.contains(int) 1.0 1.0 1.0
MyNetwork.delFromGroup(int,int) 4.0 1.0 4.0
MyNetwork.getGroup(int) 1.0 1.0 1.0
MyNetwork.getPerson(int) 2.0 1.0 2.0
MyNetwork.isCircle(int,int) 2.0 2.0 3.0
MyNetwork.MyNetwork() 1.0 1.0 1.0
MyNetwork.queryAcquaintanceSum(int) 2.0 1.0 2.0
MyNetwork.queryAgeSum(int,int) 1.0 3.0 4.0
MyNetwork.queryBlockSum() 1.0 1.0 1.0
MyNetwork.queryConflict(int,int) 2.0 2.0 3.0
MyNetwork.queryGroupAgeMean(int) 2.0 1.0 2.0
MyNetwork.queryGroupAgeVar(int) 2.0 1.0 2.0
MyNetwork.queryGroupConflictSum(int) 2.0 1.0 2.0
MyNetwork.queryGroupPeopleSum(int) 2.0 1.0 2.0
MyNetwork.queryGroupRelationSum(int) 2.0 1.0 2.0
MyNetwork.queryGroupSum() 1.0 1.0 1.0
MyNetwork.queryGroupValueSum(int) 2.0 1.0 2.0
MyNetwork.queryMinPath(int,int) 10.0 9.0 14.0
MyNetwork.queryMoney(int) 2.0 1.0 2.0
MyNetwork.queryNameRank(int) 2.0 2.0 4.0
MyNetwork.queryPeopleSum() 1.0 1.0 1.0
MyNetwork.queryStrongLinked(int,int) 5.0 7.0 10.0
MyNetwork.queryValue(int,int) 3.0 2.0 4.0
MyNetwork.renew(int,int) 2.0 7.0 7.0
MyNetwork.tarjan(int,int) 6.0 7.0 8.0

也可以看出来是qmp和qsl方法复杂度比较高,也是只有这两个方法含有图的反复查询操作

七、心得体会

坦白地讲,在做第一次作业时,我用了oo有史以来的最短时间,而且个人感觉没什么需要测试的地方,不由感叹oo作业变简单了,在进行互测时,也是第一次认真看完了所有人的代码,并找出了他们的漏洞,感觉第一次作业还是比较注重规格的。

第二次作业,虽然形式与第一次相同,但突然讲究的多了起来,也开始对时间和算法有要求了,但由于当周的os作业太难搞,我还是放松了对oo的优化及测试,导致我出了一个很大的bug没有进互测!!!真的没有想到中测突然变得如此简单。。我是大大的失策了,再加上双重循环的遍历,挂掉了所有强测。

第三次作业,我吸收了前两次的教训,自己对算法和测试都比较认真地规划了,而且我也能感受到第三次作业十分看重算法,难度甚至大过了数据结构,这次的结果还算比较正常,也由于优化程度不够导致TLE了一些点,而且互测的大佬们也查出了我没注意的很小的点,经过最艰难的一次bug修复,我终于完成了这一单元的代码部分。

真的感触颇多,从这一次作业,我充分认识到了JML规格的实现不是纯”实现”的问题,要进行自己的优化, 使程序效率达到最好,想想这其实是合理的,规格的书写者只负责想好方法的效果,保证规格正确性即可,不可能连我们怎么实现都考虑到了。最后,再赞一句助教这次强测的点简直太妙了,就卡那一点点时间,我是挣扎着才由2.0XX秒到了1.9X秒,真是一次颇为“愉悦”的经历。。

2020北航OO第三单元总结的更多相关文章

  1. 2020北航OO第四单元总结

    2020北航OO第四单元总结 一.本单元架构设计 本单元作业是实现一个UML图解析器,其中实现接口及主要框架课程组已经提供,只需要我们完成特定功能. 在第一次作业时,感到十分迷茫,不知道如何下手,最后 ...

  2. 2019年北航OO第三单元(JML规格任务)总结

    一.JML简介 1.1 JML与契约式设计 说起JML,就不得不提到契约式设计(Design by Contract).这种设计模式的始祖是1986年的Eiffel语言.它是一种限定了软件中每个元素所 ...

  3. 2019北航OO第三单元作业总结

    1.梳理JML语言的理论基础.应用工具链情况 JML基础理论: JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言.JML是一种行为接口规格语言,基 ...

  4. 北航OO第三单元作业总结(3.1~3.3)

    JML简介及相关工具链使用 1.JML规格描述语言介绍 本单元学习的内容是JML规格描述语言.我们知道,面向对象方法是一个抽象过程,需求者仅需关注方法的规格.规格是对一个方法/类/程序的外部可感知行为 ...

  5. 北航OO第三单元总结

    JML基础梳理及工具链 JML的全称是Java Modeling language,即Java建模语言.JML是一种行为接口规格.它为严格的程序设计提供了一套行之有效的方法.通过JML不仅可以基于规格 ...

  6. 2020北航OO第二单元总结

    2020北航OO第二单元总结 前言 本单元考察基于多线程的电梯调度问题,成功让我从一个多线程小白到了基本掌握了使用锁来控制线程安全的能力,收获颇多(充分体验了迷茫地de一个又一个死锁bug的痛苦). ...

  7. 2020 OO 第三单元总结 JML语言

    title: 2020 OO 第三单元总结 date: 2020-05-21 10:10:06 tags: OO categories: 学习 第三单元终于结束了,这是我目前为止最惨的一单元,第十次作 ...

  8. 2019年北航OO第1单元(表达式求导)总结

    2019年北航OO第1单元(表达式求导)总结 1 基于度量的程序结构分析 量化指标及分析 以下是三次作业的量化指标统计: 关于图中指标在这里简要介绍一下: ev(G):基本复杂度,用来衡量程序非结构化 ...

  9. 北航OO第四单元——UML图解析

    北航OO第四单元--UML图解析 作业要求简析 刚接触本次作业可能需要花上一会才能搞清楚到底是要我们写个啥,在这里简单说一下: UML图的保存格式.mdj文件是以json文件的形式存储的,将每一个Um ...

随机推荐

  1. 生成pdf phantomjs

    注:原创文件,转载请注明出处 使用phantomjs生成还原度比较高的pdf文件,理论上生成word也可以,因需求没有做这块要求,功课留给大家去做了. 下载 https://phantomjs.org ...

  2. SpringBoot注解集合

    使用注解的优势: 1.采用纯java代码,不在需要配置繁杂的xml文件 2.在配置中也可享受面向对象带来的好处 3.减少复杂配置文件的同时亦能享受到springIoC容器提供的功能 @SpringBo ...

  3. 如果要是把标记为2的那一行Lable1.Text改为其他的Lable显示执行代码

    转: 如果要是把标记为2的那一行Lable1.Text改为其他的Lable显示执行代码 如图,程序很简单,文件路径也没问题,为什么会报错,百思不得其解?[url]https://book.douban ...

  4. HDR(高动态范围)

    一: 简介 一般来说,当存储在帧缓冲(Framebuffer)中时,亮度和颜色的值是默认被限制在0.0到1.0之间的. 但是如果我们遇上了一个特定的区域,其中有多个亮光源使这些数值总和超过了1.0,又 ...

  5. 手把手教你集成华为机器学习服务(ML Kit)人脸检测功能

    当给自己拍一张美美的自拍照时,却发现照片中自己的脸不够瘦.眼睛不够大.表情不够丰富可爱-如果此时能够一键美颜瘦脸并且添加可爱的贴纸的话,是不是很棒? 当家里的小孩观看iPad屏幕时间过长或者眼睛离屏幕 ...

  6. allure报告详解+jenkins配置

    今天的博客分为两部分 1.allure报告实战 2.allure结合jenkins 一.allure 1.allure安装 a.下载路径 https://repo.maven.apache.org/m ...

  7. Kotlin/Java Base64编码和解码(图片、文件)

    原文: Kotlin/Java Base64编码和解码(图片.文件) | Stars-One的杂货小窝 最近在项目中使用到了Base64编码和解码,便是稍微写篇文章记录一下 PS:本文代码都是使用Ko ...

  8. dex、apk完整性校验

    对Dex进行完整性的检查,可通过CRC,或者Hash值.可将校验值放到String资源文件里,或者放到服务器中. 在代码中完成校验值对比逻辑,此部分代码后续不能再改变,否则CRC值会发生变化: 从生成 ...

  9. HDU_3949 XOR 【线性基】

    一.题目 XOR 二.分析 给定$N$个数,问它的任意子集异或产生的数进行排列,求第K小的数. 构造出线性基$B$后,如果$|B| < N$,那么代表N个数中有一个数是可以由线性基中的其他数异或 ...

  10. css实现一个电影卡片

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...