第三单元实验总结 | TrickEye

第三单元实验总结 | TrickEye

基本情况部分

  • 这篇帖子为什么会在这?

    • 这是北航计算机学院面向对象构造与设计2022春季课程第三单元的总结博客

  • 本次作业的要求是什么?

    • 根据课程组提供的JML形式语言,实现一个简单的社交网络建模程序,按要求完成对该社交网络的信息查询功能。

自测篇:如何构造测试数据?

对于JML而言,受其规定的一个方法在各种情况下的行为都受到了JML精确严格的约束,因此如果一个数据能够覆盖JML指出的所有情况,那么这个数据就是合理的。对于我们的程序而言,一个合理的数据应当能够对应代码的每一行。

注意到Idea提供了代码覆盖率检查的工具,它可以根据java程序的一次执行,检测哪些代码被执行了,那些代码没有执行。通过在自动构造数据的基础上手动添加一些数据,能够达到比较高的覆盖率,在此情况下,与其他同学的代码对拍,可以快速检查自己有没有问题。

上面讲到的这种测试方式对于行为复杂,涉及多个异常抛出的方法比较合适。而有些方法,行为种类不多,(只有一个normal_behavior),但是要求繁琐,例如要求连通块数、最小生成树、单源最短路等等。对于这种函数的测试,要求我们构造各种类型的图,避免算法在某些情况下性能退化或者过低不能满足要求的情况。

例如,并查集求最小连通块问题,如果不在合并时维护连通块数量,而是每次询问时用一个O(n)的算法来计算,那就会超时。

再例如,求所有边权值和(QueryGroupValueSum)也不能在询问时查询,而应当在产生关系时维护。

模型构建和维护策略

本次作业的模型可以被抽象成一个无向图。在增删改查这四大操作中,我们只需要支持增和查。增的基本操作包括增加节点,增加关系,增加群组,群组增加成员等;查的操作就有不少。

为了解决比较复杂的查询操作问题,我们采用多种数据结构和算法,例如,维护并查集来解决qbs问题,使用kruskal算法来求最小生成树,使用dijkstra算法求单源最短路。

性能问题

通过使用以上的模型,可以得到正确答案,但是性能不一定强。在一些操作容易出现超时的问题。这是因为虽然JML提供了方法行为的约束,但是不约束,不指定方法的实现方式,如果只是简单的翻译JML的描述,那么的得到的方法性能不会太好。

我遇到的几次这样的性能问题,几乎都是因为询问时查询导致的。例如,qbs的HashSet实现跑了一遍O(n),ArrayList实现跑了一个O(n^2),qgvs跑了一个BFS等等,而优化的思路就是在添加关系的时候适当维护一些量,使得在询问的时候可以直接O(1)查询。

Network 拓展

首先,Advertiser,Producer,Customer均继承自Person类

Advertisement继承自message类

