第三单元总结

第三单元总结

一、自动化测试

​ 本单元推荐使用Junit 单元测试来对自己的程序进行测试,但事实上配置比较麻烦,而且跟着推荐的两篇博客配置时,也遇到了一点问题,第一篇博客要求修改测试用例模板,将模板中生成的package的包名需去掉test,事实上我修改之后就出现错误,无法识别测试文件,搞了好长时间,最后发现不修改反而可以识别测试文件。

​ 在了解了Junit单元测试后,发现Junit单元测试并不能很好满足我希望的本地测试要求,似乎难以对大量数据的答案的正确性进行检测,加上相对于Junit自动化测试,我还是更熟悉传统的对拍测试方法,考虑到效率等因素,我还是选择了传统的测试模式。

​ 第一单元的正确性检查可以使用python库,第二单元我利用了类似有限状态机的方法。但是本单元的正确型检查比较麻烦,我选择了和小伙伴对拍。

​ 查看JML规格可发现,对于生成的数据并没有严格的显示,如果数据不满足要求的话总会触发异常,而且触发异常也是一种“正确性检查”,所以这里对测试数据没有什么限制。我们可以生成大量的随机数据。

​ 生成数据方面完全随机,在大量的数据投入下,双方都发现了错误,主要还是漏看JML规则,或者是错误理解JML规则的问题。

​ 本单元强测互测均无bug。

二、架构设计——图模型构建和维护策略

​ 本单元架构设计方面,主要还是根据JML规格完善方法。

​ 在图模型构建方面,最主要的还是人之间的关系网络图。关系的网络图是带权的无向图,每个Person都保存了自己相邻的Person节点以及相应的权值。此外并没有特殊构建图模型。仅仅是在向社会网络中添加人和关系的时候维护相关量就可以了。

​ 本单元主要是JML的学习和一些图算法的了解,涉及最小生成树算法,最短路径算法以及并查集。同时为了不超时,需要尽量压缩算法复杂度,消除O(n^2)复杂的算法,复杂度不超过O(nlog(n))。

​ 本单元作业中,queryBlockSum指令官方推荐并查集算法。实际上我们只需在加人和加关系的时候,维护一下连通分量集和连通分量的数目就可以了,而且本单元的三次作业均不必再次扩展。可以做到O(1)的复杂度。

三、按照作业分析代码实现出现的性能问题和修复情况

  • queryLeastConnection需要使用最小生成树算法,由于可能超时,需要加入堆优化,可以使用java现有的优先队列。

  • queryGroupValueSum可以在改变Group里的人时,修改相关的ValueSum。

  • queryGroupAgeVar可以在加人,删人或者加关系的时候维护相关变量,在查询时做到O(1)的复杂度;

​ 对于queryGroupAgeVar,有公式

\(\sum (\mathrm{age}_i - \overline{\mathrm{age}})^2 = \sum \mathrm{age}_i^2 - 2 * \overline{\mathrm{age}} \sum \mathrm{age}_i+ n * \overline{\mathrm{age}}^2\)

因此我们可以在每次加人或者加关系的时候维护\(\sum \mathrm{age}_i^2\), \(\sum \mathrm{age}\), 而且由于计算结果是取整的,因此这样算在精度上不会出现错误。

  • sendIndirectMessage指令涉及到最短路径算法,同样为了避免超时,需要进行堆优化。

  • 如果需要在集合中间删减元素的话,可以使用LinkedList替代ArrayList,效率更高。

  • 此外,比较容易忽视的一个点就是第三次作业中有关表情包信息操作,注意的是这里的一个EmojiId可能对应多个不同的Id(Message)。需要区别EmojiId和Id;

这些优化相当于将一个方法的复杂度平摊到众多的方法里面。

如果不进行相关查询的话,速度可能会慢,但是进行大量的高复杂度的查询命令的话,速度会大大提升。在课程组10000指令的规模下,相比未优化前,可以减少程序最大运行CPU时间,保证不超时。

