oo_第三单元博客

引言

本单元主要的学习内容为JML规格语言,通过阅读JML规格来实现一个社交网络及其相应的功能。本次作业相较于前两次作业较为简单,难度主要在于读懂JML规格和实现性能的优化。

正文

一、实现规格所采取的设计策略

在实现前我一般会先阅读指导书上的简要介绍,优先实现异常类。实现异常类之后由简到难实现各个类,三次均在最后实现network类。

在具体到类的实现时,我会先大致阅读一遍这个类各个方法的规格,确定成员变量所用的容器,然后按顺序逐步实现各个方法。

在实现单个方法时,首先确定normal_behaviorexceptional_behavior各自的requires,然后根据其规定弄懂其功能和最终需要的\result,选择已知性能最佳的方式进行实现。

二、基于JML规格的测试方法和策略

采用Junit 单元测试来对代码正确性进行测试。方法如下:

  1. 根据JML的前置条件设置对象的状态
  2. 调用方法并检查后置条件是否满足
  3. 测试数据量较小的边界情况和所有逻辑分支

测试过程中可把方法分为三类分别进行。

  • 具有分支的方法

    对JML所规定的所有分支进行测试,使用Assert判断分支的运行结果与预期结果是否一致。

  • 抛出异常的方法

    除了测试普通的行为之外,还要使用@Test(expected=Exception.class)注解对抛出异常的功能进行测试。

  • 维护中间变量的方法

    需要对所有影响该中间变量的方法进行针对性的测试。

三、容器选择和使用的经验

  • 有对应关系的量,且无顺序需求,如personid之间,idvalue之间,一般采用HashMap,方便查找和算法设计,降低查找时的时间复杂度。
  • 没有对应关系的量,或者不需要通过一个属性寻找另一个对应属性的量,特别是有顺序规定的,一般采用ArrayList,方便根据标号寻找对象和顺序遍历。

四、本单元易出现的性能问题

第一单元作业

本单元易出现性能问题的其实就是queryBlockSum函数对应的qbs指令,很多同学用dfsbfs来实现isCircle方法,复杂度已经为\(O(n^2)\),再用isCircle方法实现queryBlockSum方法,很容易导致CPU超时。

我在这里采用了并查集的方式实现,通过findjoin两个方法构建关系网络和实现路径压缩,维护一个qbs变量来得到块的个数,通过判断两人是否有同一个根节点来判断isCircle,降低了复杂度,最终没有出现性能问题。

第二单元作业

本单元易出现性能问题的方法为getAgeVarqueryValueSum

  • getAgeVar

    很多同学使用了双重循环来实现这一函数,使得复杂度为\(O(n^2)\),最终导致超时。我在这里使用了\(Var(x)=E(x^2)-E(x)^2\)这一公式,在addPersondelPerson时维护\(E(x^2)\)\(E(x)^2\)两个中间变量,将时间复杂度降为了\(O(1)\),从而解决了这一问题。

  • queryGroupValueSum

    通过设置一个valueSum中间变量进行维护来解决性能问题。在addPersondelPerson以及addRelation三个方法分别对中间变量来进行对应的维护工作。

第三单元作业

本单元易出现性能问题的方法为sendIndirectMessage方法,这个方法要实现最短路径的搜索,一般采用普通的dijkstra算法复杂度为\(O(n^2)\)可能会导致超时。

我在这里采用了堆优化的dijkstra算法,使用java的priorityQueue进行操作,降低了时间复杂度,没有出现性能问题。

五、作业架构设计

整体的架构均严格按照JML规格进行实现。

  • MyPerson类相当于图的结点,负责储存Person的一系列信息和修改工作,同时设有acquaintance列表储存与点有关联的其他结点,相当于邻接表。
  • MyGroup类负责构建小组,储存组的信息和组的维护工作。
  • MyNetwork类是整个作业的核心,负责构建和维护整个图模型和传递信息,其中addPerson负责添加点,addRelation负责添加边,addToGroup负责将点进行分组。
  • Message相关类负责各种信息的构建。
  • 各种异常类负责构建异常。
posted @ 2021-05-30 17:03  禾草  Views(64)  Comments(1Edit  收藏  举报