BUAA_OO_Unit3总结
Unit 3
测试数据
-
正确性测试
通过python实现的数据生成器和对拍器,生成大量随机数据,并和室友对拍,来确保程序的正确性。
-
时间测试
对于时间复杂度较高的方法,虽然正确性测试没有问题,但可能会有超时的问题,对于此类问题,采取针对性构造数据的方法进行测试。例如query_least_connection、send_indirect_message、query_group_value_sum等方法,逐一验证其性能是否满足。
图模型构建和维护
-
构建
public class MyPerson implements Person { private final HashMap<Integer, Integer> values; } public class UnionFindSet { private final HashMap<Integer, Integer> set; } public class MyNetwork implements Network { private final HashMap<Integer, Person> people; private final UnionFindSet unionFindSet; }
我使用了两种数据结构:邻接表和并查集,来构建图模型以实现对图的各种查询,对图的各种修改也需要我们同时维护这两种数据结构。
其中HashMap<Integer, Person> people为邻接表,UnionFindSet unionFindSet为并查集。
-
维护
-
add_person
people.put(person.getId(), person); unionFindSet.put(person.getId());
在邻接表和并查集同时加入一个节点。
-
add_relation
((MyPerson) getPerson(id1)).link(getPerson(id2), value); ((MyPerson) getPerson(id2)).link(getPerson(id1), value); unionFindSet.union(id1, id2);
将两个Person互相加入对方的邻接链表中,在并查集中将两个节点合并。
-
query_value
return getPerson(id1).queryValue(getPerson(id2));
在邻接表中查询边的权值。
-
query_circle
return unionFindSet.find(id1) == unionFindSet.find(id2);
在并查集中查询两节点的根节点,若相同则联通。
-
query_block_sum
int sum = 0; for (Integer id : unionFindSet.getSet().keySet()) { if (unionFindSet.getSet().get(id).equals(id)) { sum++; } } return sum;
在并查集中查询连通块的数量,若一个节点的父节点为自己,则连通块的数量加一。
-
query_least_connection
Kruskal算法或者Prim算法
-
send_indirect_message
Dijkstra算法
-
性能问题
因为本次作业中数据量较大,指令条数不多于10000,且时间要求严格,最大运行cpu时间为10s。所以对于任何一条指令,如果时间复杂度为O(n²),就会造成超时,所以我们要尽量将时间复杂度控制在O(nlogn)以下。
-
query_least_connection
最小生成树可以使用Kruskal算法,时间复杂度为O(nlogn),也可以使用Prim算法,时间复杂度为O(n²),性能并不是很好,但可以使用堆进行优化,将时间复杂度优化为O(nlogn)。
-
send_indirect_message
最短路径可以使用Dijkstra算法,时间复杂度为O(n²),性能并不是很好,但可以使用堆进行优化,将时间复杂度优化为O(nlogn)。
-
query_group_value_sum
本指令为查询Group中所有Person的边的权值之和。如果二重循环遍历所有的点,对任意两点之间查询边的权值,那么时间复杂度为O(n²)。性能也不是很好,可以通过遍历每一个点的所有邻接边,即遍历该图中的所有边,将时间复杂度优化为O(n)。
-
delete_cold_emoji
如果使用ArrayList存储emoji,边遍历边删除的时间复杂度是O(n²)的,所以可以用LinkedList,或者使用removeIf方法,时间复杂度为O(n)。
emoji.values().removeIf(value -> value < limit); messages.values().removeIf(message -> (message instanceof EmojiMessage) && !containsEmojiId(((EmojiMessage) message).getEmojiId()));
-
clean_notices
同上。
messages.removeIf(message -> message instanceof NoticeMessage);
-
数据结构
可以使用HashMap来存储Person、Group、Message等信息,其中键为id,值为Person、Group、Message等对象,以实现在O(logn)的时间复杂度内通过id查找对象。
扩展
-
发送广告
/*@ public normal_behavior @ requires containsMessage(messageId) && contains(personId) && @ getPerson(personId) instanceOf Advertiser; @ assignable messages; @ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))); @ ensures !containsMessage(messageId) && messages.length == \old(messages.length) - 1 && @ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != messageId; @ also @ public exceptional_behavior @ signals (MessageIdNotFoundException e) !containsMessage(messageId); @ signals (PersonIdNotFoundException e) containsMessage(id) && !contains(personId); @ signals (PersonIdNotFoundException e) containsMessage(id) && contains(personId) @ && !(getPerson(personId) instanceOf Advertiser; @*/ public void sendAdvertise(int id1, int id2) throws PersonIdNotFoundException, MessageIdNotFoundException;
-
查询销售额
/*@ public normal_behavior @ requires contains(personId) && getPerson(personId) instanceOf Producer; @ assignable nothing; @ ensures \result == (Producer) getPerson(PersonId).querySale(productId); @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(personId) || (contains(personId) @ && !(getPerson(personId) instanceOf Producer); @ signals (ProductIdNotFoundException e) contains(personId) @ && getPerson(personId) instanceOf Producer @ && (Producer) getPerson(personId).containProduct(productId); @*/ public int querySale(int personId,int productId) throws ProductIdNotFoundException, PersonIdNotFoundException;
-
查询销售路径
/*@ public normal_behavior @ requires contains(personId) && getPerson(personId) instanceOf Producer; @ assignable nothing; @ ensures \result == (Producer) getPerson(PersonId).queryProdectPath(productId); @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(personId) || (contains(personId) @ && !(getPerson(personId) instanceOf Producer); @ signals (ProductIdNotFoundException e) contains(personId) @ && getPerson(personId) instanceOf Producer @ && (Producer) getPerson(personId).containProduct(productId); @*/ public List<Person> queryProdectPath(int personId,int productId) throws ProductIdNotFoundException, PersonIdNotFoundException;
学习体会
通过本单元的学习,学习到了如何阅读以及编写JML,体会到了JML语言相对于自然语言的严谨性以及无二义性,但也有其相应的缺点,例如阅读编写起来比较困难,难以理解。同时,巩固复习了图论的相关内容,例如图的存储、连通块的数量、最小生成树、最短路径等问题。此外,该单元增强了自己对于时间复杂度的分析能力,对算法时间复杂度的优化产生了兴趣。