ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OO第三单元总结

2022-06-03 01:31:47  阅读:131  来源: 互联网

标签:OO 总结 遍历 HashMap 最小 算法 JML 顶点 单元


OO第三单元总结

如何自测

关于如何利用JML规格来准备测试数据这一问题:

  • 首先一开始的时候,尝试使用课程组介绍的工具Junit。但后来发现,该工具的作用主要是能够更佳细致地测试每一个函数的功能正确性。但正确答案、以及数据仍需自己给出。
  • 之后便是根据JML准备测试数据。这一部分,首先是随机测试随机测试的覆盖性较弱,针对性也较弱。但能初步地验证程序的正确性,验证不同模块的功能性正确。之后便是针对测试,这一部分主要是集中在两块,一是函数能否正确抛出异常,二是根据JML描述中十分耗时的部分,构造相应的阴间数据,来测试程序的算法是否会出现CPU超时的情况。这主要是通过构造复杂的图结构,增加查询语句。实现诸如稀疏图完全图,等等完成。这其中,第二部分的测试用例十分有效。也让我更深刻地理解了不同JAVA Container之间地性能差异。为了优化程序,不断地改进表达的数据结构,从最开始的600s到最后提交时的2-3s。构造这部分测试用例十分有效!。

图模型构建和维护策略

本次模型涉及到的图结构主要为类Person与类Person之间,并带有加权边value。对此,我们在MyPerson类内部,用HashMap来维护顶点与边的关系。用id作为键值,索引得到相应的边权值。

而对于该图结构,需要处理的三类问题分别是最小生成树最短路径并查集。由于这两个问题,在迭代维护上可以采用不同的策略,于是我采用了不同的数据结构来维护这两个算法。

  • 最小生成树:针对最小生成树,其具有强烈的迭代特性。我们很容易得出,在得到当前图的最小生成树时,若新增一条边,则对应以下两种情况。
    • 新增边在最小生成树内部:这种情况时,由于原最小生成树边集的数量为n-1。因此根据定理,新增一条边后,只需要去除一条边使其不成环,便得到新的生成树。而为了得到最小生成树,需要保证留下最小边。因此,我们构建了一个有序递增边集。对其仅需遍历,判断是否成环操作,来去除第一个使其成环的边,便可以得到新的最小生成树。
    • 新增边使其连通了其他图或顶点:对于这种情况,我们仅需要维护有序递增边集,依顺序插入即可,不需要去除额外的边。

​ 由上可知由于这个有序递增边集存在大量插入,删除操作。因此我们选用链表来实现该最小生成树算法。

  • 最短路径:针对最短路径算法,我选用迪杰克斯拉算法。该算法的好处是实现容易,思考量低。但同样在整个算法的实现过程中我们需要维护两个集合,已加入路径顶点未加入路径顶点;以及一个距离矩阵,原点到未加入路径顶点的距离。为了实现不重的特性,采用HashSet作为存储顶点的容器,而对于路径矩阵选用HashMap实现。这样便能较为优雅的实现迪杰克斯拉算法。
  • 并查集:并查集主要是判断任意两点之间是否连通这一问题。对于该问题,我首先想到的是能否使用HashSet来表示处于同一个连通子集的个体。但如何来保存所有的连通子集呢。如果用List则对于每一次查询需要遍历List来判断自己处于哪一个连通子集中。因此,采用HashMap,Key为MyPerson类自己,Value为相对应的连通子集。因此查询就变味了O(1)。

性能问题和修复情况

这个在每一次作业都有遇到。

第九次作业:主要是针对并查集的优化问题,原始采用ArrayList<HashSet<Person>>的嵌套方式,储存该图下的每一个连通子集。此时对于每一个查询是否处于同一连通子集操作的时间复杂度为O(n)。而后优化为HashMap<Person, HashSet<Person>>,此时在针对于每一个查询,时间复杂度优化到O(1)。

第十次作业:这一次作业是优化最多的部分。由一开始的对一份数据从600s优化为2-3s

  • 算法优化,最开始为Prim算法。这需要我们遍历每一个点的每一条边,这是一个十分耗时间的操作,因为我们储存边、顶点的结构体是HashMapHashMap的遍历是一个十分费时的操作。所以改为Kruskal算法
  • 数据结构优化,一开始未能注意到最小生成树的迭代特性。在每一次访问最小生成树时,遍历HashMap容器重新计算。导致每一次时间开销较大。而后,转为如上文所说。用自定义的链表结构实现,同时仅保存最小生成树的边,设置脏位,防止频繁计算。极大地优化了性能。
  • 实现细节优化,这一步主要针对算法编写过程中的遍历行为。对于kruskal算法用到的许多集合,通过减少遍历次数,减少容器使用量等等。都可以进一步优化速度。

