其他分享
首页 > 其他分享> > 第三单元总结

第三单元总结

作者:互联网

第三单元总结

心得体会

倒数第二个单元结束辽!因为这一单元不需要我们自己去设计整体的结构,只需要理解规格后编写具体函数即可,所以感觉完成时速度会快很多(除了某些规格太长导致看晕了,最后用中文一句一句“翻译”的情况)。但是由于一直以来对性能都无甚追求,所以在算法的实现上还是受到了一些教训的T_T。

经过这一单元的学习,切实体会到了JML表达的严谨性,但是感觉除了对JML的理解,更重要的是把理解的逻辑用代码实现的能力和优化算法的能力。从第一次作业中按照JML给出的数组设置相应的ArrayList,到后来根据具体的需要选择其他容器来简化某些结构并且优化性能,这种感觉也越来略明显——JML的实现并不需要完全按照规格所表述的形式来完成,而是可以根据具体情况来设计实现过程的。

自测方法

在测试方面我是通过JUnit来对代码进行测试,虽然可以通过构造一些数据来尽量全面地测试整个代码,但是并不能测试到算法的性能,因此会出现在强测中超时的情况。因此需要还需要计算一下时间复杂度,并且通过一些数据结构尽量减小时间复杂度来优化性能。

作业分析

第一次作业

第一次作业架构如下:

维护策略

我在最后采用的是并查集的方法来实现qci指令,因此我在addPerson函数与addRelation函数中对几个相关容器的更新操作来维护整个结构,具体实现如下:

我在原有的属性基础上增加了一个新的属性:

public ArrayList<ArrayList> circle = new ArrayList<>();

其作用就是存储当前图中的连通分量。

每当增加一个person时,需要对连通分量集合(circle)进行更新:

@Override
public void addPerson(Person person) throws MyEqualPersonIdException {
   for (int i = 0; i < people.size(); i++) {
       if (people.get(i).equals(person)) {
           throw new MyEqualPersonIdException(person.getId());
      }
  }
   people.add(person);
   ArrayList<Person> point = new ArrayList<>();//每当新增一个Person时,图中便多一个连通分量point
   circle.add(point);//将新增的连通分量(point)加入到连通分量的集合(circle)中;
   point.add(person);//将Person加入到连通分量(point)中

}

每当增加一个relation时,同样需要对连通分量集合(circle)进行更新:

@Override
public void addRelation(int id1, int id2, int value) throws
       MyPersonIdNotFoundException, MyEqualRelationException {
   int i1 = -1;
   int i2 = -1;
   if (!contains(id1) || !contains(id2) || getPerson(id1).isLinked(getPerson(id2))) {
       if (!contains(id1)) {
           throw new MyPersonIdNotFoundException(id1);
      } else if (contains(id1) && !contains(id2)) {
           throw new MyPersonIdNotFoundException(id2);
      } else if (contains(id1) && contains(id2) && getPerson(id1).isLinked(getPerson(id2))) {
           throw new MyEqualRelationException(id1,id2);
      }
  } else {
      ((MyPerson) (getPerson(id1))).acquaintance.add(getPerson(id2));
      ((MyPerson) (getPerson(id1))).value.add(value);
      ((MyPerson) (getPerson(id2))).acquaintance.add(getPerson(id1));
      ((MyPerson) (getPerson(id2))).value.add(value);
       //在addRelation时,关系的两端一定在连通分量集合(circle)中
       for (int j = 0; j < circle.size(); j++) {
           for (int i = 0; i < circle.get(j).size(); i++) {
               if (circle.get(j).get(i).equals(getPerson(id1))) {
                   i1 = j;
                   continue;
              }
               if (circle.get(j).get(i).equals(getPerson(id2))) {
                   i2 = j;
              }
          }
           if (i1 != -1 && i2 != -1) {
               break;
          }
      }
       //只有当两端在两个连通分量中时,需要将两个连通分量合并
       if (i1 != i2) {
           circle.get(i1).addAll(circle.get(i2));
           circle.remove(circle.get(i2));
      }
  }
}

此时若查询两个person是否连通,只需要判断两者是否在同一连通分量中即可。

性能问题和修复情况

本次作业中,一开始我在实现qci指令时采用了DFS算法,导致搜索的性能较低,在强测与互测中都有CPU超时的情况,因此我采用了并查集的方法来提高性能。

第二次作业

第二次作业架构如下:

维护策略

由于本次作业中query_least_connection操作的实现较为复杂,因此为了提高性能,我在MyNetwork中增加了一属性来存储当前图中各个连通分量的边集:

private ArrayList<RelationGroup> relationsCircle = new ArrayList<>();

因此在addRelation操作中,我调用了setRelationsCircle函数,根据具体情况更新当前图中各连通分量的边集,具体实现如下:

