第三单元总结性博客作业

第三单元总结性博客作业

(1) 分析在本单元自测过程中如何利用JML规格来准备测试数据

我认为在自测过程中构造数据是非常重要的,而且可以利用JML规格的各种分支条件来尽可能构造覆盖性强的数据。下面我将用addToGroup函数来说明我们如何通过JML规格来构造数据。

    /*@ public normal_behavior
      @ requires (\exists int i; 0 <= i && i < groups.length; groups[i].getId() == id2) &&
      @           (\exists int i; 0 <= i && i < people.length; people[i].getId() == id1) &&
      @            getGroup(id2).hasPerson(getPerson(id1)) == false &&
      @             getGroup(id2).people.length < 1111;
      @ assignable getGroup(id2).people;
      @ ensures (\forall Person i; \old(getGroup(id2).hasPerson(i));
      @          getGroup(id2).hasPerson(i));
      @ ensures \old(getGroup(id2).people.length) == getGroup(id2).people.length - 1;
      @ ensures getGroup(id2).hasPerson(getPerson(id1));
      @ also
      @ public normal_behavior
      @ requires (\exists int i; 0 <= i && i < groups.length; groups[i].getId() == id2) &&
      @           (\exists int i; 0 <= i && i < people.length; people[i].getId() == id1) &&
      @            getGroup(id2).hasPerson(getPerson(id1)) == false && 
      @             getGroup(id2).people.length >= 1111;
      @ assignable \nothing;
      @ also
      @ public exceptional_behavior
      @ signals (GroupIdNotFoundException e) !(\exists int i; 0 <= i && i < groups.length;
      @          groups[i].getId() == id2);
      @ signals (PersonIdNotFoundException e) (\exists int i; 0 <= i && i < groups.length;
      @          groups[i].getId() == id2) && !(\exists int i; 0 <= i && i < people.length;
      @           people[i].getId() == id1);
      @ signals (EqualPersonIdException e) (\exists int i; 0 <= i && i < groups.length;
      @          groups[i].getId() == id2) && (\exists int i; 0 <= i && i < people.length;
      @           people[i].getId() == id1) && getGroup(id2).hasPerson(getPerson(id1));
      @*/
    public void addToGroup(int id1, int id2) throws GroupIdNotFoundException,
            PersonIdNotFoundException, EqualPersonIdException;

我们可以明显地看出这段JML规格中有两段public normal_behavior和一段public exceptional_behavior。对于两个public normal_behavior我们就对照它的requires前置条件来构造加入person之后人数(未)超过1111的数据;对于public exceptional_behavior我们就分别对抛出的三种异常分支来构造能够抛出groupIdNotFoundPersonIdNotFoundEqualPersonId这三种异常的数据。这样的话就完成了针对addToGroup这个函数的较为全面的数据点的构造,其他的函数也参照这种方法构造覆盖面较强的数据点来测试。

(2) 梳理本单元的架构设计,分析自己的图模型构建和维护策略

本单元的主体就是MyNetwork这个类以及类中方法的实现,其他的如MyPersonMyGroupMyMessage以及继承它的三个类、异常类都是服务于MyNetworkRunner类负责提供输入输出接口、解析等工作。主类将MyNetwork和这些类传入Runner构成一个有机的整体。本单元三次作业都涉及到了图论的知识,第一次作业是并查集、第二次作业是最小生成树、第三次作业是最短路径,涉及到DSU、Kruskal算法和Dijkstra算法,是对于大一下学期学习的数据结构很好的一次复习机会。我的维护策略是尽可能把算法写成static静态函数,比如并查集的findunion,这些函数不仅在isCircle中被调用,而且在Kruskal算法和Dijkstra算法的实现中也一样会被调用,因此写成static静态函数的适用性更强,也更易维护。

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

本单元的每一次作业都对时间复杂度提出了很高的要求,一般只要出现了O(n^2)及以上的算法就会出现CTLE的情况,还需要对算法进行一定程度上的优化,如并查集的路径压缩算法,就把find()操作的时间复杂度从O(n)降到了O(1),大大降低了强测和互测超时的可能性。在这一单元我也认识到缓存数据对于时间复杂度的有效降低(用空间换取时间),如果出现用户对一个函数多次访问的情况,更好地方法是在加入数据时实时更新,用户访问时只需要读就可以了。在实现算法时还需要因地制宜,利用已有的信息最大程度降低时间复杂度,如在第三次作业中,我们已知边的数量远远小于点的数量,那么我们在实现Dijkstra算法更新未连接的点到初始点的最短距离时,就可以遍历边而不是去遍历点。在存储数据上,我尽量使用HashMap和HashSet等通过哈希算法实现的数据结构而不是ArrayList这种线性结构来完成存储,这样也可以降低查询的时间复杂度。

