BUAA_OO 第三单元总结

一.自测数据  

  本次作业我采用的自测策略是根据JML规格中给出的边界条件自造数据进行自测。例如本单元第一次作业的IsCircle函数,可以通过建立复杂图的方式验证算法本身及细节是否正确;第二次作业的queryLeastConnection函数可通过建立含复杂最小生成树的图进行自测;第三次作业的sendIndirectMessage函数可以建立权值差别过大、过小的图来进行自测。

  当然,此方法弊端是有些JML规格的隐含条件可能会被忽略。例如有关Group大小不能超过1111的条件就被忽视,导致程序出现bug。

二.图的构建与维护

  我利用了Hashmap来存储图的所有点,而图中所有的点(即person类)与其他点的关系则需要查询获得,我在Myperson类中建立了 getAcquaintance()和 getValue()以便直接查询目标点的联通点与边权值。同时,在Myperson类中,我采用Hashmap来存储与该点联通的点和边权值,其中id为所有Hashmap的key,这样在加删点或边时都可以通过put或remove对两个Hashmap分别操作,复杂度都是O(1)。这样的缺点是存储时过于繁琐,会导致代码量增大,代码风格分很难保证。

  对于IsCircle函数关于联通分支的查询,我采用了并查集算法:

public int find(int x) {
        if (x == maper.get(x)) {
            return x;
        } else {
            maper.replace(x, find(maper.get(x)));
            return maper.get(x);
        }
    }

  通过递归与路径压缩的方式维护并查集,使查询两点联通性的复杂度为O(1),极大程度地节约了时间复杂度。

  对于queryLeastConnection函数关于最小生成树的查询,我采用了Kruskal算法。因为该算法是以边为核心,涉及连通性的查询,所以可以直接套用并查集的模板。同时,对于该算法我采用封装建立新类的方式,这样既能体现面向对象思想,便于程序拓展,又可减少MyNetwork类码量。该方法复杂度为O(Elog(E))。

public Person find(Person person) {
        if (person == personHashMap.get(person)) {
            return person;
        } else {
            personHashMap.replace(person, find(personHashMap.get(person)));
            return personHashMap.get(person);
        }
    }

  

 public int queryLeastConnection(int id) throws PersonIdNotFoundException {
        if (this.contains(id)) {
            Kruskal kruskal = new Kruskal();
            ArrayList<Person> personArrayList = new ArrayList<>();
            for (Person person : people.values()) {
                if (isCircle(id, person.getId())) {
                    personArrayList.add(person);
                }
            }
            if (personArrayList.size() == 1) {
                return 0;
            }
            return kruskal.work(personArrayList);
        } else {
            throw new MyPersonIdNotFoundException(id);
        }
    }

  对于sendIndirectMessage函数关于最短路径的查询,采用了Dijkstra算法。通过图中点的遍历寻找每次最小权值的边,进行筛选。关于该算法,本人没有进行堆优化,导致程序在强测互测中出现TLE。

三.问题修复

  • 在本单元第一次作业时,由于本人疏忽,部分异常类的计数器没有正常工作(居然过了中测),导致强测出现严重bug。最后一个异常类cnt没有自增。
  • 在本单元第三次作业时,Dijkstra算法没有采用堆优化,强测一个点有TLE。

四.心得体会

  本单元的学习比起第二单元的电梯专题,还是比较轻松的。在我看来,本单元更像是算法课,因为一些函数让我们必须去考虑时空复杂度,利用更合理、优化过的算法去实现。

  同时,JML是一个规范性标准,对于标准的学习是十分有必要的。在今后的学习乃至工作过程中,熟悉标准可以让我们少走弯路,也让彼此的交流变得准确高效。

  此外,JML可以让我对代码的逻辑有了新的理解角度。我认为,JML的编写逻辑并不是在于过程,而是在于起点与终点。就如同本次作业的代码,一个JML可以有多种多样的算法来实现。所以,本次作业让我在今后编写代码的过程中不止于关注过程,也要关注起终点。

五.Network扩展

假设出现了几种不同的Person

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

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

  新建Advertiser、Producer、Customer类,继承Person。

  新建Product类,包含Id,value属性。

  新建Products,记录所有出现的产品。

  方法:

  • 查询某product的value -- getValue()
    /*@ public normal_behavior
    
          @ requires containsProduct(id);
    
          @ ensures \result == getProduct(id).value;
    
          @ also
    
          @ public exceptional_behavior
    
          @ signals (ProductIdNotFoundException e) !containsProduct(id);
    
          @*/
    
    public /*@ pure @*/ int getValue(int id) throws ProductIdNotFoundException;
  • 增加一个product -- addProduct()  
    /*  @ public normal_behavior
        @ requires !(\exists int i; 0 <= i && i < Products.length; Products[i] == product);
        @ assignable Products; 
        @ ensures (\exists int i; 0 <= i && i < Products.length; Products[i] == product);
        @ ensures (\forall int i; 0 <= i && i < \old(Products.length);
        @          (\exists int j; 0 <= j && j < Products.length; Products[j] == (\old(Products[i]))));
        @ 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;
  • 发送广告 -- sendAdvertisement()
    /*@ public normal_behavior
      @ requires containsMessage(id);
      @ assignable Advertisements;
      @ assignable people[*].Advertisements;
      @ ensures !containsMessage(id) && Advertisements.length == \old(Advertisements.length) - 1 &&
      @         (\forall int i; 0 <= i && i < \old(Advertisements.length) && \old(Advertisements[i].getId()) != id;
      @         (\exists int j; 0 <= j && j < Advertisements.length; Advertisements[j].equals(\old(Advertisements[i]))));
      @ ensures (\forall int i; 0 <= i && i < people.length; person[i].likeType(\old(((Advertisement)getMessage(id))).getType) ==> 
      @         ((\forall int j; 0 <= j && j < \old(person[i].getMessages().size());
      @          person[i].getMessages().get(j+1) == \old(person[i].getMessages().get(j))) &&
      @         (person[i].getMessages().get(0).equals(\old(getMessage(id)))) &&
      @         (person[i].getMessages().size() == \old(person[i].getMessages().size()) + 1)));
      @ also
      @ public exceptional_behavior
      @ signals (MessageIdNotFoundException e) !containsMessage(id);
      @*/
    public /*@ pure @*/ void sendAdvertise(int id) throws MessageIdNotFoundException;

     

posted @ 2022-06-02 18:16  KeiEswy  阅读(20)  评论(1编辑  收藏  举报