OO_第三单元总结

如何利用JML规格来准备测试数据

在第二次作业互测中有位同学因为忽略了一些前置条件和后置条件,结果抛出空指针和除零异常了,因此可以发现JML规格是很严谨的,所以可以多琢磨JML规格去准备测试数据。
比如

    /*@ ensures \result == (people.length == 0? 0:
      @          ((\sum int i; 0 <= i && i < people.length; people[i].getAge()) / people.length));
      @*/
    public /*@ pure @*/ int getAgeMean();

可以构造people.length == 0的测试数据。
比如

    /*@ public normal_behavior
      @ requires contains(id);
      @ ensures \result == ……
      @ also
      @ public exceptional_behavior
      @ signals (PersonIdNotFoundException e) !contains(id);
      @*/
    public /*@ pure @*/ int queryLeastConnection(int id) throws PersonIdNotFoundException;

可以构造!contains(id)的测试数据。

此外,照着JML规格写很容易超时,但是照着JML规格写不容易出错,因此我觉得可以写两种版本,一个版本是照着JML规格的,另一个版本是为了避免超时做了优化的,写两个版本方便对拍。

分析图模型构建和维护策略

第一次作业

qci指令查询两个节点是否连通,可以使用并查集实现。在所有指令中只有ap和ar指令会影响qci指令的图模型。

我使用了HashMap<Integer, Integer> nodes来存取每一个节点的父节点,key为节点id,value为父节点id。

在执行ap指令时,添加一个新节点nodes.put(id, id);(初始状态,自己与自己是连通的)

在执行ar指令时,合并连通分量,维护图模型的连通性即可。

第二次作业

qlc指令查询某个节点的最小生成树,可以使用kruskal算法实现。

我使用了HashMap<Integer, Integer> minTrees来存取每一个根节点的最小生成树。

qlc指令和qci指令类似,也是在执行ap指令时添加新节点,在执行ar指令时合并或更新最小生成树(合并时根节点减少了,需要remove)。但是可以发现ar指令是没有限制条数的,若每执行一条ar指令就更新最小生成树,那么肯定会超时,所以我做了个标记表示需要更新,当执行qlc指令时才更新最小生成树。

第三次作业

sim指令查询某个节点到另一个节点的最短路径,可以使用dijkstra算法实现。

可以使用PriorityQueue实现堆优化,确保了poll出来的一定是距离当前节点路径最短的节点。

分析性能问题

在互测当中发现被hack超时的都是复杂度为\(O(n^{2})\)的指令,可见复杂度为\(O(n^{2})\)的指令是需要去“特殊照顾”的,比如qbs,qgvs,qci等等。

qbs可以和qci指令一起计算,即在新建一个节点时block++,在合并一个连通分量时block--。

qgvs可以创建一个valueSum变量,在执行atg、ar、dfg指令时其值可能会发生变化,需要去维护。

qci指令使用了路径压缩,即寻找根节点return时,将经过的节点的父节点都更新为根节点,可以减少下一次的寻找次数。

qlc指令限制条数了,性能上可以偷懒点🤪。

sim指令使用了堆优化,优化了性能。

Network扩展

添加购买信息

    /*@ public normal_behavior
      @ requires !(\exists int i; 0 <= i && i < messages.length; messages[i].equals(message)) &&
      @             (message instanceof PurchaseMessage) ==> (message.getPerson1() instanceof Customer &&
      @              message.getPerson2() instanceof Producer) &&
      @                (message.getType() == 0)  ==> (message.getPerson1() != message.getPerson2());
      @ 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(message));
      @ ensures (message instanceof PurchaseMessage && message.getType() == 1) ==> (\not_assigned(messages));
      @ signals (EqualMessageIdException e) (\exists int i; 0 <= i && i < messages.length;
      @                                     messages[i].equals(message));
      @ signals (PersonIdNotFoundException e) !(\exists int i; 0 <= i && i < messages.length;
      @                                        messages[i].equals(message)) && 
      @                                        (message instanceof PurchaseMessage) && 
      @                                        !(message.getPerson1() instanceof Customer &&
      @                                        message.getPerson2() instanceof Producer);
      @ signals (EqualPersonIdException e) !(\exists int i; 0 <= i && i < messages.length;
      @                                     messages[i].equals(message)) &&
      @                                     ((message instanceof PurchaseMessage) ==> 
      @                                     (message.getPerson1() instanceof Customer &&
      @                                     message.getPerson2() instanceof Producer)) &&
      @                                     message.getType() == 0 && message.getPerson1() == message.getPerson2();
      @*/
    public void addMessage(Message message) throws
            EqualMessageIdException, EmojiIdNotFoundException, EqualPersonIdException;

发送购买信息

    /*@ public normal_behavior
      @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
      @          getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
      @          getMessage(id).getPerson1() != getMessage(id).getPerson2();
      @ assignable messages;
      @ 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 \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 PurchaseMessage) ==>
      @         (\old(getMessage(id)).getPerson1.getMoney() ==
      @         \old(getMessage(id).getPerson1.getMoney()) - \old(getMessage(id).getPerson2.getProducePrice(produceId)) &&
      @         \old(getMessage(id)).getPerson2.getMoney() ==
      @         \old(getMessage(id).getPerson2.getMoney()) + \old(getMessage(id).getPerson2.getProducePrice(produceId)));
      @ ensures (!(\old(getMessage(id)) instanceof PurchaseMessage)) ==> (\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);
      @ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
      @          !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
      @*/
    public void sendMessage(int id) throws
            RelationNotFoundException, MessageIdNotFoundException, PersonIdNotFoundException;

查询某种商品的销售额

    /*@ public normal_behavior
      @ requires contains(id) && getPerson(id) instanceof Producer;
      @ ensures \result == (containsProduct(productId)) ? getPerson(id).getProduct(productId).getSales : 0;
      @ also
      @ public exceptional_behavior
      @ signals (PersonIdNotFoundException e) !contains(id);
      @ signals (PersonIdNotFoundException e) !(getPerson(id) instanceof Producer);
      @*/
      public /*@ pure @*/ int queryProductSales(int id, int productId) throws PersonIdNotFoundException;

学习体会

一开始很懵,看着一堆JML规格不知道需要实现什么功能,感觉是边写边了解的,得先从简单的JML规格开始写起,在根据方法名猜一猜,后来才越来越熟练🤣。
这一单元的感想:JML规格的好处是严谨,坏处是难读懂难写出。

posted @ 2022-06-06 15:16  日常写bug的路人甲  阅读(39)  评论(0编辑  收藏  举报