2021-OO-第三单元总结

OO-第三单元总结

一、作业概况分析

面向对象构造与设计第三单元要求我们根据指导书中给出的JML规格实现相应的功能,保证程序设计的正确性。我们根据JML的要求,来对给定限制范围输入变量进行处理,并且保证输出符合相应的规则,同时对异常进行处理,模拟了工程标准下的代码编写任务。由于大部分代码由官方包给定,本次作业相较于前几单元更加简单,只要细心就可以拿到较高分数,可以更有时间去研究性能上的提升。

二、设计策略

完成本单元的作业的时候,我的设计策略主要可以分为三个阶段。

  • 单纯翻译:既然规格已经给定,那么就严格按照规格办事

    • 容器使用数组

    • 全部功能使用循环遍历

    • 优点:正确性无可置疑

    • 缺点:性能表现极差

  • 优化

    • 对JML进行等价转换(翻译)

      • 第一次作业

        • 两个id对应的人之间是否可以通过isLinked连通 = isCircle

        • 图中是否连通块的数量 = queryBlockSum

    • 对算法和数据结构进行优化

      • 第一次作业

        • 容器的实现

          • 使用ArrayList等顺序结构存储对象(一定超时)

          • 使用HashMap等索引结构存储对象

        • ​isCircle

          • 深度优先搜索 (可能超时)

          • 动态构建并查集

        • ​queryBlcokSum

          • 根据循环查找(可能超时)

          • 直接返回并查集父节点个数

  • 由于篇幅所限,仅以第一次作业为代表举例,第二次第三次作业也是同样的设计策略,目标就是在完美符合规格的同时尽可能缩小时间复杂度,寻找等价条件,翻译明白了然后找合适算法和容器。

三、基于JML规格测试方法及策略

测试在本单元的作业中尤为重要,课堂上,老师甚至提到在工业界中,编写测试样例是要优先于编写代码本身的。

在本单元作业中,我的测试策略大概也可以分为三个阶段。

  • 基本功能测试

    • 基本思想:根据每条指令的基本功能,进行最基本的功能测试。

    • 主要以样例以及简单的自造样例为主,长度不太长,主要测试异常是否能正常响应、容器操作是否成功等基本问题。

  • 极限性能测试

    • 基本思想:针对每单元的高复杂度部分,进行有针对性的测试。

    • 如作业二中的:限定10000条指令,添加指令和等量指令,条ag指令,剩下的全为qgvs指令,根据相应时间复杂度列二次方程求解大概的极值,生成样例进行测试。

  • 乱序随机测试

    • 基本思想:通过评测机不同指令顺序的组合,发现静态分析所难以察觉的功能问题。

    • 花费时间长,发现bug多,但需要评测机基础、靠谱的.jar包以及合理的指令生成策略,缺一不可。

  • 正确性备注:与同学互换.jar包进行黑盒对拍测试。

四、浅谈容器选择和使用

Java8为我们提供了种类繁多的容器,我们可以在Java容器等看到它们的用法。其中可以大致分为列表结构List、集合结构Set、索引结构Map三大类。

List 进行随机存取和顺序存取很快,但在插入或删除时开销较大。(LinkedList)除外。常用的为ArrayList,LinkedList

Set 主要特征是根据元素的hashcode判断元素是否是同一个,一个Set中只能容纳一个元素,而且其中元素具有无序性。分别对应数学中集合的互异性和无序性。用来解决“存在与否”的问题效率较高。常用的为快速增删索引的HashSet以及自动排序的TreeSet。

Map 是一种可以通过key映射value的一种索引数据结构,常用的有HashMap、TreeMap和多线程中的ConcurrentHashMap等。

在C中我们还经常用一种叫最大堆的数据结构,在Java中,它的官方实现为PriorityQueue,可以用来实现特定的算法。

对于Person类、NetWork类中大多需要实现查找的person[] 和 accquaintance[] ,为了追求较高的索引速度,我会选择HashMap作为具体数据结构(O(1)的查找复杂度远低于遍历查找的O(n),而且遍历复杂度相同,删除也是O(1)复杂度)

对于特殊的Person类中的Message[]而言,考虑到JML规格中要求每次插入头部,且数据结构中原数据仍具有固定的相对顺序,我选择使用LinkedList,降低插入的开销。

对于并查集的父节点,我使用HashSet进行存储,这样,在queryBlockSum时便只用返回HashSet.size()了,极大降低了时间复杂度。

对于第三次作业中djistra算法的优化,我采用了经典的堆优化方式,使用PriorityQueue进行了算法的优化,提高了程序的性能。

总而言之,根据数据结构的特性和自己的需求合适地挑选容器的提,是我们熟悉了解这些容器本身和实现原理,在这之后,才能如鱼得水的使用它们。

五、本单元性能问题分析

  • 性能问题概况

    作业中比较明显的性能问题就是双循环问题,凡是看到有O(n^2)的复杂度,必然需要我们优化。

    • 第一次作业中,正值五一假期,虽然我发现了不用并查集可能会tle,但是人在飞机已经来不及,回来后t了两个点,及时使用了并查集进行修改。

    • 第二、三次作业,我经过多方学习与讨论,及时使用优化方法解决了JML中高复杂度的部分。

  • 性能问题分析

    • 所有的作业中,出现性能问题的部分都是简单粗暴的双循环导致的。

    • 解决方案

      • 前两次作业都可以采用记忆化的方式,避免重复计算,分别采用并查集和在线计算的方式,避免了每次查询都进行O(n^2)复杂度的计算。

      • 第三次作业则采用优化版的迪杰斯特拉算法,将程序复杂度由O(n^2)降至O(mlogm)。

六、架构设计

由于第一二次作业的架构包含在第三次中,而且第三次作业有关图的架构最为完整,此处以第三次作业的架构为例分析架构。

  • 图模型构建与维护

    • 如果把整个关系网络看作一个图,那么每个节点——Person中的accquaintance[]便是该节点的邻接表,我们可以通过addPerson addRelation、deletePerson等方法进行图的维护和构建。

  • 并查集

    •  

       

  • 在线计算

    •  

       

  • djistra

    • 是市面上常见的djistra啦

七、心得体会

第三单元的OO作业较前几次作业来说,难度有所下降,但是要求我们掌握更多工程化的方法,使我们编写的代码更加规范化。此外,本单元中还出现了选择合适数据结构和算法的问题,也启示我们算法和数据结构是我们写码之路中不可丢失的法宝。

 

 

posted @ 2021-05-31 10:50  WuhuAirforce  阅读(108)  评论(0)    收藏  举报