第十一次作业:这一次作业主要是针对迪杰克斯拉算法。该部分的优化遇到的问题仍然是上面所提到的HashMap遍历问题。最大的问题是距离矩阵,由于未维护最小堆。在已加入顶点越来越多时,距离矩阵的键值对数量也越来越多。而我们需要遍历得到未加入顶点的最小值,所以需要遍历的对象也越来越多,这非常耗时。于是做了一个优化是,在每次加入顶点后,将该顶点从距离矩阵中删除,这样便可以得到更小的距离矩阵。从而加速。

JML规格

Advertiser

/*@ public normal_behavior
      @ requires containsProduct(product.getId()) && contains(advertiser.getId())
      @ assignable people[*].favorProducts;
      @ ensure ((\forall int i; 0 <= i && i < people.length && people[i] instanceof Customer && people[i].isLinked(advertiser)
      @          && !people[i].hashProduct(product.getId);
      @          people[i].favorProducts.length == \old(people[i].favorProducts) + 1 &&
      @        (\exist int j; 0 <= j && j < people[i].favorProducts.length; people[i].favorProducts[j] == product)
      @ ensure ((\forall int i; 0 <= i && i < people.length && people[i] instanceof Customer && people[i].isLinked(advertiser)
      @          && !people[i].hashProduct(product.getId);
      @          people[i].favorProductsValue.length == \old(people[i].favorProductsValue) + 1 &&
      @        (\exist int j; 0 <= j && j < people[i].favorProductsValue.length && people[i].favorProducts[j] == product;
      @         people[i].favorProductsValue[j] == 1)
      @ ensure ((\forall int i; 0 <= i && i < people.length && people[i] instanceof Customer && people[i].isLinked(advertiser)
      @          && people[i].hashProduct(product.getId);
      @        (\exist int j; 0 <= j && j < people[i].favorProductsValue.length && people[i].favorProducts[j] == product;
      @         people[i].favorProductsValue[j] == \old(people[i].favorProductsValue[j]) + 1)
      @ also
      @ public exceptional_behavior
      @ signals (ProductIdNotFoundException e) !containsProduct(product.getId())
     */
    public void publishAdvertise(Product product, Advertiser advertiser);

Producer

/*@ public norm_behavior
      @ requires !containsProduct(product.getId())
      @ assignable products;
      @ ensure products.length = \old(products.length) + 1;
      @ ensure (\forall int i; 0 <= i && i < \old(products.length);
      @         (\exists int j; 0 <= j && j < products.length; products[j] == (\old(products[i]))));
      @ ensure (\exists int i; 0 <= i && i < products.length; products[i] == product);
      @ also
      @ signals (EqualProductIdException e) (\exists int i; 0 <= i && i < products.length;
      @                                     products[i].equals(product));
     */
    public void produceProduct(Product product);

Customer

/*@ public norm_behavior
      @ requires contains(id) && containsCustomer(id);
      @ assignable people[*].saleProducts;
      @ ensure \result == (\max int i; 0 <= i && i < getPerson(id).favorProducts.length; getPerson(id).favorProductsValue[i])
      @ also
      @ signals (PersonIdNotFoundException e) !contains(id);
      @ signals (CustomerIdNotFoundException e) contains(id) && !containsCustomer(id);
     */
    public Product buyProduct(int id);

本单元学习体会

本单元的学习心得主要是针对JML规格的一系列问题。对于JML规格本身而言,他是一个很好地能够使得机器以及人类都能理解规格语言,缺点在于表述不够直观,针对一些复杂的需求,往往会占用大量篇幅。容易让人忽视细节。我这次的作业中便出现了这样的问题,在第十一单元的作业中,忽视了一个异常处理。导致强测诸多测试点出现问题。这是十分不妙的。

于此,我认为本单元主要需要我们养成规格化的阅读能力,能够解析复杂的JML格式。理解不同类之间的关系,以及JML语法的应用等等。这对于以后我们在其他场合,建立相应的规格描述思想,规范十分有帮助。

标签:OO,总结,遍历,HashMap,最小,算法,JML,顶点,单元
来源: https://www.cnblogs.com/carkham/p/16339454.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有