(也许不进行一些优化也能过强测,但是很可能在互测遇见一些极其***钻的数据,导致超时。)

四、bug分析

主要是和小伙伴对拍的时候发现的错误

  • 第一次没有注意到EmojiId可能对应多个不同的Id(Message)。
  • queryGroupAgeVar 没有除人数,主要是漏看了JML,凭这个方法的名字主观判断了
  • 细节问题,比如有的指令要删信息,但是一些相似的指令不需要删,就导致了主观性的错误。
  • 没有先判断异常,导致出错。
  • 最多的问题就是在优化里出错了,在优化里要格外注意,往往需要在多个方法里对一个变量进行维护,比较分散,容易忽略,导致出错。

五、对Network进行扩展,并给出相应的JML规格

设计的类如下:

image

新增的方法和部分JML规格

//public instance model non_null Product[] product;


/*@ public normal_behavior
  @ requires (\exists int i; 0 <= i && i < people.length;    
  @          people[i].getId() == id && people[i] instanceof Producer); 
  @ assignable product;
  @ assignable getProducer(id).product;
  @ ensures product.length == \old(product.length) + 1;
  @ ensures (\forall int i; 0 <= i && i < \old(product.length);
  @         (\exists int j; 0 <= j && j < product.length; product[j] ==   
  @         (\old(product[i]))));
  @ ensures (\exists int i; 0 <= i && i < product.length; product[i] ==   
  @		     product);
  @ ensures getProducer(id).product.length ==    
  @          \old(getProducer(id).product.length) + 1;
  @ ensures (\forall int i; 0 <= i && i < \old(getProducer(id).product.length);
  @         (\exists int j; 0 <= j && j <getProducer(id).product.length;   
  @       getProducer(id).product[j] == (\old(getProducer(id).product[i])));       
  @ ensures (\exists int i; 0 <= i && i < getProducer(id).product.length;   
  @          getProducer(id).product[i] == product);
  @ also
  @ public normal_behavior
  @ requires !(\exists int i; 0 <= i && i < people.length;    
  @          people[i].getId() == id && people[i] instanceof Producer); 
  @ assignable \nothing;
  @*/
public void createProduct(Product product,int id);

public void advertiseProduct(Product product,int producerId,int advertiserId);

public void watchingAdvertisements(int customerId);

public void buyProduct(int customerId,int productId);

/*
@public normal_behavior
@requires (exists int i;0 <=i && i<product.length ; 
@		   product[i].getid == id );
@ensures  (exists int i;0 <=i && i<product.length ; 
@		   product[i].getid == id && \result ==      	 
@          product[i].getSalesPath );
@also
@requires !(exists int i;0 <=i && i<product.length ; 
@		   product[i].getid == id );
@ensures \result == null;
*/
public String querySalePath(int id);

/*
@public normal_behavior
@requires (exists int i;0 <=i && i<product.length ; 
@		   product[i].getid == id );
@ensures  (exists int i;0 <=i && i<product.length ; 
@		   product[i].getid == id && \result ==      	 
@          product[i].getSalesVolume );
@also
@requires !(exists int i;0 <=i && i<product.length ; 
@		   product[i].getid == id );
@ensures \result == null;
*/
public int querySalesVolume(int id)

六、本单元学习体会

本单元主要学习了一些基础的JML语法,并且复习了一些基础的图算法。

但是阅读JML的时候,对于众多的(),{},并不好掌握,我使用了VS Code准化为.v文件进行阅读,可以把匹配的()和{}用不同的颜色标出来,便于阅读,此外,对于每次作业的迭代,可以使用VS Code的文件对比功能,找到需要进行扩增的点。

JML语言十分准确,保证了课程组传递给我们的信息的准确性,但是一堆的\exists和\forall着实不容易阅读,对于一个简单的向一个容器中加一个元素,都要分四五句着实有点麻烦,可能程序的规格本身就不那么容易地准确表达吧。

posted @ 2022-06-01 19:55  eiang  阅读(60)  评论(0编辑  收藏  举报