ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

第三单元总结

2022-06-05 14:03:39  阅读:165  来源: 互联网

标签:总结 null int id2 relations1 id1 circle 第三 单元


第三单元总结

心得体会

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

  • Advertiser:持续向外发送产品广告

  • Producer:产品生产商,通过Advertiser来销售产品

  • Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息

  • 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有