OO_Unit3_JML总结
2022 OO U3 JML规格
一、 本单元自测过程中如何利用JML规格来准备测试数据
自测过程中首先选择根据规格捏造一个符合条件的数据进行测试,之后根据JML规格逐条改变条件,测试程序运行结果是否正确。
上述方法虽然能覆盖到针对某一条JML的大部分情况,但由于手捏数据产生的图模型较为简单,故实现了自动化工具。
本单元自动化工具实现起来相对容易,只需针对每一条指令实现相应的数据生成函数,之后先调用ap、ag等指令构造图模型,再随机生成其他的查询指令进行测试。针对部分时间复杂度可能出现问题的方法,可以刻意调整指令的条数等进行测试。
二、梳理本单元的架构设计,分析自己的图模型构建和维护策略
代码架构基本与官方包无差异,主要梳理图模型的构建、维护等。
本单元中person为图的点,relation为图的边,group可以看作点集。为了方便后续的查询,维护了每个点集所属的极大连通子图。在加入点时为其创建并查集;在加入边时若两个并查集不相同将其合并。同时在每个person类里维护了所有从该点出发的边(即relation)
图算法:
最小生成树使用了kruscal算法,代码如下:
int rootId = idToCircle.get(id);
MyPerson root = (MyPerson) people.get(rootId);
if (!root.isDirty()) {
return root.getQlc();
}
int sum = 0;
ArrayList<Edge> edges = new ArrayList<>();
int circleId = idToCircle.get(id);
ArrayList<Integer> personIdList = circleToIdList.get(circleId);
HashMap<Integer, Tree> trees = new HashMap<>();
for (Integer personId : personIdList) {
trees.put(personId, new Tree(personId));
edges.addAll(((MyPerson) getPerson(personId)).getEdges());
}
edges.sort(Edge::compareTo);
for (Edge edge : edges) {
Tree from = findSet(edge.getFrom(), trees);
Tree to = findSet(edge.getTo(), trees);
if (from.getId() != to.getId()) {
sum += edge.getWeight();
if (from.getHeight() <= to.getHeight()) {
from.setFather(to.getId());
if (from.getHeight() == to.getHeight()) {
to.setHeight(to.getHeight() + 1);
}
} else {
to.setFather(from.getId());
}
}
}
root.setDirty(false);
root.setQlc(sum);
最短路径使用了Dijkstra算法:
int id1 = getMessage(id).getPerson1().getId();
ArrayList<Integer> g = circleToIdList.get(idToCircle.get(id1));
HashMap<Integer, Integer> dis = new HashMap<>();
HashMap<Integer, Integer> vis = new HashMap<>();
for (int i = 0; i < g.size(); i++) {
Person person = people.get(g.get(i));
dis.put(person.getId(), INF);
vis.put(person.getId(), 0);
}
PriorityQueue<Edge> que = new PriorityQueue<>();
que.add(new Edge(id1, id1, 0));
dis.put(id1, 0);
while (!que.isEmpty()) {
int now = que.poll().getTo();
if (vis.get(now) == 1) {
continue;
}
vis.put(now, 1);
ArrayList<Edge> edges = ((MyPerson) getPerson(now)).getEdges();
for (int i = 0; i < edges.size(); i++) {
Edge temp = edges.get(i);
if (dis.get(temp.getTo()) > dis.get(now) + temp.getWeight()) {
dis.put(temp.getTo(), dis.get(now) + temp.getWeight());
que.add(new Edge(temp.getFrom(), temp.getTo(), dis.get(temp.getTo())));
}
}
}
int id2 = getMessage(id).getPerson2().getId();
return dis.get(id2);
三、按照作业分析代码实现出现的性能问题和修复情况
本单元没有遇到性能问题。代码实现时只要避免复杂度超过O(n^2)的方法即可。需要注意的是在前期设计时由于不清楚后续作业求和过程中会不会超过int范围,使用了BigInteger,该类型转化以及运算消耗了较多的cpu时间,可能会导致TLE。
四、对Network进行扩展,并给出相应的JML规格
假设出现了几种不同的Person
- Advertiser:持续向外发送产品广告
- Producer:产品生产商,通过Advertiser来销售产品
- Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买
-- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息- Person:吃瓜群众,不发广告,不买东西,不卖东西
Porduder、Advertiser、Customer均实现Person接口,实现了Advertise广告类,其实现了Message接口,且实现了获取对应的生产商方法;Network类增加了products 属性。
- 生产商添加可推广产品
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length;
@ people[i].getId() == id && people[i] instanceof Producer);
@ assignable products;
@ ensures products.length == \old(products.length) + 1;
@ 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 (PersonIdNotFoundException e)
@ (\forall i; 0 <= i && i < people.size; !people[i].equals(producer))
@*/
public void addProduct(Product product, int id) throws PersonIdNotFoundException;
- 广告商推送广告
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() ==
@ id && people[i] instanceof Advertiser) && (\exists int i; 0 <= i && i<=
@ products.length;products[i].getId() == adcertise.getProductId())
@ assignable messages;
@ ensures messages.length == \old(messages.length) + 1
@ ensures (\exists int i; 0 <= i && i < messages.length; messages[i] ==
@ advertise);
@ ensures (\forall int i; 0 <= i && i < \old(messages.length);
@ (\exists int j; 0 <= j && j < messages.length; messages[j] ==
@ (\old(messages[i]))));
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e)
@ (\forall i; 0 <= i && i < people.size; !people[i].equals(producer))
@*/
public void advertise(int id,Message advertise) throw PersonIdNotFoundException;
-
消费者购买产品
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() ==
@ customerId && people[i] instanceof Customer) && (\exists int i; 0 <= i &&
@ i<= messages.length;messages[i].getId() == advertiseId && messages[i]
@ instance of advertise)
@ assignable poeple[*],money;
@ ensures messages.length == \old(messages.length) + 1
@ ensures getPerson(customerId).money == \old(getPerson(customerId).money) -
@ getMessage(advertiseId).getPrice()
@ ensures getPerson(getMessage(advertiseId).getProducer()).money ==
@ \old(getPerson(customerId).money) + getMessage(advertiseId).getPrice()
@ ensures (\forall int i; 0 <= i && i < people.length;
@ (people[i].id != customerId && people[i].id !=
@ getMessage(advertiseId).getProducer() ==> people[i].money == \old(people[i].money)
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e)
@ (\forall i; 0 <= i && i < people.size; !people[i].equals(producer))
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@*/
public void buy(int customerId,int advertiseId) throw PersonIdNotFoundException, MessageIdNotFoundException;
五、学习心得
第通过学习规格我掌握了一种能够消除二义性规范方法作用的手段,但需要注意的是规格是用严谨的方式告诉我们程序运行所产生的结果,而不是给我们提供了实现的思路。不能把方法实现变为简单的规格搬运、看图说话,仍需要有自己的设计以实现正确且架构优秀的代码。