折叠

OO 第三单元总结

OO 第三单元总结

架构设计

图模型构建

本单元中我们已经构建的图就是 Network,图中的节点是 Person,图中的边是 Relation,节点之间的连接通过 Person 类中的 Association 这个数组实现,实际上应该是一个邻接表的结构。但为了之后对最短路径以及最小生成树的实现方便以及更方便拓展,我们可以将这些信息抽离出来。

  • 构建 Edge 类,用于表示一条边,包含一下属性:
private final Person person1; //顶点1  
private final Person person2; //顶点2 
private final int value;      //边的权重
private Genealogy genealogy = new Genealogy (); //对应的根节点
  • 构建 Genealogy 类,用于构建并查集储存各节点对应的父节点以及相关方法
    • 每当 addPerson 时,便将(id, id)添加到 familyRelation 中
    • addRelation 时,比较并更新 fatherMap,并判断是否需要合并 Block

维护策略

维护策略:由于在互测时,想必会有构建复杂度高的数据来尝试卡爆时间,所以在写算法是应该尽量优化算法的性能。在本单元作业中需要用维护的指令主要有:

qbs:

加入(删除)Person(value)时,利用并查集对队列信息进行更新,在调用时,直接从队列中取出

qlc:

求最小生成树,可先将该连通图中的边按照从大到小的顺寻排列,然后利用 Kruskal 算法,值得一提的时类的创建与删除是一个很费时间的操作,如果可以应该尽量重复利用,来减少 cpu 运行花费的总时间。

sim:

单源最短路径:利用 Dijkstra 算法,但使得注意的是,利用 java 内置的 PriorityQueue 来实现对元素的大小排序,将复杂度从 O (V²) 降到 O (ElogV)。

性能问题和修复情况

本次作业如果 JML 规格理解正确,代码正确性基本不会有问题,bug 一般都是超时问题。本单元中出现的性能问题如下:

getValueSum: 设置记录的全局变量

对于在操作中不断修改的值,可以通过逻辑分析设置记录的全局变量:如 getValueSum() 设置 valueSum 变量,在 ap 时进行 valueSum = valueSum + 2 * person.queryValue(item);delete person 时进行
valueSum = valueSum - 2 * person.queryValue(item);valueSum 记录了变化的过程,可以随时进行访问。

qbs: 并查集树的合并

在实现 qbs 的时候虽然使用了并查集的方法,但是在合并并查集树的时候我是随意合并的,并没有进行相关优化,这就可能导致一个情况是所有的树合并之后成为一条长链的尴尬情况。为了防止并查集的树退化,可以进行了合并操作,每一次有新的连接被建立,就将较小的树加到较大的树上去(按秩合并)

测试设计

本单元中我尝试了指导书上推荐的 Junit 测试,但由于我对 Junit 了解较少,通过 Junit 所做的测试很是鸡肋,几乎没有找到什么 bug,最终的测试则是通过自己会构造一些边界数据以及依靠同学编写的评测机进行测试。
在编写测试数据时,可以利用 JML 规格的前置条件构造数据便于对所有可能的情况进行分类讨论,保障了测试数据的覆盖率。用后置条件和约束条件来判断输出结果的正确性。但对于代码性能的测试则另需额外构造数据。

NetWork 拓展

拓展分析:
新建 Advertiser、Producer 和 Customer 类继承 Person 类;
新建 Product 类,内含有 id,value 等信息;
新建 Advertisement 、BuyMessage 继承 Message 类。

addProduct(Product product) :添加产品

    /*@ public normal_behavior
      @ requires !(\exists int i; 0 <= i && i < products.length; peoducts[i].equals(product));
      @ assignable products;
      @ ensures products.length == \old(products.length) + 1;
      @ ensures (\forall int i; 0 <= i && i < \old(products.length);
      @          (\exists int j; 0 <= j && j < products.length; products[j] == (\old(products[i]))));
      @ ensures (\exists int i; 0 <= i && i < products.length; products[i] == product);
      @ also
      @ public exceptional_behavior
      @ signals (EqualProductIdException e) (\exists int i; 0 <= i && i < products.length;
      @                                     products[i].equals(product));
      @*/
    public void addProduct(/*@ non_null @*/Product product) throws EqualProductIdException;

queryProductValue(Product product): 查询商品的 value

    /*@ public normal_behavior
      @ requires (\exists int i; 0 <= i && i < products.length; 
      @          products[i].getId() == product.getId());
      @ assignable \nothing;
      @ ensures (\exists int i; 0 <= i && i < products.length; 
      @         products[i].getId() == product.getId() && \result == value[i]);
      @ also
      @ public normal_behavior
      @ requires (\forall int i; 0 <= i && i < products.length; 
      @          products[i].getId() != product.getId());
      @ ensures \result == 0;
      @ also
      @ public exceptional_behavior
      @ signals (ProductNotFoundException e) (\forall int i; 0 <= i && i < products.length; 
      @          products[i].getId () != product.getId ());(bugMessageId);
      @*/
    public /*@ pure @*/ int queryProductValue (Product product) throws ProductNotFoundException;

sendBuyMessage (int advertiserId , int bugMessageId):顾客向 advertiser 发送购买信息

    /*@ public normal_behavior
      @ requires containsMessage(bugMessageId) && getMessage(bugMessageId).getType() == 0
      @         && getMessage(bugMessageId).getPerson1().getType == "Customer"
      @         && getMessage(bugMessageId).getPerson2().getType == "Advertiser"
      @ assignable messages getMessage(id).getPerson1().money;
      @ ensures !containsMessage(bugMessageId) && messages.length == \old(messages.length) - 1 &&
      @         (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != bugMessageId;
      @         (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
      @ ensures (\old(getMessage(bugMessageId)).getPerson1().getMoney() ==
      @         \old(getMessage(bugMessageId).getPerson1().getMoney()) - getMessage(bugMessageId).getProduct.getValue();
      @ also
      @ public exceptional_behavior
      @ signals (MessageIdNotFoundException e) !containsMessage(bugMessageId);
      @ also
      @ public exceptional_behavior
      @ signals (CustomerNotFount id) !containsCustomer(getMessage(bugMessageId).getPerson1());
      @ also
      @ public exceptional_behavior
      @ signals (AdvertiserNotFount id) !containsCustomer(getMessage(bugMessageId).getPerson2());
      @*/
public void sendBuyMessage(int advertiserId , int bugMessageId) throws MessageIdNotFoundException ,CustomerNotFount ,AdvertiserNotFount ;

学习体会

总体来说,本单元的难度相较于之前的两个单元较低,毕竟需要设计的地方比较少,每次作业的难点比较少,只需要读懂 JML 规格进行翻译即可。
通过本单元,我学会了理解基础的 JML 语言,并能根据规范写出符合要求的代码。我认为用 JML 语言来描述方法的规格优点在于严谨,不会出现歧义的表达,但是缺点在于相对于自然语言要更难阅读和理解,又时会遇到自然语言一句话能说清,但是 JML 描述了一堆约束的情况。同时通过本次作业我也回顾了之前学的数据结构,如最小生成树算法、最短路径算法以及并查集等。

posted @ 2022-06-06 10:51  百觅  阅读(6)  评论(0编辑  收藏  举报