BUAA OO 第三单元博客

前言

在本单元的学习中,由于我之前对算法接触较少,而且对大一时学到的一些数据结构遗忘较多,再加之没有上过 oo 先导课,对 java 的一些基础语法掌握不牢固,导致这几周以来作业写得较为痛苦,总体而言我这一单元的成绩并不好,而且也花费了较多的时间,但也有一点毫无疑问,就是我也学到了很多东西,收获匪浅。

一、分析测试过程

1、黑箱与白箱测试

黑箱测试:

顾名思义,即只关注输入和输出,而不考虑内部的逻辑架构,通过大量的输入检测程序的输出是否正确。

比如我们的测评机测试以及 OKTest,采用的就是黑箱测试。黑箱测试往往需要大量的输入数据,要考虑各种各样的情况,从而对程序进行全方位的测试,这样的测试优缺点都很明显。优点就是测试者根本不需要关心被测试方程序的写法,可以对所有人的程序进行统一的测试,而缺点则是无法定位到 bug 位置,而且测试覆盖不一定全面,如果想要尽可能覆盖所有情况,需要测试者尽可能地扩大测试数据的范围,这项工作一般较为困难,需要花费大量的时间和精力,也需要一定的经验。在我们个人测试自己的程序时,最好采用黑箱 + 白箱的方式。

白箱测试:

与黑箱相对,即关注程序本身,从程序设计的角度进行测试。

白盒测试其实考虑的是测试用例对程序内部逻辑的覆盖程度,最彻底的白盒测试就是覆盖程序中的每一条逻辑路径。白盒测试是高效的测试,不仅可以发现问题,还可以定位问题和解决问题,但是也有缺点,比如对规模较大的代码测试会十分困难,而且无法对所有人的程序采用统一的测试方法。

2、测试手段

单元测试:

即将程序分为几个相互独立的模块,对每个模块进行测试,例如对某个类的测试。

功能测试:

即黑箱测试,近测试程序的功能。

集成测试:

单元测试保证每个模块可以正常运行,集成测试就是将各个模块放在一起进行整体的测试。

压力测试:

在极限的压力情况下观察软件或程序的运行情况,从而测试其在极限情况下的性能,针对性地找出影响系统性能的瓶颈。

回归测试:

即迭代之后,需要保证原有功能依旧正常。

3、测试工具

仅使用了对拍器和同学进行对拍,即跑完相同的输入后,检测输出是否一致。

4、数据构造策略

1、对所有指令先进行正确性测试。

2、针对特殊指令进行压力测试,如 qcs、qlm、mr、qci 等,主要采用大数据量的方式。

3、针对 OKTest 进行白箱测试。

二、架构设计

对比官方的代码,基本上三次作业在整体的架构上都并没有做很大的修改。

第九次作业:

在这里插入图片描述

最开始我只是采用了暴力方式遍历整个 people,后来我采用了最基本的并查集算法,但是没有做其他优化。

测试中性能的重点是 qbs 测试,我在 addRelation 以及 addPerson 的过程中维护了 blockSum,在调用 qbs 指令时,直接返回 blockSum 即可。除了 blockSum,我还维护了 tripleSum,并使用了 HashMap 辅助 tripleSum 的维护,在维护的过程中并不耗费时间复杂度。

第十次作业:

在这里插入图片描述

此次作业的难点在于 modifyRelation,众所周知,并查集从数据结构的根本上就不支持删边操作。

所以在每一次的 mr 过程中,如果需要删边,我就会直接重构整个并查集,在 addRelation 的过程中就记录所有的 relation,当需要重新构图时,将所有人的 parent 重新设置为自己,再对所有的 relation 进行重新添加。

对于 blockSum 和 tripleSum,都可以在重新构图的过程中进行维护。除此之外,我还额外维护了 bestAcquaintances 哈希表,key 为 person,value 为该 person 的 bestAcquaintance,若不存在则为 null,维护该哈希表可以在 addRelation 以及 modifyRelation 的过程中同步进行,虽然给 addRelation 和 modifyRelation 增加了负担,但是可以大幅度降低 queryCoupleSum 以及 queryBestAcquaintance 的时间复杂度

第十一次作业:

在这里插入图片描述

本次作业又新增了很多内容,但大部分内容只要可以读懂规格就并不难,真正的难点在于 qlm 指令,该指令需要返回以编号为 id 的 person 为起始点的最小环的权值。

我采用了遍历该 person 的 acquaintance,对每个熟人先假设删边,再找最短路径的方法,寻找最短路径的过程中使用了迪杰斯特拉算法,具体过程如下:

1、将当前 person 设置为 srcId 对应的 person,并将该 person 的 acquaintance 加入寻找最短路径队列中,将权值设置为 person 与此人的 value,然后将当前 person 加入已找到最短路径队列中,并更新 person 的权值为 0,即初始化操作。

2、遍历当前 person 的 acquaintance,如果此人没有被标记找到最短路径,则将当前 person 的权值与此人和当前 person 之间的 value 相加,如果此人未在寻找最短路径队列中,或者计算所得权值小于此人在队列中记录的权值,就将此人重新加入寻找最短路径队列并更新权值。

3、遍历完成后,将当前 peron 重新设置为寻找最短队列中权值最小的人,然后标记此人为已找到最短路径,权值即为此人在寻找最短路径队列中时的权值,然后返回步骤 2 ,直到当前 person 为 null 或者当前 person 是 dstPerson。

4、若已找到最短路径队列中有 dstPerson,就返回 dstPerson 的权值,否则返回 -1,表示 srcPerson 和 dstPerson 之间不存在路径。

三、性能问题及其修复情况,对规格与实现分离的理解

这三次作业中均出现了性能问题:

第一次作业中是我在 qts 中使用了三重循环,在 isCircle 中使用了递归,后来我采用并查集并维护 blockSum、tripleSum 的方式修复了 bug。

第二次作业中我在 qcs 中也是直接采用了遍历的方法,导致了 ctle,后来我维护了 bestAcquaintances 哈希表,解决了此性能问题。

第三次作业中强测第四个点 qlm 压力测试未能通过,因为算法能力不足,没有进行修复(悲)。

规格和实现分离:

jml 对规格只描述算法结束时数据结构的状态,但不关心算法的过程(黑箱思想),所以规格和算法本身的逻辑是分离开的。要写出高性能的算法,就需要先从自然语言的角度去理解规格的描述,知道究竟要做什么,再考虑具体使用什么样的数据结构以及算法,在完成后再进行算法性能的测试以及 OKTest(对规格的测试)。

四、OK测试方法

在 OKTest 中,我们可以很严谨地针对规格编写出测试程序,同时 OKTest 的返回值可以告诉我们具体是哪一条规格没有被满足,有助于我们定位 bug,改进算法。

同时我也有一点建议,我认为 OKTest 可以作为静态方法或者放在一个专门的 Test 类中,而不是放在 Network 里,一方面 OKTest 实在是太长,另一方面是这种方式不太符合面向对象的思想。

学习体会

由于我对算法的了解和训练不多,而且这一单元涉及算法的难度较大,对算法性能的要求又很高,导致我这一单元的成绩十分惨淡,当然其中也有一些粗心的错误,例如对 jml 阅读不仔细,在编写 OKTest 时出错等等。

但从另一方面而言,我的收获也很大,一是了解了规格设计的方式,加深了我对规格的认识,二是训练了关于图论的算法,加深了我对并查集、最短路径等算法的理解,提高了我解决现实中常见问题的能力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值