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语言相对于自然语言的严谨性以及无二义性,但也有其相应的缺点,例如阅读编写起来比较困难,难以理解。同时,巩固复习了图论的相关内容,例如图的存储、连通块的数量、最小生成树、最短路径等问题。此外,该单元增强了自己对于时间复杂度的分析能力,对算法时间复杂度的优化产生了兴趣。

posted @ 2022-06-06 15:34  隐姓埋名4567  阅读(19)  评论(2编辑  收藏  举报