第三单元总结

第三单元总结

一、测试数据构造

1、随机数据测试

  使用程序随机生成数据。在一组数据的头部先添加一些诸如ap、am、ag等添加基本元素的指令,之后再按照设定的比例随机生成各个指令。生成好数据之后我选择的是和多个人的程序对拍来验证正确性。自动化评测能够找到比较明显的bug,并通过调高时间复杂度高的指令的比例来验证CPU时间是否会超过限制。

2、手工构造数据测试

  在随机轰炸之后,基本可以保证正确性和性能。这时可以手工构造一些数据针对某一指令做功能或性能上的测试(在互测环节也可以使用)。例如针对ag,可以向group加入超过JML限制的人数;对于高复杂度的指令,随机生成的数据有可能不够强,在这一环节可以手工构造比较复杂的图,并在指令中安排多次查询来检测是否会超时。

 

二、架构设计

  本次作业需要实现的类已经给出,在这个基础上根据自己的需求可以新增类;方法只需要阅读JML了解需求并实现即可。难点在于每次作业中图的实现。

  第九次作业

  本次作业中的qci指令要求查询两个点之间是否存在一条简单路径。

  本来打算采用dfs的方法实现,后来看了助教的提示,使用了并查集的方法来实现,有效降低了时间复杂度。其中并查集的节点为Perosn,两个点之间的路径为Person的Relation。

  并查集是一片森林,每一颗树里面的节点都是互相联通的,不在一棵树的节点必定不连通。因此要判断两个点是否联通,只需要判断两个节点的根节点是否相同。在本次作业中,我选择用HashMap来存储关系,value所对应的id是key值对应id的父节点。在加入person时,便在HashMap中加入一个key和value均为personId的元素;加入relation时只需将两棵树合并即可。这样便可以对并查集进行维护。

  第十次作业

  本次作业中需要完成的图论部分是最小生成树,我选用了Kruskal算法实现。首先取出id所在的连通块,初始时这个联通块的每个点都是一个划分,每次都取出value最小的关系,并将两个划分合并,直到连成一个划分。在这个过程中可以使用之前实现的并查集来实现。在加入person和relation时需要维护连通块的内容。

  第十一次作业

  本次作业需要实现最短路径,我使用Dijkstra算法:就是不断寻找和已知点集距离最小的点,那么这个点的离初始点之间的距离就可以被确定;重复该过程直到目标点与初始点之间的最短距离被找到。

 

三、性能问题和修复情况

  开始时我仅仅对图部分的性能关注的比较多,但是还有另外一些地方会出现性能问题。在这几次作业中,有几个指令可以通过维护一个变量实现,而如果单纯“翻译”JML,容易在互测中被卡超时。例如qbs不通过一个变量储存的话,时间复杂度是O(n^2),改为变量存储则为O(1)(虽然需要一些步骤维护,但是总体复杂度小于前者),时间复杂度大大降低。这样的问题出现了多次,也就不再赘述。

  在最后一次作业中,强测中的有个点出现了CTLE,查看了迪杰斯特拉算法的实现,发现没有什么问题。之后使用IDEA中的分析工具发现ArrayList的contains方法耗时很多,就想到改为HashMap/HashSet,查询速度快了很多。

 

四、扩展与JML规格

  增加Advertiser、Producer、Customer三个继承自Person的类;PurchaseMessage 继承自Message;还有一些异常类。

  Network增加int[] advertise,其中存放一个广告对应的产品id;int[] advertiserId,存放advertise对应的advertiser;int[] sale,存放对应的销售额。

发送广告

  /*@ public normal_behavior
   @ requires contains(advertiserId) && (getPerson(advertiserId) instanceof Advertiser)
   @ assignable advertise, advertiser;
   @ ensures advertise.length == \old(advertise.length) + 1;
   @ ensures advertiser.length == \old(advertiser.length) + 1;
   @ ensures sale.length == \old(sale.length) + 1;
   @ ensures  (\forall int i; 0 <= i && i < \old(advertise.length);
           (\exists int j; 0 <= j && j < advertise.length; advertise[j] == (\old(advertise[i]))));
   @ ensures  (\forall int i; 0 <= i && i < \old(advertiser.length);
           (\exists int j; 0 <= j && j < advertiser.length; advertiser[j] == (\old(advertiser[i]))));
   @ ensures  (\forall int i; 0 <= i && i < \old(sale.length);
           (\exists int j; 0 <= j && j < sale.length; sale[j] == (\old(sale[i]))));
   @ ensures (\exists int i; 0 <= i && i < advertise.length; advertise[i] == productId; advertiser[i] == advertiserId; sale[i] == 0);
   @ also
   @ public exceptional_behavior
   @ signals (PersonIdNotFoundException e) !contains(advertiserId);
   @ signals (PersonIsNotAdvertiserException e) !(getPerson(advertiserId) instanceof Advertiser)
   @*/
  public void addAdvertise(int productId, int advertiserId) throws PersonIdNotFoundException, PersonIsNotAdvertiserException;

添加购买产品的消息

  /*@ public normal_behavior
   @ requires contains(customerId) && (getPerson(customerId) instanceof Advertiser)
   @ assignable messages;
   @ ensures messages.length == \old(messages.length) + 1;
   @ ensures (\forall int i; 0 <= i && i < \old(messages.length);
     (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))); @ ensures (\exists int i; 0 <= i && i < messages.length; messages[i].equals(((Customer)getPerson(customerId)).purchase); @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(customerId); @ signals (PersonIsNotAdvertiserException e) !(getPerson(customerId) instanceof Customer) @
*/ public void addPurchaseMessage(int customerId) throws PersonIdNotFoundException, PersonIsNotCustomerException;

查询销量

  /*@ public normal_behavior
   @ requires containsProduct(id);
   @ ensures (\exists int i; 0 <= i && i < advertise.length; advertise[i] == id && \result == sale[i]);
   @ also
   @ public exceptional_behavior
   @ signals (ProductIdNotFoundException e) !containsProduct(id);
   @*/
  public /*@ pure @*/ int queryProductSale(int id) throws ProductIdNotFoundException;

 

五、本单元学习体会

  这个单元除了第一次作业时理解JML规格花了比较多的时间,完成作业的时间相比前两个单元少了很多,难度相比之前也小了些,只需要根据规格描述完成即可。这样一来就有了更多时间构造评测数据,进行测试。

  在这个单元的理论、作业、实验中,我获得了阅读JML、自己写JML的能力,相信在未来这些能力能被广泛使用。

  

posted @ 2022-06-06 14:33  jht0725  阅读(21)  评论(0编辑  收藏  举报