BUAA_OO_Unit3 总结

BUAA_OO_Unit3 总结

目录

  •  分析在本单元自测过程中如何利用JML规格来准备测试数据
  • 梳理本单元的架构设计,分析自己的图模型构建和维护策略
  •  按照作业分析代码实现出现的性能问题和修复情况
  • 对Network进行扩展,并给出相应的JML规格
  • 本单元学习体会

一、测试思路

  • 在测试方面,主要依靠测评机进行对拍。
  • 在构造数据方面:根据不同的前置条件情况构造数据,覆盖所有可能出现的数据类型。

二、架构设计

本次作业架构主要按照JML规格来进行设计,涉及到的图论算法有并查集最小生成树最短路径

图模型构建

  • 并查集

   在第一次作业中需要查询两个结点是否连通,使用dfs算法复杂度高达O(n^2),时间效率不高。为了降低复杂度,我们可以使用并查集来处理判断结点是否连通的问题。但传统的并查集在判断两个结点是否连通的过程中,需要递归查询结点的根节点,通过判断根节点是否相同来判断这两个是否连通,如果集合中的结点形成一条链,会降低代码的运行效率。我们可以通过路径压缩——同一集合中的结点指向同一个根节点,可以进一步降低查询的复杂度。

 维护策略:

public static void kUnion(int id1, int id2,HashMap<Integer,Integer> trees,
                        HashMap<Integer,ArrayList<Integer>> gathers) {
        int parentId1 = trees.get(id1);
        int parentId2 = trees.get(id2);
        if (id1 != id2 && parentId1 != parentId2) {
            ArrayList<Integer> gather1 = gathers.get(parentId1);
            ArrayList<Integer> gather2 = gathers.get(parentId2);
            if (gather1.size() < gather2.size()) {
                trees.put(id1, parentId2);
                gather2.add(id1);
                for (int i : gather1) {
                    trees.put(i, parentId2);
                    if (!gather2.contains(i)) { gather2.add(i); }
                }
                gathers.put(parentId2, gather2);
                gathers.remove(parentId1);
            } else {
                trees.put(id2, parentId1);
                gather1.add(id2);
                for (int i : gather2) {
                    trees.put(i, parentId1);
                    if (!gather1.contains(i)) { gather1.add(i); }
                }
                gathers.put(parentId1, gather1);
                gathers.remove(parentId2);
            }
        }
    }

其中,trees储存了所有的结点及其对应的根节点信息;groups储存了所有的根节点及该根节点对应的子节点的集合。

  • 最小生成树——kruskal算法

    为了实现hw10中的qlr(求person所在连通图的最小生成树)指令,使用了kruskal算法。

    新建了MyEdge类,包含属性startId(startId == person1.getId())、endId(endId == person2.getId())、weight (weight == person1.queryValue(person2))。

    在求person所在连通图的最小生成树时,先获取树中的所有结点。查询结点间的连通关系,并或得所有的边, 再通过kruskal算法获得最小生成树。

核心代码如下:

for (int i = 0; i < size; i++) {
            int id1 = nodes.get(i);
            Person person1 = getPerson(id1);
            for (Person person : ((MyPerson) person1).getAcquaintance()) {
                if (person1.isLinked(person)) {
                    MyEdge edge = new MyEdge(id1, person.getId(), person1.queryValue(person));
                    edges.add(edge);
                }
            }
        }   
  • 最短路径——dijkstra算法

    为了实现hw11中的sim(求连通图中两点间的最短路径)指令,使用了Dijkstra算法;

三、问题分析

  • 性能问题
    • hw9:isCircle直接使用了dfs实现;
    • hw10:qgvs使用for循环嵌套;
    • hw11:最小生成树算法中使用常规Dijkstra算法,导致超时。
  • 修复情况
    • hw9:使用并查集,queryBlockSum通过查询有几个不连通的集合实现;

    • hw10:MyGroup中增加valueSum属性,通过查询valueSum实现queryGroupValueSum,将复杂度降为了O(1);
    • hw11:采用Dijkstra堆优化算法;

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

题目:

假设出现了几种不同的Person

  • Advertiser:持续向外发送产品广告
  • Producer:产品生产商,通过Advertiser来销售产品
  • Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
  • Person:吃瓜群众,不发广告,不买东西,不卖东西