private void setRelationsCircle(int id1,int id2,Relation relation) {
   RelationGroup relations1 = null;
   RelationGroup relations2 = null;
   for (RelationGroup relationGroup1 : relationsCircle) {
       if (relationGroup1.getPointGroup().get(id1) != null) {
           relations1 = relationGroup1;
      }
       if (relationGroup1.getPointGroup().get(id2) != null) {
           relations2 = relationGroup1;
      }
       if (relations1 != null && relations2 != null) {
           break;
      }
  }
   //若不存在包含relation两端点的连通分量边集,则新建一连通分量边集
   if (relations1 == null && relations2 == null) {
       RelationGroup newRelationGroup = new RelationGroup();
       newRelationGroup.getEdgeGroup().add(relation);
       newRelationGroup.getPointGroup().put(id1,id1);
       newRelationGroup.getPointGroup().put(id2,id2);
       relationsCircle.add(newRelationGroup);
   //若连通分量的边集中仅存在含有relation一个端点的边,则将relation加入原有的相应边集中
  } else if (relations1 == null && relations2 != null) {
       relations2.getEdgeGroup().add(relation);
       relations2.getPointGroup().put(id1,id1);
  } else if (relations1 != null && relations2 == null) {
       relations1.getEdgeGroup().add(relation);
       relations1.getPointGroup().put(id2,id2);
  } else if (relations1 != null && relations2 != null) {
       //若relation的两端点在同一边集中,则将relation加入该边集中
       if (relations1 == relations2) {
           relations1.getEdgeGroup().add(relation);
       //若含有relation两端点在两个边集中的边上出现,则合并两边集
      } else {
           relations1.getEdgeGroup().addAll(relations2.getEdgeGroup());
           relations1.getEdgeGroup().add(relation);
           relations1.getPointGroup().putAll(relations2.getPointGroup());
           relationsCircle.remove(relations2);
      }
  }
}
性能问题和修复情况

与上一次作业相比,我将大多数容器由ArrayList改为了HashMap,以便于在根据id查找对象时能直接获得对象。本次作业中有一处出现超时情况——在统计平均年龄时,我原本采用的两层循环来进行统计,但是当测试数据条数逼近上限时会出现CPU超时的情况。因此我在MyGroup中加入了ageSum属性,每当addToGroup和delFromGroup时更新,以减小统计平均年龄这一操作的复杂度。

第三次作业

第三次作业架构如下:

维护策略

由于本次作业增加的函数都与Message有关,因此我将MyNetwork中的messages新建一个类——MessageSet,并将所有涉及到messages的函数改为MessageSet类中的方法,来维护代码风格。

性能问题和修复情况

我在本次作业中sendIndirectMessage函数的实现上在用了Dijkstra算法,但是强侧中有一个点超时了。可以通过建立邻接表与采用优先队列的方法解决。

扩展任务

假设出现了几种不同的Person

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

我认为应新增Advertiser、Producer、Customer三个Person的子类,同时新增AdvertisementMessage与PerchaseMessage两个Message的子类,最后增加Product类,由Producer管理不同的Product。

querySalesVolume:查询销售量

可以在Product中设置salesVolume属性,由于一种商品应该只有一家生产商,因此在查询某件产品的销售量只需指明产品Id。而每当完成一单生意时,即Producer收到Advertiser发来的购买信息时,对相应的产品的销售额进行更新。

/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && < producers.length;(\exists int j; 0 <= j && j < producers[i].products.length; producers[i].products[j].getId() == productId));
@ ensures \result == getProduct(productId).salesVolume;
@ also
@ public exceptional_behavior
@ signals (ProductNotFoundException e) !(\exists int i; 0 <= i && < producers.length;(\exists int j; 0 <= j && j < producers[i].products.length; producers[i].products[j].getId() == productId));
@*/
public /*@pure@*/ int querySalesVolume(int productId) throws ProductNotFoundException;

addProduct:增加产品

由于增加产品这一动作的执行者是生产商,因此该函数应设置为Prodecer的一个方法;同时每个producer中都应有一个装载自己product的容器products。

/*@ 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; peoducts[i].equals(product));
   @*/
public void addProduct(Product product) throws EqualProductIdException;

salePeoduct:销售产品

虽然销售产品这一行为的执行者依然是生产商,但是真正改变的是对应产品的销量,因此该方法应为Product类中的方法。

/*@ public normal_behavior
@ assignable salesVolume;
@ ensures salesVolume = \old(salesVolume) + 1;
@*/
public void saleProduct();
 

标签:总结,null,int,id2,relations1,id1,circle,第三,单元
来源: https://www.cnblogs.com/LessIsMore1210/p/16343839.html