BUAA_OO_2021_第三单元总结博客

第三单元总结博客

一.总结分析自己实现规格所采取的设计策略

  • 最开始的时候,我本着严格按照规格实现的“刻板想法”,每一个函数都是按照规格实现,在规格中并没有给出的容器选择方面我只选择了我最熟悉的Arraylist,所有的步骤都跟规格一模一样。但这样也就出现了一些性能问题。

  • 性能问题主要是CPU时间超出题目限制,在这里我对于自己程序进行了复杂度的相关分析,发现由于我使用了深度优先的算法,加上严格按照规格写了两层for循环,使得我的程序会不断调用复杂的递归函数。于是,找到规格实现上的问题后我就开始修改算法,把原本的双重循环改成了一重,在第二次作业中甚至直接把深度优先算法改成了并查集算法。总结下来,我的设计策略就是:根据规格中给出的描述首先初步实现基本的需求,然后在保证这个需求的情况下,对一些可以使用更加快速的方法的地方进行一些优化,最终实现一个完整、高效的作业。

二.结合课程内容,整理基于JML规格来设计测试的方法和策略

  • 基于JML规格进行测试的方法:主要是使用Junit,但我并没有使用这个方法,而是使用自造数据进行debug,这样debug的好处就是非常直观,对于各种操作可以非常准确地定位,同时使用大量随机数据,可以保证我们的测试是比较充分的。

  • 测试策略:策略主要是采取一个覆盖性测试的策略,对于作业要求中提出的各种指令进行强化的测试。当然在这里,我们还需要对于每一次作业的重点指令进行重点的测试,例如第二次作业中的sendMessage指令和第三次作业的sendIndirectMessage指令,需要我们可以利用一个巨大的关系网络,也就是通过输入大量的ap(add person)指令和ar(add relation)指令构建一个庞大的关系网络,然后来一定规模的sim指令,直接与正确答案进行对拍,就可以精准定位bug了。

三.总结分析容器选择和使用的经验

  • 容器选择:这个单元的JML相关作业对于性能还是有比较高的要求,所以在容器的选择上我们需要好好斟酌。在这里最大的问题就在于数据的规模,在第三次作业中人员的个数可能达到5000,这时候如果使用Arraylist来作为存储人员的容器的话,在一些规格中给出的遍历过程中可能会花费大量的时间,所以在这里最高效的做法是使用Hashmap这个容器,这样在规格中要求的一些原来需要遍历Arraylist来实现的函数,就可以直接使用Hashmap的一些方法,例如containKey和containValue来实现原本使用Arraylist的时候非常复杂的遍历过程。综上,在这个单元的作业中使用Hashmap是一个比较明智的选择。

  • 使用经验:从使用经验来看,Hashmap使用比Arraylist更加方便,例如我们利用message的id作为Key值,这样在查询某一个id对应的message是否存在的时候,就可以直接使用containKey方法查询,用一行代码解决了Arraylist复杂的for循环的过程,而且Hashmap理解起来非常简单,利用之前学到过的数据结构中的哈希函数就可以非常容易地理解,所以使用起来也不会有太高的学习成本(不过也主要是因为我太摸,第一次作业全部用的Arraylist,不过学习了Hashmap以后只能说是真香了)。

四.针对本单元容易出现的性能问题,总结分析原因如果自己作业没有出 现,分析自己的设计为何可以避免

  • 性能问题分析:在本单元作业中出现的最主要问题就是我们严格地按照规格来写,即使使用了Hashmap这个快速的容器,也会由于一些原本可以优化的地方拖慢程序的时间。例如,在Group中就有一个函数是需要我们计算这个group中的所有人的平均年龄和方差,在这里我们完全不用每次调用这个函数的时候再进行一次foreach循环,可以直接在每次进行加人和减人的时候设置一个变量计算年龄和和年龄的方差。但恰恰也就是这些类似地方的一些细节,造成了大部分同学在性能上出现了一些bug。

  • 避免上述性能问题的设计:在这里我们需要注意两个地方,第一个就是容器方面还是需要使用Hashmap,这一点在第二次作业得到了非常重要的体现(为什么没有第三次作业主要是因为到了第三次作业大家几乎都改成了Hashmap,只有一些跟我一样头铁的同学第一次作业使用的就是Arraylist,到了第二次作业仍然不信邪继续使用Arraylist),速度上的差距在sendMessage这个函数中就体现得淋漓尽致:Arraylist需要遍历,Hashmap直接操作,而强测的数据规模远比中测大,这个时间差直接给我带走了……第二个需要注意的地方主要是上一段提到的一些函数中存在的一层甚至双层for循环,可以进行大量的优化,在加人减人的时候做一些操作,这样这些原本时间复杂度可能达到O(n)和O(N^2)的函数瞬间就降为了O(1)

五.梳理自己的作业架构设计,特别是图模型构建与维护策略

  • 历次作业的架构设计(图模型构建与维护):第一次作业中我直接使用了最简单的Arraylist,看成之前数据结构中的邻接矩阵,实现一个图的存储;到了第二次作业和第三次作业,我虽然还是构建了一个邻接矩阵,但实际上主要的算法已经集中在了并查集上;并查集的核心就是将有联系的一系列节点,联系起来,

    public int find(int id) {
    if (id == root.get(id)) {
    return id;
    } else {
    root.put(id, find(root.get(id)));  //父节点设为根节点
    return root.get(id);         //返回父节点
    }
    }

    public void merge(int i, int j) {
    int tmp1 = find(i);
    int tmp2 = find(j);
    if (tmp1 != tmp2) {
    root.put(find(i), find(j));
    }
    }

    图中是并查集的核心思想,主要是利用一个Hashmap记录下各个节点的父节点,这样在判断两个节点是否连通的时候就只需要判断两个节点的父节点相不相同就行,这样就不需要依靠邻接矩阵来进行深度优先的递归了,这样的图维护起来既简单又高效。

  • 在第三次作业中需要我们实现图的最短路径的算法,大部分同学使用的都是迪杰斯克拉算法,这就要求我们还是要有邻接矩阵,所以在第二次作业中加上去的并查集并没有影响第三次作业的迭代开发,相反,完美地完成了各自的任务。

六.心得体会

  • 这一单元最大的体会就是:不能小看规格。此话怎讲?一方面,我们的实现必须严格完成规格的要求,另一方面,我们可以在规格的描述上,做出一些我们的优化,而不是一味地对规格进行简单的“翻译”,这样才能兼顾正确性和程序的效率。

  • 规格是之后我们可能面临的工程开发的工作中经常可能遇到的朋友,我们必须学会如何按照规格来实现需求并进一步实现优化。

  •  

posted @ 2021-05-30 22:38  ChrisLeff  阅读(45)  评论(1编辑  收藏  举报