BUAA OO 第三单元总结

BUAA OO 第三单元总结

本次作业相对来说比较简单。JML规格已经给出,只需要根据规格写出代码。关键部分在于要对于几个函数作出一定的性能优化,以便于缩短程序运行时间。

一. 测试数据

本次作业相对来说比较简单,数据的形式也不复杂,因此测试主要是通过随机生成数据的方式进行测试。

在刚刚完成的程序测试,并不需要多么复杂的数据,仅需要针对每一个函数设计一些指令以满足程序不存在一些数组初始化之类的问题。然后在进行大规模的强数据测试。

最原始的方式是存储这二十余种数据的形式,然后随机生成一定量的数据进行测试。这种方式在检测正确性方面是能够发生较大作用的,几十组数据没有问题,几乎就可以保证程序的正确行。然而在程序运行性能方面有一定的缺陷。

这几次作业中生成数据时首先先增加people和group,导致在强测中出现了一些问题,所以数据的生成模式需要进行一些改进,进一步增强随机性。其次,对于一些特殊的指令比如sim需要有一定的针对性,以便于观察程序的运行时间以满足强测的要求。然而一味的卡sim,icr这一类的指令也是没有意义的,对于知识的学习没有什么意义。

在生成数据之后,正确性主要是通过和同学对拍来完成的,大部分情况中可以保证正确性。

二. 架构分析

本单元的三次作业架构都是类似的,为避免重复,主要以第三单元的的架构进行分析。在下面的类图之中,相同的类如异常类进行了一些省略,类的方法主要展示了相对重要的部分。

 

 

图中所示的接口都由作业提前给出,只需要阅读这之中的规格将代码写出即可,本单元引入新的异常类。异常根据我个人观点,就是在程序运行时出现了预期存在的问题,将问题抛出到上层,终止程序的进一步运行。主要是throws和try catch几种方法,通过这次作业,也有了更加深刻的认识。

这几次的难度主要在于阅读JML规格,能够理解规格之后,任务就变得简单了。但本单元没在阅读规格上花费了很多时间,主要的时间都用在了算法优化之上。

本单元设计一些图论的算法,例如判断是否连通,最小生成树和最短路径等问题。单纯完成以保证正确性是不难的,但是在优化部分花费了很长时间,优化的同时又怕影响正确性。兜兜转转改了好多次,最后又回到原点,不得不再一次感受到写代码的乐趣。

三. 性能分析

第一单元能够出现性能问题的主要在于isCircle和queryBlockSum这两个方法。主要是通过并查集来实现。对于getValueSum等问题也可以进行一定优化,主要是在类中就定义变量,在增减或者删除人的时候对这个变量进行维护,可以避免规格里面的遍历实现的方式。

第二单元主要是对最小生成树的实现,在queryLeastConnection中,主要是应用Kruskal算法+并查集进行实现,主要的代码如下

while (var2.hasNext()) {
           MyEdge n = var2.next();
           if (linkedPerson.contains(n.getStart()) || linkedPerson.contains(n.getEnd())) {
               if (find(people.indexOf(getPerson(people, n.getStart())))
                       != find(people.indexOf(getPerson(people, n.getEnd())))) {
                   unite(people.indexOf(getPerson(people, n.getStart())),
                           people.indexOf(getPerson(people, n.getEnd())));
                   result += n.getPrice();
                   edge++;
              }
          }
           if (edge >= newNumPoint - 1) {
               break;
          }
      }
       return result;

第三单元主要是对最短路径的实现,在sendIndirectionMessage方法中,主要是应用dijkstra算法+堆优化。这里进行了很久的优化但是效果并不是很明显,运行时间还是比较长,最终也是没有达到一个比较好的程度,强侧超时一个点也是心甘情愿了。主要代码如下:

for (int i = 0; i < numPoint; i++) {
           flag[i] = 0;
           if (newPeople.get(id1).isLinked(newPeople.get(i))) {
               dist[i] = newPeople.get(id1).queryValue(newPeople.get(i));
          }
           else {
               dist[i] = 6000;
          }
      }
       flag[id1] = 1;
       for (int i = 0; i < numPoint; i++) {
           min = 6000;
           for (int j = 0; j < numPoint; j++) {
               if (flag[j] == 0 && dist[j] < min) {
                   min = dist[j];
                   k = j;
              }
          }
           flag[k] = 1;
           if (k == id2) {
               break;
          }
           for (int j = 0; j < numPoint; j++) {
               if (flag[j] == 0 && (newPeople.get(k).isLinked(newPeople.get(j))) &&
                      (newPeople.get(k).queryValue(newPeople.get(j)) + min) < dist[j]) {
                   dist[j] = newPeople.get(k).queryValue(newPeople.get(j)) + min;
              }
          }
      }
       return dist[id2];
四. Network拓展

1.发送产品广告

/*@ public normal_behavior
     @ requires containsMessage(messageId) && contains(personId) &&
     @         getPerson(personId) instanceOf Advertiser;
     @ assignable messages;
     @ ensures !containsMessage(messageId) && messages.length == \old(messages.length) - 1 &&
     @         (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != messageId;
     @         (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
     @ 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 sendMessage(int personId,int messageId) throws
           MessageIdNotFoundException, PersonIdNotFoundException;

2.查询某种商品的销售额

这个是增加Producer继承Person类,在MyProducer类内部增加容器记录生产的product的id,同时建立容器对应每一个产品的销售额,类似于MyGroup类中的valueSum,只不过这里增加了ValueSum的数量,通过querySaleValue(int productId)进行查询某件产品的总销售额。

/*@ public normal_behavior
     @ requires contains(personId) && getPerson(personId) instanceOf Producer;
     @ assignable nothing;
     @ ensures \result == (Producer) getPerson(PersonId).querySaleValue(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 querySaleValue(int personId,int productId) throws
           ProductIdNotFoundException, PersonIdNotFoundException;

3.查询销售路径

与查询销售额类似,每一次销售产品的时候就在商品对应的容器中增加购买人的id,获得这个容器List,就能知道销售的途径。

/*@ public normal_behavior
     @ requires contains(personId) && getPerson(personId) instanceOf Producer;
     @ assignable nothing;
     @ ensures \result == (Producer) getPerson(PersonId).queryProdectConnection(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> queryProdectConnection(int personId,int productId) throws
           ProductIdNotFoundException, PersonIdNotFoundException;

 

五. 学习体会

本单元的任务相对简单,能够理解JML规格写出代码即可。同时对于算法进行一定的优化,就能较好的完成本单元任务。

 



posted @ 2022-06-06 12:10  徐俊响  阅读(39)  评论(0编辑  收藏  举报