面向对象JML系列
-
分析在本单元自测过程中如何利用JML规格来准备测试数据
1、本单元的每次指导书中总是建议用Juint单元测试测试程序。
Juint的单元测试是利用相关插件自动生成一个测试文件,在文件中针对要测试的方法编写测试方法。具体的实现是在该方法内,new一个待测试的类,创建相关的主要原理是在该测试方法中先创建相关环境后再对待测试方法得到的结果返回值同正确结果返回值用”assert”比较,以此检验方法的正确性。
示例:
public class Calculator { public int a; public int b; public Calculator(int a,int b) { this.a = a; this.b = b; } public int add(){ return a+b; } } public class CalculatorTest { @Test //测试 add()方法 public void testAdd(){ Calculator c = new Calculator(1,2); Assert.assertEquals(c.add(), 3); } }
但单元测试方法比较适合检验特定测试方法的正确性,并不能检验异常的判断以及处理是否争取,具有一定的局限性同时构造测试方法比较麻烦,如果想要针对所有可能出现的情况以及比较边缘的数据也很费劲。
2、其实类似Juint的测试,每次写完一个方法,可能会构造一个简单的数据输入并通过输出结果同样可以检验该方法的实现是否正确。
同样是根据JML规格对每个新实现的方法构造自测数据,针对每种“public normal_behavior”和“publicexceptional_behavior”所“requires”的条件编写相关数据,同时,比较复杂的指令往往需要调用几个方法,则需要综合考虑多个方法内对“public normal_behavior”和“publicexceptional_behavior”所“requires”的条件构造数据。
3、使用大量随机数据以及针对某一指令生成的特殊数据测试
-
梳理本单元的架构设计,分析自己的图模型构建和维护策略
第九次作业:
针对指令:qbs,操作:求连通块数目
:qci id1 id2 操作:判断两点是否连通
主要算法:并查集
构建:用HashMap<Integer, Integer>来存储id->parentid用来存储和查询连通关系;HashMap<Integer, Integer>来存储id->root,用来建立一个比较平衡的关系树以此来压缩查询路径。用HashMap来存储可以降低查询目标id时的复杂度。
维护:用unionElements() 为两个点添加关系在parent中添加维护关系,同时,根据sz来建立一个更加平衡的关系树。
查询:qci: 用find()找到跟节点,如果根节点相等则为连通的。
qbs: 因为每个连通块中,除了根节点外,其他的均指向其在添加关系时指定的父节点,故遍历已经添加person的在parent中记录的id中指向的parentid == id的点,即可统计连通块数。
代码:
public static void unionElements(int p, int q, HashMap<Integer,Integer> sz,HashMap<Integer,Integer> parent) { int proot = find(p, parent); int qroot = find(q, parent); if (proot == qroot) { return; } if (!sz.containsKey(proot)) { sz.put(proot,1); } if (!sz.containsKey(qroot)) { sz.put(qroot,1); } if (sz.get(proot) < sz.get(qroot)) { parent.put(proot,qroot); sz.put(qroot, sz.get(qroot) + sz.get(proot)); } else { parent.put(qroot,proot); sz.put(proot, sz.get(proot) + sz.get(qroot)); } }
public static int find(int p, HashMap<Integer,Integer> parent) { int q = p; while (parent.containsKey(q) && parent.get(q) != q) { q = parent.get(q); } return q; }
第十次作业:
针对指令:qlc id 操作:查找以id为起点的最小生成树
主要算法:Prim算法
构建:每次查询时都遍历一遍people,挑选出连通的person,存储到Edge[] edge中,其中,Edge是一个存储两端节点和边value的类。
查询:先挑选出来联通的点及所有边后,用Prim算法求出最小生成树的各路径之和。
优化:用并查集维护查询判断边的端点是否已经添加到最小生成树中
public int queryLeastConnection(int id) throws PersonIdNotFoundException { if (contains(id)) { Edge[] tree = new Edge[200000]; int size = 1; for (int i: people.keySet()) { if (i == id) { continue; } if (Edge.isConnected(id, i, parent)) { for (int p:((MyPerson) getPerson(i)).getAcquaintance().keySet()) { tree[size++] = new Edge(i, p, getPerson(i).queryValue(getPerson(p))); } } } quickSort(tree,1,size - 1); HashMap<Integer,Integer> father = new HashMap<>(); HashMap<Integer,Integer> ksz = new HashMap<>(); int ans = 0; for (int i = 1; i < size; i++) { int x = Edge.findset(tree[i].getX(), father); int y = Edge.findset(tree[i].getY(), father); if (x != y) { Edge.push(x, y, father, ksz); ans += tree[i].getZ(); } } return ans; } else { throw new MyPersonIdNotFoundException(id); } }
第十一次作业:
针对指令:sim id 操作:查询messag的两个person间的最短路径
构建:每次查询,都先遍历一遍所有的person并创建一个HashMap<HashMap<Integer, Integer>>来存储连通图内的所有边的两端点和边权重。
查询:Dijkstra算法实现查找目标两点的最短路径,同时特判id1 == id2时return 0;
优化:用堆优化最小边的查找过程。
public void dijkstraInit(HashMap<Integer, HashMap<Integer,Integer>> graph, int id1, int id2, HashMap<Integer, Boolean> s) { for (Person p:this.people.values()) { try { if (isCircle(p.getId(), id1)) { HashMap<Integer, Integer> road = new HashMap<>(); for (int id:((MyPerson)p).getAcquaintance().keySet()) { road.put(id,p.queryValue(getPerson(id))); } graph.put(p.getId(), road); s.put(p.getId(), false); } } catch (PersonIdNotFoundException e) { e.printStackTrace(); } } } public int dijkstra(int id1, int id2) { if (id1 == id2) { return 0; } int max = 2000000; HashMap<Integer,HashMap<Integer,Integer>> graph = new HashMap<>(); HashMap<Integer, Boolean> s = new HashMap<>(); dijkstraInit(graph, id1, id2, s); Queue<E> que = new PriorityQueue<>(); que.add(new E(id1, 0)); HashMap<Integer, Integer> dis = new HashMap<>();//初始不包含 则为无限大 dis.put(id1, 0); while (!que.isEmpty()) { E node = que.poll(); int to = node.getTo(); int cost = node.getCost(); if (s.get(to)) { continue; } s.put(node.getTo(), true); for (int otherNode: graph.get(to).keySet()) { int d; if (dis.containsKey(otherNode)) { d = dis.get(otherNode); } else { d = max; } if (!s.get(otherNode) && d > cost + graph.get(to).get(otherNode)) { dis.put(otherNode, cost + graph.get(to).get(otherNode)); que.offer(new E(otherNode, cost + graph.get(to).get(otherNode))); } } if (to == id2) { break; } } return dis.get(id2); }
-
按照作业分析代码实现出现的性能问题和修复情况
本单元在第十次作业的互测中,对于“qci id1 id2”指令,我写得时候脑子一抽特殊判断的是”return 0”,但是写得时候是没写明白,即使测试时也没有想明白逻辑关系;
第十一次作业中,在“sim”的所实现的堆优化Dijkstra的算法中,表示无穷大的值设置的不够大QAQ,导致当数据量变大后,两点间最短路径边长,即使CPU运算时间是能保证的但会出现抛异常的情况QAQ。
-
请针对下页ppt内容对Network进行扩展,并给出相应的JML规格
/*@ public normal_behavior @ requires contains(id1) && contains(id2) && getPerson(id1) instanceof Advertiser && getPerson(id2) instanceof Customer @ ensures ((Advertiser) getPerson(id1).advType.equals((Customer) getPerson(id2).perType)) ==> (isCircle(id1, id2) == true) @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(id1); @ signals (PersonIdNotFoundException e) contains(id1) && !contains(id2); @ signals (WrongTypePersonException e) contains(id1) && contains(id2) && !(getPerson(id1) instanceof Advertiser) @ signals (WrongTypePersonException e) contains(id1) && contains(id2) && (getPerson(id1) instanceof Advertiser) && !(getPerson(id2) instanceof Customer); @*/ public void sendAdvertise(int id1, int id2) throws PersonIdNotFoundException, WrongTypePersonException; /*@ public normal_behavior @ requires contains(id) && getPerson(id) instanceof Advertiser @ ensures \result == (\num_of int i; 0 <= i && i < people.length; @ people[i] instanceof Customer && @ (Customer) people[i].perType.equals((Advertiser) getPerson(id).AdvType)); @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(id); @ signals (WrongTypePersonException e) contains(id) && !(getPerson(id) instanceof Advertiser) @*/ public int querySalesValum(int id) throws PersonIdNotFoundException, WrongTypePersonException; /*@ public normal_behavior @ requires contains(id) && getPerson(id) instanceof Producer @ ensures \result == (\num_of int i; 0 <= i && i < people.length; @ people[i] instanceof Advertiser && @ (Advertiser) people[i].proType.equals((Producer) getPerson(id).AdvType)); @ also @ public exceptional_behavior @ signals (PersonIdNotFoundException e) !contains(id); @ signals (WrongTypePersonException e) contains(id) && !(getPerson(id) instanceof Producer) @*/ public List<Person> queryMarketChennel(int id) throws PersonIdNotFoundException, WrongTypePersonException;
-
本单元学习体会
本单元主要学习的JML语言,类似一种离散数学的语言,约定好接口的条件和实现结果,以此来约定好该方法的实现。方法的实现又是多种多样的。
在本单元中,在阅读接口JML过程中,不停的查JML手册,逐渐掌握JML语言。同时,由于此次作业的评分方式的改变,性能不是作为性能分出现,而是可以作为一个RTLE的bug来卡,在写方法的时候还需要注意算法的复杂度,此次作业也算是复习了一下算法复杂度的运算。此外,本单元作业是围绕社交网络模型展开的,涉及到查找连通分量、最小生成树、两点间的最短路径的算法,巩固数据结构的基础。