OO_第三单元总结
OO_第三单元总结
一、如何准备测试数据
这次单元的三次作业中,我都没有使用 Junit 单元测试来对自己的程序进行测试。
我采用的方法是 静态查找 + 构造特定数据 + 自动测评机对拍 的方式来进行测试。
构造特定数据时,我首先测试每个指令的基本功能,然后依据自己对JML类图的理解,找到一些可能出现错误的边界问题,再构造指令进行测试。这种方法让我在基本功能实现和边界数据上没有出现问题。考虑到自己对JML类图的理解可能有所偏差,所以我还使用了自动测评机进行大量数据的暴力测试并和小伙伴们对拍。
二、分析自己的图模型构建和维护策略
先放上自己的UML类图(只显示了相应的属性)
图模型的构建主要是按照JML规格来构建,除了一个堆优化的Dijkstra算法单独建类(因为行数超500了)和边类就没有再新建自己定义的类。在每个类中,新建了一些优化算法的方法用于图的维护。
维护策略:
-
容器选择
HashMap
- 从类图中可以看出,此容器被大量使用,用来处理
id
和person、message
等的对应关系
- 从类图中可以看出,此容器被大量使用,用来处理
PriorityQueue
- 此容器为优先队列,是使用堆排序实现的优先队列(就不用自己写堆排序了)在用克鲁斯卡尔算法求最小生成树时可以有效利用。
LinkedList
- 在发消息时,需要将消息插入收消息人的消息开头,可以使用这个链表的方法,效率更高。
-
数据维护
数据维护主要和几个复杂度比较高的方法有关,我将从方法角度展开分析。
-
query_circle
- 这个方法主要使用并查集的算法,我在每个
Person
类中设置了father
属性,当每次添加熟人的时候对father
进行维护(如果不是同一个father,就把两人的id小的father
作为最大father
),在query_circle
的时候也先对二人进行递归寻找father
如果父亲相同,则返回True
- 这个方法主要使用并查集的算法,我在每个
-
query_block_sum
- 最开始这个方法我是采用暴力解法的,显然这样效率很低,于是我设置
blocksum
属性进行维护,加人的时候相对于加一棵树,blocksum++
,加关系是如果合并了两棵树(父结点合并了),则blocksum--
,这样只需要直接返回blcoksum
。
- 最开始这个方法我是采用暴力解法的,显然这样效率很低,于是我设置
-
query_group_value_sum
- 加人时遍历全组,对
valueSum
进行维护。
- 加人时遍历全组,对
-
query_group_age_var
- 由于\[\sum(age_i-\bar{age})^2 = \sum age_i^2 - 2*\bar {age}\sum age_i + n* \bar {age}^2 \]所以只需要在每次加人的时候对\(\sum age_i\)和\(\sum age_i^2\)进行维护就好。
- 由于
-
send_indirect_message
- 堆优化的Dijkstra算法!
-
三、分析代码实现出现的性能问题和修复情况
性能问题在这次作业中并没有出现(该优化的大部分都优化了),第一次作业在自测的时候发现性能比同学慢,经过询问,发现是query_block_sum
没有维护而是使用暴力解法,改正之后就好了。
在互测中使用大量qlc
指令实现了5/7的hack率!!!(hack到的时候差点以为强测炸了到B房去了...但结果是好的~)
四、对Network进行扩展,并给出相应的JML规格
题目要求
-
假设出现了几种不同的Person
- Advertiser:持续向外发送产品广告
- Producer:产品生产商,通过Advertiser来销售商品
- Customer:消费者,会关注广告并选择和自己偏好匹配的商品来购买
- Person:吃瓜群众,不发广告,不买东西,不卖东西
Network扩展:建立广告消息类
AdvertiseMessage extends Message
,发送广告send_Advertise()
,交易商品trade_produce()
,查询商品销售额quary_produce_sales()
,查询销售路径quary_sales_path()
,设置偏好set_preference()
。发送广告
send_Advertise()
/*@ public normal_behavior @ requires containsAdvertiseMessage(id) && getAdvertiseMessage(id).getType() == 0 && @ getAdvertiseMessage(id).getPerson1().isLinked(getAdvertiseMessage(id).getPerson2()) && @ getAdvertiseMessage(id).getPerson1() != getAdvertiseMessage(id).getPerson2() && @ \old(getAdvertiseMessage(id).getPerson1()) instanceof Advertiser; @ assignable AdvertiseMessages; @ assignable \old(getAdvertiseMessage(id)).getPerson1().sentAdvertises; @ assignable \old(getAdvertiseMessage(id)).getPerson2().receivedAdvertises; @ ensures !containsAdvertiseMessage(id) && @ Advertisemessages.length == \old(Advertisemessages.length) - 1 && @ (\forall int i; 0 <= i && i < \old(Advertisemessages.length) && @ \old(Advertisemessages[i].getId()) != id; @ (\exists int j; 0 <= j && j < Advertisemessages.length; @ Advertisemessages[j].equals(\old(Advertisemessages[i])))); @ ensures (\old(getAdvertiseMessage(id)).getPerson1().Advertises.lenth == @ \old(getAdvertiseMessage(id).getPerson1().Advertises.lenth) - 1) @ ensures (\forall int i; @ 0 <= i && i < \old(getAdvertiseMessage(id).getPerson2().getAdvertiseMessages().size()); @ \old(getAdvertiseMessage(id)).getPerson2().getAdvertiseMessages().get(i+1) == @ \old(getAdvertiseMessage(id).getPerson2().getAdvertiseMessages().get(i))); @ ensures \old(getAdvertiseMessage(id)).getPerson2().getAdvertiseMessages().size() == @ \old(getAdvertiseMessage(id).getPerson2().getAdvertisetMessages().size()) + 1; @ also @ public normal_behavior @ requires containsAdvertiseMessage(id) && getAdvertiseMessage(id).getType() == 1 && @ getAdvertiseMessage(id).getGroup().hasPerson(getAdvertiseMessage(id).getPerson1()) && @ \old(getAdvertiseMessage(id).getPerson1()) instanceof Advertiser; @ assignable Advertisemessages; @ assignable \old(getAdvertiseMessage(id)).getPerson1().sentAdvertises; @ ensures !containsAdvertiseMessage(id) && @ Advertisemessages.length == \old(Advertisemessages.length) - 1 && @ (\forall int i; 0 <= i && i < \old(Advertisemessages.length) && @ \old(Advertisemessages[i].getId()) != id; @ (\exists int j; 0 <= j && j < Advertisemessages.length; @ Advertisemessages[j].equals(\old(Advertisemessages[i])))); @ ensures (\forall int i; 0 <= i && i < people.length && @ !\old(getAdvertiseMessage(id)).getGroup().hasPerson(people[i]); @ ensures (\forall Person p; \old(getAdvertiseMessage(id)).getGroup().hasPerson(p) && @ p != \old(getAdvertiseMessage(id)).getPerson1(); @ p.getAdvertiseMessages().size == \old(p.getAdvertiseMessages().size) + 1)) @ ensures (\old(getAdvertiseMessage(id)).getPerson1().Advertises.lenth == @ \old(getAdvertiseMessage(id).getPerson1().Advertises.lenth) - 1) @ ensures (\forall int i; 0 <= i && i < people.length && @ !\old(getAdvertiseMessage(id)).getGroup().hasPerson(people[i]); @ \old(people[i].getAdvertiseMessages()) == @ people[i].getAdvertiseMessages()); @ ensures (\not_assigned(people[*].getAdvertiseMessages())); @*/ public void sendAdvertiseMessage(int id);
查询商品销售额
quary_produce_sales()
/*@ public normal_behavior @ requires contains(ProducerId) && getPerson(ProducerId) instanceof Producer && @ getPerson(ProducerId).containsProduce(id); @ ensures \result == getPerson(ProducerId).querySales(id); @*/ public /*@ pure @*/ int query_produce_sales(int ProducerId, int id)
设置偏好
add_preference()
/*@ public normal_behavior @ requires contains(ProducerId) && getPerson(ProducerId) instanceof Customer; @ assignable getPerson(ProducerId).pre; @ ensures getPerson(ProducerId).getPre() == pre; @ ensures (\forall int i; 0 <= i && i < people.length && getPerson(ProducerId) != i; @ \old(people[i].getPre()) == people[i].getPre()); @*/ public void add_preference(int personId, int pre)
五、本单元学习体会
本单元是三个单元以来最轻松的一个单元,在这个单元中我对JML规格有了较为清晰的认识,还记得老师上第一节JML规格课的时候我并不是很能理解它是干啥的,通过不断阅读JML手册理解基本语法后,接下来的工作就是按规格写代码,有些死板,但是确实在这个单元中,由于JML规格指导书的歧义少了很多(甚至几乎没有)。