(4) 请针对下页ppt内容对Network进行扩展,并给出相应的JML规格

假设出现了几种不同的Person
  • Advertiser:持续向外发送产品广告
  • Producer:产品生产商,通过Advertiser来销售产品
  • Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
  • Person:吃瓜群众,不发广告,不买东西,不卖东西
如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)

市场营销必不可少的是商品,因此我们需要额外引入一个product类,包含idprice等必要的属性以及getIdgetPrice等必要的方法。为了处理找不到product的异常情况,我们还需要建立一个ProductIdNotFoundException异常类。

首先需要在MyNetwork类增加两个属性

    /*@ public instance model non_null Product[] productList;
      @ public instance model non_null int[] productCount;
      @*/

第一个核心业务功能是销售额的计算

这个功能比较好实现,只需要在市场上能找到该产品的情况下,结果就是产品的价格乘以市场上该产品的数量;当市场上不存在这个产品时抛出异常。

    /*@ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == id);
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == id && \result == productList[i].getPrice() * productCount[i]);
      @ also
      @ public exceptional_behavior
      @ signals (ProductIdNotFoundException e) !(\exists int i; 0 <= i && i < productList.length; productList[i].getId() == id);
      @*/
    public /*@ pure @*/ int getSales(int id) throws ProductIdNotFoundException;

第二个核心业务功能是产品上架

这个功能具体实现有四种正常分支和两种异常分支,分别对应seller拥有大于一个这种product且市场上有这种product;seller只有一个这种product且市场上有这种product;seller拥有大于一个这种product且市场上没有这种product;seller只有一个这种product且市场上没有这种product;sellerId不存在抛出异常;sellerId存在但seller没有这种product抛出异常。

    /*@ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId && people[i].productCount[j] > 1)) &&
      @          (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId);
      @ assignable people[*].productCount, productCount;
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId && people[i].productCount[j] == \old(people[i].productCount[j]) - 1));
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] == \old(productCount[i]) + 1);
      @ also
      @ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId && people[i].productCount[j] == 1)) &&
      @          (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId);
      @ assignable people[*].productCount, people[*].productList, productCount
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && people[i].productList.length == \old(people[i].productList.length) - 1);
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && people[i].productCount.length == \old(people[i].productCount.length) - 1);
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\forall int j; 0 <= j && j < \old(people[i].productList.length && \old(people[i].productList[j].getId()) != productId; (\exists int k; 0 <= k && k < people[i].productList.length; people[i].productList[k].equals(\old(people[i].productList[j]))))));
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\forall int j; 0 <= j && j < \old(people[i].productList.length && \old(people[i].productList[j].getId()) != productId; (\exists int k; 0 <= k && k < people[i].productList.length; people[i].productCount[k] == \old(people[i].productCount[j])))));
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] == \old(productCount[i]) + 1);
      @ also
      @ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId && people[i].productCount[j] > 1)) &&
      @          !(\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId);
      @ assignable people[*].productCount, productCount, productList
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId && people[i].productCount[j] == \old(people[i].productCount[j]) - 1));
      @ ensures productList.length == \old(productList.length) + 1;
      @ ensures productCount.length == \old(productCount.length) + 1;
      @ ensures (\forall int i; 0 <= i && i < \old(productList.length); (\exists int j; 0 <= j && j < productList.length; productList[j].equals(\old(productList[i]))));
      @ ensures (\forall int i; 0 <= i && i < \old(productList.length); (\exists int j; 0 <= j && j < productList.length; productCount[j] == \old(productCount[i])));
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] == 1);
      @ also
      @ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId && people[i].productCount[j] == 1)) &&
      @          !(\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId);
      @ assignable people[*].productCount, people[*].productList, productCount, productList
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && people[i].productList.length == \old(people[i].productList.length) - 1);
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && people[i].productCount.length == \old(people[i].productCount.length) - 1);
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\forall int j; 0 <= j && j < \old(people[i].productList.length && \old(people[i].productList[j].getId()) != productId; (\exists int k; 0 <= k && k < people[i].productList.length; people[i].productList[k].equals(\old(people[i].productList[j]))))));
      @ ensures (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && (\forall int j; 0 <= j && j < \old(people[i].productList.length && \old(people[i].productList[j].getId()) != productId; (\exists int k; 0 <= k && k < people[i].productList.length; people[i].productCount[k] == \old(people[i].productCount[j])))));
      @ ensures productList.length == \old(productList.length) + 1;
      @ ensures productCount.length == \old(productCount.length) + 1;
      @ ensures (\forall int i; 0 <= i && i < \old(productList.length); (\exists int j; 0 <= j && j < productList.length; productList[j].equals(\old(productList[i]))));
      @ ensures (\forall int i; 0 <= i && i < \old(productList.length); (\exists int j; 0 <= j && j < productList.length; productCount[j] == \old(productCount[i])));
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] == 1);
      @ also
      @ public exceptional_behavior
      @ signals (PersonIdNotFoundException e) !(\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId);
      @ signals (ProductIdNotFoundException e) (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId && !(\exists int j; 0 <= j && j < people[i].productList.length; people[i].productList[j].getId() == productId));
      @*/
    public void putOnSale(int sellerId, int productId) throws PersonIdNotFoundException, ProductIdNotFoundException;

