oo第三单元总结
一、总体概述
本单元的作业就是在解读JML规格下完成模拟社交网络以及异常的处理。主要任务就是理解JML,所以相对做起来比较容易。同时要注意JML是一种规格化的语言,只要在完成所描述的任务的条件下、并没有限制所使用的容器、算法等。同时也要掌握一些基本的图算法,比如最短路径和最小生成树算法。同时要注意性能的影响,有些指令按照“正常的思路”写有的复杂度甚至能到n5或者n6,1w组数据根本受不了。
同时在这一单元尽量避免arraylist,普通数组的使用,尽量使用hashset等存储结构。(不然说不定莫名其妙复杂度就多了一个数量级
二、作业分析
第九次作业
第九次作业主要重写了三个类,MyPerson、MyGroup以及Mynetwork,同时有6个异常处理操作。MyPerson就是相当于社交网络中的用户,MyGroup就相当于是一个群,而Mynetwork最为复杂,相当于是记录了所有用户的信息,可以访问所有的用户并得到他们之间的关系。具体细节不在赘述。
此处有一个函数isCircle,用于判断两个人之间有没有”间接联系”,简单的来说就是一个人通过他的好友,他的好友在找好友的好友,能联系到另一个人。
此处可以采用并查集的知识,大大降低复杂度。在addrelation的时候首先判断这两个人是否已经在同一个树里了(parent节点相同),如果没有那么就更新,只需要将其中一个的根节点设置成另一个跟节点,存在的话就不必更新。每次查找之后将父节点设置成根节点,进行路径压缩。从而将iscircle的复杂度降至o1。
public int find(int id) {
if (id == pa.get(id)) {
return id;
} else {
pa.replace(id, find(pa.get(id)));//路径压缩
return find(pa.get(id));
}
}
第十次作业
和第九次作业明显的区别就是JML的描述量增加,有一个关于最小生成树的描述,queryLeatConnection,此数我是采用了Prim算法,复杂度是o(n2)。我的prim写的非常拉,因为我用数组实现了(就是大一那个c代码)。但是还是考虑了一下降低复杂度的方法。就是在考虑图的存储,person的数量3000左右,而我们需要的其实是与查询id相关的人,而第九次作业分析了iscircle的复杂度用并查集实现为1。所以在建图的时候可以采用find找到与id的根节点相同的人存储。会在一定程度上降低复杂度。加上对最小生成树查询指令的限制,应该是没有超时的。
第十一次作业
第十一次作业加入了很多消息的种类,发红包、表情啊之类的,加入了几个新的异常。
就是求最短路径的时候容易超时。开始用普通的dist,跑了一组数据cpu用时40s,果断放弃。采用堆优化的dist。首先是图的维护。采用了HashMap<Integer, HashMap<Integer, HashMap<Integer, Integer>>> matrix;维护了所有的边。第一个integer代表一个树的根节点,它所对应的hashmap就是这个树中所有的节点。这个hashmap的key就是每个节点所对应的id,然后下一个hashmap中存储的就是这个节点和其他节点所对应的queryvalue。实现了边的存储。
而dsit复杂度体现在他需要一个比较的过程,即遍历已选好的顶点的相邻的所有边,找到最小的边。而这个比较我们可以采用堆进行优化,将复杂度降至nlogn。具体来说就是用java中已经存在的数据结构priorityqueue(堆实现,就不用我们再花时间写堆了),重写对应的compareTo方法,他会在add方法进行的过程中自动排序。使得我们poll出来是最小的。将找最小值复杂度由n降至logn。
bug:虽然但是,我的money计算错了,手滑将一个人发红包一个人收红包写成两个人都发红包了,不可思议的是强测对了。(说明大部分时候人们还不太喜欢发红包
三、bug分析
这个单元bug出现就比较零散。卡时间复杂度的话,必须有大量的数据堆积。同时也会有很多对jml理解出现的失误(比如括号的位置)。建议写评测机进行数据的投喂。
四、Network拓展
/*@ public instance model non_null Advertiser[] advertisers;
@ public instance model non_null Advertise[] advertise;
@*/
Advertiser
/*@ public instance model int Advertiserid;
@ public instance model Advertise newadvertise;
@*/
/*@ public normal_behavior
@ requires containsAdvertiserid(Advertiserid) && containsId(personid) && getperson(Advertiseid).isLinked(personid);
@ assignable getperson(personid).getadvertise;
@ ensures getperson(personid).getadvertise.size() = \old(getperson(personid).getadvertise.size()) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(getperson(personid).getadvertise.size()); getperson(personid).getadvertise[i+1] == \old(getperson(personid).getadvertise[i]));
@ ensures (getperon(personid).getadvertise[0] == newadvertise);
@ also
@ public exception_behavior
@ assignable nothing
@ requires !containsAdvertiseid(Advertiseod) || !containsid(personid) || !getperson(Advertiseid).isLinked(getperson(personid));
@ signals (PersonIdNotFoundException e) !containsAdvertiseid(Advertiseod);
@ signals (PersonIdNotFoundException e) !containsid(personid);
@ signals (RelarionNotFoundException e) !getperson(Advertiseid).isLinked(getperson(personid));
@*/
public void sendadvertise(int Advertiserid, int personid) throws PersonIdNotFoundException, RelarionNotFoundException;
/*@ public normal_behavior
@ requires containspersonid(personid)
@ assignable getcustomer(personid).itsproducts;
@ ensures (\forall int i; i >= 0 && i <= advertise.size(); getcustomer(personid).itsproducts.add(advertise2produce(advertise[i])));
@ ensures itsproducts.size() == \old(itsproducts.size()) + advertise.size();
@ ensures advertise.size() == 0;
@ public exception_behavior
@ assignable nothing
@ requires !containspersonid(personid)
@ signals (PersonIdNotFoundException e) !containspersonid(personid);
@*/
public void sendmessage2buy(int personid) throws PersonIdNotFoundException;
Product
/*@ public instance model int proid;
public instance model int value;
@*/
/*@ public normal_behavior
@ requires containsproid(proid);
@ ensures \result == getpro(proid).getvalue;
@ also
@ public exception_behavior
@ requires !containsproid(proid);
@ signals (ProductionidNotFoundException e) !containsproid(proid);
public /*pure*/ int getvalueodproduction(int proid) throws ProductionidNotFoundException;
五、本单元总结
本单元重在JML类图的理解,相对来说代码实现并没有很难,但是会出现各种奇奇怪怪的bug。在时间复杂度上尽量降到n2以下,不然数据量过大很容易挂掉。同时要考虑合适的存储结构,比如边的存储。然后就是注意一下ArrayList之类的方法,比如contains之类的就会提高你的复杂度。然后就是评测机的重要性,有些bug手搓是可以的,小数据即可,但是也有些很大量数据才能体现。通过对拍的方式进行bug验证也是不错的方式。总体来说本单元还算顺利。
六、鸣谢
感谢br、xh、ljc哥哥的帮助!给我提供了很多思路和bug的修复。尤其感谢xhgg的评测!