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