如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)

  • 对Network扩展
    • 新建Advertiser、Producer、Customer类,都继承Person类
      • Customer类增加属性preference表示自己偏好的产品, product[]表示购买的产品
      • Producer类增加属性type表示生产产品的类型
    • 新建 AdvertiseMessage 、CustomMessage类 ,都继承Message类
      • AdvertiseMessage表示Advertiser发送的产品广告,包含属性:product, advertiser(发送广告的人)
      • CustomMessage表示Customer直接通过Advertiser给相应Producer发送的购买消息,包含属性:customer(发送购买消息的人),advertiser(直接接收消息的advertiser)
    • 新建Product类,包含属性:type(产品类型), value...
  • 选择3个核心业务功能的接口方法撰写JML规格
    • 发送产品广告
       /*@ public normal_behavior
            @ requires containsMessage(id) && (getMessage(id) instanceof Advertisement);
            @ assignable messages;
            @ assignable people[*].messages;
            @ ensures  !containsMeaage(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 < people.length && !getMessage(id).getPerson1().isLinked(people[i]); @ people[i].getMessages().equals(\old(people[i].getMessages())); @ ensures (\forall int i; 0 <= i && i < people.length && getMessage(id).getAdvertiser().isLinked(people[i]); @ (\forall int j; 0 <= j && j < \old(people[i].getMessages().size()); @ people[i].getMessages().get(j+1) == \old(people[i].getMessages().get(j))) @ ensures people[i].getMessages().get(0).equals(\old(getMessage(id))) @ ensures people[i].getMessages().size() == \old(people[i].getMessages().size()) + 1); @ also @ public exceptional_behavior @ signals (MessageIdNotFoundException e) !containsMessage(id); @ signals (NotAdvertisementException e) !(getMessage(id) instanceof Advertisement); @
      */ public void sendAdvertisement(int id) throws MessageIdNotFoundException, NotAdvertisementException;

       

    • 生产产品
      /*@ public normal_behavior
            @ requires contains(producerId) && (getPerson(producerId) instanceof Producer)
           
            @ assignable products[];
          @ ensures (\exists int i; i<product.length && i>=0; products[i] == product);
            @ ensures getProductCount(productId) ==
            @           \old(getProductCount(productId)) + 1;
            @ also
            @ public exceptional_behavior
            @ signals (PersonIdNotFoundException e) !contains(producerId);
            @ signals (NotProducerException e) !(getPerson(producerId) instanceof Producer);
          @
            @*/
          public void produceProduct(int producerId, Produce product) throws
                  PersonIdNotFoundException, NotProducerException;
    • 购买产品
      /*public normal_behavior
        @ requires product.contains(product) && contains(coustomerId) && getPerson(customerId) instanceof Customer
        
         @ assignable products[], getPerson(customerId).money, getPerson(customerId).products;
        @ ensures products.length == \old(products.length) - 1;
        @ ensures (\forall int i; i>=0 && i<=\old(products.length)) && product[i]!=product; 
                (\exist int j; j>=0 && j<=products.length; product[j].equals(product[i]))
        @ ensures getPerson(customerId).getmoney == \old(getPerson(customerId)) - product.getValue();
        
        @ ensures getPerson(customerId).getProduct().length == \old(getPerson(customerId).getProduct().length)+1;
        @ ensures (\forall int i; i>=0&&i<=\old(getPerson(customerId).getProduct().length)&&\old(getPerson(customerId).getProduct().get(i));
                (\exist int j; j>=0 && j<=getPerson(customerId).getProduct().length; 
                  getPerson(customerId).getProduct().get(j).equals(\old(getPerson(customerId).getProduct().get(i)))
        @ ensures (\exist int j; j>=0 && j<=getPerson(customerId).getProduct().length; getPerson(customerId).getProduct().get(i).equals(product);
        
        @public exceptional_behavior
        @ signals (NotProductException e) !product.contains(product); 
        @ signals (NotCustomerException e) !contains(coustomerId) || !(getPerson(producerId) instanceof Producer);
      */
       
      public void buyProduct(int coustomerId, Product product) throws NotProductException, NotCustomerException

       

       

五、学习体会

本单元的难度小了很多,但更需要严谨仔细地阅读JML规格,充分理解代码的前置条件和后置条件。

遇到复杂的前置条件和后置条件时,需要将其提炼化简,理解它想要我们干什么。在提炼化简的过程中需要注意正确性。

为了提高代码的效率,我们编程时没有必要完全按照JML规格的描述进行,可以进行一定的优化,但这需要在充分理解JML规格。

此外,在学习过程中,我们需要细心,在本单元的练习中,我们需要阅读大量的UML代码,在阅读过程中一定要沉下心,仔细阅读,不可想当然,写完代码后最好对照JML规格检查一遍。

posted @ 2022-06-05 19:39  Hu_ly  阅读(27)  评论(0编辑  收藏  举报