第三个核心业务功能是购买和销售产品

这个功能有两个正常分支和三个异常分支:买卖双方存在且非同一人且市场上存在该产品且该产品数量大于一;买卖双方存在且非同一人且市场上存在该产品且该产品数量等于一;买卖双方有一方不存在则抛出异常;买卖双方都存在且为同一人则抛出异常;买卖双方都存在且非同一人且市场上不存在该商品则抛出异常。

    /*@ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] > 1) &&
      @          (\exists int i; 0 <= i && i < people.length; people[i].getId() == buyerId) &&
      @          (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId) &&
      @          buyerId != sellerId;
      @ assignable people[*].money;
      @ ensures productCount.length == \old(productCount.length);
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] == \old(productCount[i]) - 1);
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && getPerson(buyerId).getMoney() == \old(getPerson(buyerId).getMoney()) - productList[i].getPrice());
      @ ensures (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && getPerson(sellerId).getMoney() == \old(getPerson(sellerId).getMoney()) + productList[i].getPrice());
      @ also
      @ public normal_behaviour
      @ requires (\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId && productCount[i] == 1) &&
      @          (\exists int i; 0 <= i && i < people.length; people[i].getId() == buyerId) &&
      @          (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId) &&
      @          buyerId != sellerId;
      @ assignable productList, productCount, people[*].money;
      @ ensures productList.length == \old(productList.length) - 1;
      @ ensures productCount.length == \old(productCount.length) - 1;
      @ ensures (\forall int i; 0 <= i && i < \old(productList.length) && \old(productList[i]).getId() != productId; (\exists int j; 0 <= j && j < productList.length; productList[j].equals(\old(productList[i]))));
      @ ensures (\forall int i; 0 <= i && i < \old(productList.length) && \old(productList[i]).getId() != productId; (\exists int j; 0 <= j && j < productList.length; productCount[j].equals(\old(productCount[i]))));
      @ ensures (\exists int i; 0 <= i && i < \old(productList.length); \old(productList[i]).getId() == productId && getPerson(buyerId).getMoney() == \old(getPerson(buyerId).getMoney()) - productList[i].getPrice());
      @ ensures (\exists int i; 0 <= i && i < \old(productList.length); \old(productList[i]).getId() == productId && getPerson(sellerId).getMoney() == \old(getPerson(sellerId).getMoney()) + productList[i].getPrice());
      @ also
      @ public exceptional_behavior
      @ signals (PersonIdNotFoundException e) !(\exists int i; 0 <= i && i < people.length; people[i].getId() == buyerId) || !(\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId);
      @ signals (EqualPersonIdException e) (\exists int i; 0 <= i && i < people.length; people[i].getId() == buyerId) && (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId) && buyerId == sellerId;
      @ signals (ProductIdNotFoundException e) (\exists int i; 0 <= i && i < people.length; people[i].getId() == buyerId) && (\exists int i; 0 <= i && i < people.length; people[i].getId() == sellerId) && buyerId != sellerId && !(\exists int i; 0 <= i && i < productList.length; productList[i].getId() == productId);
      @*/
    public void buyProduct(int buyerId, int sellerId, int productId) throws PersonIdNotFoundException, EqualPersonIdException, ProductIdNotFoundException;

(5) 本单元学习体会

在本单元中,我学习了JML规格,再一次体验到了抽象化之美。本单元的三次作业虽然相比前两个单元简单一些,但是让我对JML规格以及根据JML规格构造自测数据、根据分支检查程序正确性有了一些认知。本单元还让我复习了图论、时间复杂度等数据结构和算法基础,也让我深刻体验到了用空间换时间的巧妙方法。

posted @ 2022-06-01 23:24  Albert_ZYT  阅读(23)  评论(2编辑  收藏  举报