Advertiser向某个Customer发送广告:(这里我们假设是通过message来打广告)

 /*@ public normal_behavior
   @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
   @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
   @   (getMessage(id).getPerson1() instanceof Advertiser) &&
   @ (getMessage(id).getPerson2() instanceof Customer)
   @ assignable messages;
   @ assignable getMessage(id).getPerson1().socialValue;
   @ assignable getMessage(id).getPerson2().messages, getMessage(id).getPerson2().socialValue;
   @ ensures \old(getMessage(id)).getPerson1().getSocialValue() ==
   @         \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() &&
   @         \old(getMessage(id)).getPerson2().getSocialValue() ==
   @         \old(getMessage(id).getPerson2().getSocialValue()) + \old(getMessage(id)).getSocialValue();
   @ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
   @         (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
   @         (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
   @ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getMessages().size());
   @         \old(getMessage(id)).getPerson2().getMessages().get(i+1) ==
   @ \old(getMessage(id).getPerson2().getMessages().get(i)));
   @ ensures \old(getMessage(id)).getPerson2().getMessages().get(0) == \old(getMessage(id));
   @ ensures \old(getMessage(id)).getPerson2().getMessages().size() ==
   @         \old(getMessage(id).getPerson2().getMessages().size()) + 1;
   @ 其他涉及advertise的量的改变,例如
   @ ensures \old(getMessage(id)).getPerson2.getPurchaseWill() == \old(getMessage(id).getPerson2.getPurchaseWill()) +
   @ ((Advertisement) \old(getMessage(id))).getPurchaseWillIncrease;
   @ also
   @ public exceptional_behavior
   @ signals (MessageIdNotFoundException e) !containsMessage(id) || getMessage(id).getType() == 1;
   @ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
   @         !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
   @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
   @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
   @   !(getMessage(id).getPerson1() instanceof Advertiser)
   @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
   @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
   @   (getMessage(id).getPerson1() instanceof Advertiser) &&
   @ !(getMessage(id).getPerson2() instanceof Customer)
   @ */
 public void advertiseToCustomer(int id) throws RelationNotFoundException, MessageIdNotFoundException, PersonIdNotFoundException;
 /*@ public normal_behavior
   @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
   @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
   @   (getMessage(id).getPerson1() instanceof Advertiser) &&
   @ (getMessage(id).getPerson2() instanceof Producer)
   @ assignable messages;
   @ assignable getMessage(id).getPerson1().socialValue;
   @ assignable getMessage(id).getPerson2().messages, getMessage(id).getPerson2().socialValue;
   @ ensures \old(getMessage(id)).getPerson1().getSocialValue() ==
   @         \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() &&
   @         \old(getMessage(id)).getPerson2().getSocialValue() ==
   @         \old(getMessage(id).getPerson2().getSocialValue()) + \old(getMessage(id)).getSocialValue();
   @ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
   @         (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
   @         (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
   @ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getMessages().size());
   @         \old(getMessage(id)).getPerson2().getMessages().get(i+1) ==
   @ \old(getMessage(id).getPerson2().getMessages().get(i)));
   @ ensures \old(getMessage(id)).getPerson2().getMessages().get(0) == \old(getMessage(id));
   @ ensures \old(getMessage(id)).getPerson2().getMessages().size() ==
   @         \old(getMessage(id).getPerson2().getMessages().size()) + 1;
   @ 其他涉及advertise的量的改变,例如
   @ ensures \old(getMessage(id)).getPerson2.getProduceWill() == \old(getMessage(id).getPerson2.getProduceWill()) +
   @ ((Advertisement) \old(getMessage(id))).getProduceWillIncrease;
   @ also
   @ public exceptional_behavior
   @ signals (MessageIdNotFoundException e) !containsMessage(id) || getMessage(id).getType() == 1;
   @ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
   @         !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
   @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
   @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
   @   !(getMessage(id).getPerson1() instanceof Advertiser)
   @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
   @ getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
   @   (getMessage(id).getPerson1() instanceof Advertiser) &&
   @ !(getMessage(id).getPerson2() instanceof Producer)
   @ */
 public void advertiseToProducer(int id) throws RelationNotFoundException, MessageIdNotFoundException, PersonIdNotFoundException;
     /*@ public normal_behavior
       @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
       @ getMessage(id) instanceof RedEnvelopMessage &&
   @   (getMessage(id).getPerson1() instanceof Advertiser) &&
       @ (getMessage(id).getPerson2() instanceof Producer) &&
       @         !isCircle(getMessage(id).getPerson1().getId(), getMessage(id).getPerson2().getId());
       @ ensures \result == -1;
       @ also
       @ public normal_behavior
       @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
       @         isCircle(getMessage(id).getPerson1().getId(), getMessage(id).getPerson2().getId());
       @ assignable messages, emojiHeatList;
       @ assignable getMessage(id).getPerson1().socialValue, getMessage(id).getPerson1().money;
       @ assignable getMessage(id).getPerson2().messages, getMessage(id).getPerson2().socialValue, getMessage(id).getPerson2().money;
       @ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
       @         (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
       @         (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
       @ ensures (\exists Person[] pathM;
       @         pathM.length >= 2 &&
       @         pathM[0].equals(\old(getMessage(id)).getPerson1()) &&
       @         pathM[pathM.length - 1].equals(\old(getMessage(id)).getPerson2()) &&
       @         (\forall int i; 1 <= i && i < pathM.length; pathM[i - 1].isLinked(pathM[i]));
       @         (\forall Person[] path;
       @         path.length >= 2 &&
       @         path[0].equals(\old(getMessage(id)).getPerson1()) &&
       @         path[path.length - 1].equals(\old(getMessage(id)).getPerson2()) &&
       @         (\forall int i; 1 <= i && i < path.length; path[i - 1].isLinked(path[i]));
       @         (\sum int i; 1 <= i && i < path.length; path[i - 1].queryValue(path[i])) >=
       @         (\sum int i; 1 <= i && i < pathM.length; pathM[i - 1].queryValue(pathM[i]))) &&
       @         \result==(\sum int i; 1 <= i && i < pathM.length; pathM[i - 1].queryValue(pathM[i])));
       @ ensures \old(getMessage(id)).getPerson1().getSocialValue() ==
       @         \old(getMessage(id).getPerson1().getSocialValue()) + \old(getMessage(id)).getSocialValue() &&
       @         \old(getMessage(id)).getPerson2().getSocialValue() ==
       @         \old(getMessage(id).getPerson2().getSocialValue()) + \old(getMessage(id)).getSocialValue();
       @ ensures (\old(getMessage(id)) instanceof RedEnvelopeMessage) ==>
       @         (\old(getMessage(id)).getPerson1().getMoney() ==
       @         \old(getMessage(id).getPerson1().getMoney()) - ((RedEnvelopeMessage)\old(getMessage(id))).getMoney() &&
       @         \old(getMessage(id)).getPerson2().getMoney() ==
       @         \old(getMessage(id).getPerson2().getMoney()) + ((RedEnvelopeMessage)\old(getMessage(id))).getMoney());
       @ ensures (!(\old(getMessage(id)) instanceof RedEnvelopeMessage)) ==> (\not_assigned(people[*].money));
       @ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getMessages().size());
       @         \old(getMessage(id)).getPerson2().getMessages().get(i+1) ==
       @         \old(getMessage(id).getPerson2().getMessages().get(i)));
       @ ensures \old(getMessage(id)).getPerson2().getMessages().get(0).equals(\old(getMessage(id)));
       @ ensures \old(getMessage(id)).getPerson2().getMessages().size() ==
       @ \old(getMessage(id).getPerson2().getMessages().size()) + 1;
       @ also
       @ public exceptional_behavior
       @ signals (MessageIdNotFoundException e) !containsMessage(id) ||
       @         getMessage(id).getType() == 1 ||
       @ !(getMessage(id) instanceof RedEnvelopMessage);
       @ signals (RelationNotFoundException e) containsMessage(id) &&
       @         getMessage(id).getType() == 0 &&
       @ (getMessage(id) instanceof RedEnvelopMessage) &&
       @ !(getMessage(id).getPerson1() instanceof Customer || getMessage(id).getPerson2() instanceof Producer)
       @*/
 public void buy(int id) throws PersonIdNotFoundException, MessageIdNotFoundException;

这里,buy就是一个sendIndirectMessage的翻版。

posted @ 2022-06-06 15:54  TrickEye  阅读(53)  评论(0编辑  收藏  举报