OO 第三单元总结
OO第三单元总结
一、测试数据构造
1、随机生成数据
本单元中,我编写了随机数据生成程序。在生成数据的过程中,我用了多个容器来管理已生成的合法 id 和已生成的合法关系,以保证在生成后续指令的时候有一定的概率生成已有的 id 或关系,代码示例如下
private int generateId(ArrayList<Integer> ids) {
int ret;
if (Math.abs(random.nextInt()) % 3 == 0 || ids.size() == 0) {
ret = random.nextInt();
} else {
int index = Math.abs(random.nextInt()) % ids.size();
ret = ids.get(index);
}
return ret;
}
其中,参数 ids 是管理不同种类 id 的容器,若要生成 personId,则传入管理 personId 的容器,若要生成 groupId,则传入管理 groupId 的容器。在这个方法中,我们使其有约为 1/3 的概率生成一个完全随机的 id,有约为 2/3 的概率生成一个已有的 id。在程序的其他地方,也采用类似的方法控制生成不同种类数据的概率,使其对 MyNetwrok 的覆盖率达到 100%。
2、针对性构造数据
根据不同方法的前置条件,针对性构造边界数据。例如可以构造没有边的图、退化成链的图等来测试图论算法的正确性。
二、架构设计
1、图的存储
我采用邻接表的方式存储图,在 Person 中存储与该 Person 直接相连的点的集合。
2、图论算法
- 图的连通性:在维护图的同时,维护一个并查集。
- 最小生成树:Prim 算法。
- 最短路径:Dijkstra 算法。
三、性能问题和修复情况
本单元中产生的 bug 均为性能问题所致,具体如下
第二次作业
在 query_group_value_sum 中,我采用二重循环遍历计算,产生了 O(n^2) 的复杂度而导致运行超时。修复方案为维护一个 valueSum 变量,在加减人和增加关系的时候进行更新。
第三次作业
第三次作业采用了 O(n^2) 复杂度的 Dijkstra 算法而导致运行超时,修复方案为采用堆优化的 Dijkstra 算法,维护一个小顶堆,使复杂度降为 O(n log n) 。
四、Network 扩展
1、拓展方案
- 新增三个类 Advertiser、Producer、Customer,均继承自 Person。
- 新增 Advertisement 类,继承自 Message,用来表示广告。
- 新增 PurchaseMessage 类,继承自 Message,用来表示购买消息。
- 新增 product 类,用来表示产品。
- 在 Network 中新增 postAdvertisement 方法,表示发布广告。
- 在 Network 中新增 addProduct 方法,表示新增产品。
- 在 Network 中新增 purchase 方法,表示购买产品,即发送 PurchaseMessage。
2、JML 规格
public interface Network {
/*@ public instance model non_null Advertisement[] ads;
@ public instance model non_null Product[] products;
@*/
//@ ensures \result == (\exists int i; 0 <= i && i < products.length; products[i].getId() == id);
public /*@ pure @*/ boolean containsProduct(int id);
/*@ public normal_behavior
@ requires containsProduct(id);
@ ensures (\exists int i; 0 <= i && i < products.length;
@ products[i].getId() == id && \result == products[i]);
@ also
@ public normal_behavior
@ requires !containsProduct(id);
@ ensures \result == null;
@*/
public /*@ pure @*/ Product getProduct(int id);
/*@ public normal_behavior
@ requires containsMessage(id) && getMessage(id) instance of Advertisement;
@ assignable messages, ads;
@ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
@ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
@ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
@ ensures ads.length == \old(ads.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(ads.length);
@ (\exists int j; 0 <= j && j < ads.length; ads[j].equals(\old(ads[i]))));
@ ensures (\exists int i; 0 <= i && i < ads.length; ads[i].getId() == id);
@ also
@ public exceptional_behavior
@ signals (AdvertisementIdNotFoundException e) (!containsMessage(id)
@ || !(getMessage(id) instanceof Advertisement));
@*/
public void postAdvertisement(int id) throws AdvertisementIdNotFoundException;
/*@ public normal_behavior
@ requires contains(producerId) && getPerson(producerId) instanceof Producer &&
@ containsProduct(product.getId());
@ 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 (ProducerIdNotFoundException e) (!contains(producerId)
@ || !(getPerson(producerId) instanceof Producer));
@ signals (EqualProductIdException e) containsProduct(product.getId());
@*/
public void addProduct(int producerId, Product product) throws
ProducerIdNotFoundException, EqualProductIdException;
/*@ public normal_behavior
@ requires containsMessage(id) && getMessage(id) instanceof PurchaseMessage;
@ assignable messages;
@ assignable products;
@ assignable ((PurchaseMessage)getMessage(id)).buyer.money, ((PurchaseMessage)getMessage(id)).producer.money;
@ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
@ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
@ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
@ ensures !containsProduct(\old(((PurchaseMessage)getMessage(id)).productId)) &&
@ products.length == \old(products.length) - 1 &&
@ (\forall int i; 0 <= i && i < \old(products.length) &&
@ \old(products[i]).getId() != \old(((PurchaseMessage)getMessage(id)).productId);
@ (\exists int j; 0 <= j && j < products.length; products[j] == products[i]));
@ ensures ((PurchaseMessage)getMessage(id)).buyer.money ==
@ \old(((PurchaseMessage)getMessage(id)).buyer.money) - ((PurchaseMessage)getMessage(id)).money;
@ ensures ((PurchaseMessage)getMessage(id)).producer.money ==
@ \old(((PurchaseMessage)getMessage(id)).producer.money) + \old(((PurchaseMessage)getMessage(id)).money);
@ also
@ public exceptional_behavior
@ signals (PurchaseIdNotFoundException e) !containsMessage(id) ||
@ !(getMessage(id) instanceof PurchaseMessage);
@*/
public void purchase(int id) throws PurchaseIdNotFoundException;
}
五、学习体会
本单元中我们学习了 JML,JML 能够准确地定义方法的行为,能让我们从逻辑的角度来验证代码实现的正确性。
在学习过程中,我感触最深的是照着 JML 能够很轻松的完成大部分方法,但是代码运行效率却不一定高,为了保证较低的时间复杂度,方法的实现方案还需我们自行设计。