「BUAA-OO-2024」第三单元——JML规格化编程总结

谢卓宇-22371061 学生 2024-05-16 20:44:41

「BUAA-OO-2024」第三单元——JML规格化编程总结

目录

  • 「BUAA-OO-2024」第三单元——JML规格化编程总结
  • 前言
  • 本单元测试过程
  • 黑箱测试与白箱测试
  • 黑箱测试
  • 白箱测试
  • 单元测试、功能测试、集成测试、压力测试、回归测试
  • 单元测试
  • 功能测试
  • 集成测试
  • 压力测试
  • 回归测试
  • 本单元数据构造策略
  • 考虑int边界测试
  • 考虑某一种或几种操作的压力测试
  • 单元架构分析
  • hw9的维护与优化
  • 基于并查集与DFS的动态维护方法
  • 三元环的动态维护
  • hw10的维护与优化
  • BFS计算无权最短路径
  • 动态维护tagValueSum
  • 用treeMap动态维护每个人的关系最佳者
  • hw11的维护与优化
  • 性能问题——规格和实现的分离
  • Junit测试
  • 实现与一致性检查
  • 数据构造完备性
  • 单元体会

前言

第三单元以模拟一个社交网络为背景,让我们初步了解并学习了JML规格化设计语言,培养能够根据JML编写高安全性及便于测试的代码程序。

本单元需要注意的核心是基于JML进行算法的优化。JML的编写中不存在算法的选择,而我们在编写程序时则要细致地考虑如何在满足规格的条件下进行时间上的优化,否则很容易在强测中爆点。

本单元测试过程

黑箱测试与白箱测试

黑箱测试

  • 黑箱测试是一种功能性测试方法,它关注的是系统的功能而不考虑其内部结构或实现细节。
  • 在黑箱测试中,测试人员将系统视为一个“黑箱”,只关注输入和输出之间的关系,不考虑系统内部的逻辑和代码。
  • 测试用例设计通常基于需求规格说明或用户文档,目的是验证系统是否符合预期的功能和行为。
  • 黑箱测试独立于编码,可以由非技术人员执行,覆盖面广,容易捕获用户角度的问题。

白箱测试

  • 白箱测试是一种结构性测试方法,它关注系统内部的逻辑、代码和数据流程。
  • 在白箱测试中,测试人员拥有对系统内部结构的了解,通过检查代码覆盖率、路径覆盖等来评估系统的质量。
  • 测试用例设计通常基于代码和设计文档,目的是验证系统内部的逻辑是否正确、是否存在潜在的错误和漏洞。
  • 白箱测试能够深入挖掘系统的内部逻辑和结构,发现隐藏的错误,提高代码质量和可维护性。

单元测试、功能测试、集成测试、压力测试、回归测试

单元测试

  • 单元测试是针对软件中的最小单元进行的测试,通常是函数、方法或类等。
  • 它的主要目的是验证每个单元的行为是否符合预期,以确保单元在隔离的环境下能够正常工作。

功能测试

  • 功能测试是对软件系统的功能进行的测试,验证系统是否符合需求规格说明和用户期望。
  • 在功能测试中,测试人员通常通过模拟用户的操作来测试系统的各项功能,检查系统的输入和输出是否符合预期。

集成测试

  • 集成测试是将已经通过单元测试的模块组合在一起进行测试,验证它们在整个系统中的交互和集成是否正常。
  • 集成测试的目的是确保各个模块之间的接口和通信能够正确地工作,以及系统的各个部分能够协同合作。

压力测试

  • 压力测试是对系统在各种负载条件下进行测试,以评估系统在压力和负载增加的情况下的性能表现和稳定性。
  • 在压力测试中,测试人员通常会增加系统的负载,观察系统在高负载下的响应时间、资源利用率、吞吐量等指标。

回归测试

  • 回归测试是在对软件进行修改或添加新功能后,重新运行之前已经通过的测试用例,以确保修改不会引入新的错误或导致现有功能失效。
  • 回归测试的目的是验证系统的稳定性和一致性,在软件迭代开发过程中起到重要的作用。

本单元数据构造策略

和前两个单元一样,本单元的数据构造依然采用大量随机生成以及手动构造极端数据的方式。大量随机生成能够提高检查出代码错误的概率,而手动构造极端数据能够反映程序的优化效果以及抗压能力,极端数据对于本单元的优化非常重要。

考虑int边界测试

在编写比较方法或比较器的过程中,部分人会使用return (int)a - (int)b;这种方法,但明显如果本身数据处于边界,这种方法会爆掉。

考虑某一种或几种操作的压力测试

例如对于qbs,qtvs,qcs等操作,如果连续进行大量的查询,在没有进行动态维护的情况下非常容易出现CTLE

单元架构分析

本单元总架构如下

img

hw9的维护与优化

基于并查集与DFS的动态维护方法

hw9中qci要求判断两个人是否连通,qbs简单来讲就是求连通分支的总数。这两个指令操作很容易让人想到并查集算法。

但随之而来的问题是,mr操作可能会删除两个人之间的联系,从而改变原先并查集的结构。举例来说,其中一个连通分支是A-B-C-D,若删除了B与C的联系,那么就分裂成了2个连通分支。而加关系的操作ar同样会有类似的改变并查集的问题。

并查集算法本身并不支持删除一个集合中的元素。能想到的一种思路是在每一次改变关系时重建并查,但如果全部重建,时间复杂度依旧很大。因此,我们考虑了一种基于DFS的并查集局部重建的方式。

//基于DFS的并查集局部重建
//以删除了ab之间的关系为例
HashSet visit;
DFS(a, visit); //深度遍历所有与a连通的所有节点,将这些节点的父节点全部设为a,并将这些节点存入visit中
if (!visit.contains(b)) {
    DFS(b);
} //如果visit中不包含b了,说明这个关系的删除导致ab不再连通,从而我们模仿a的重建流程,对与b连通的节点进行DFS遍历,并将这些节点的父节点设为b。这样就完成了并查集的局部重建

实现局部重建后,对于连通分支的总数我们也能很好的进行动态维护。这些操作我们统一在一个新建类MyMap中进行操作。

三元环的动态维护

对于三元环总数的动态维护是非常容易的,我们以添加ab的关系为例,伪代码分析。

tripSum; //应是全局变量
addRelation(a,b); // 添加了ab的关系
if(size(a.aquaintance) < size(b.aquaintance)) {
        遍历a.quaintance;
        if(a.quaintance[i].isLinked(b)) {
            tripSum++;
        } //如果存在一个a的好友iib也是好友,那么就构成了一个三元环,三元环总数加1即可
} else {
        同样的方法对b遍历与维护
}

hw10的维护与优化

BFS计算无权最短路径

qsp操作要求计算从ab两人间最短的路径,这里是一个无权的路径,已经并不需要使用dijstra算法。一个普通的BFS就可以实现要求。

动态维护tagValueSum

qtvs操作要计算一个tag里所有人之间的关系值之和。如果不动态维护一个tag的tagValueSum,而是在每次qtvs操作时进行遍历计算,在强测时会被爆点。因此,我们需要去动态维护每一个tag的这个属性。

具体的维护流程并不复杂,当ab的关系发生改变时,我们都需要去维护与之有关系的tag。我们以稍复杂一些的ab关系被删除为例:

// 首先对a和b的tags中有对方的tag进行处理
for (Tag tag : a.tags) {
        if (tag.hasPerson(b)) {
                遍历该tag中所有人,从valueSum中删去关系值
        }
}
for (Tag tag : b.tags) {
        if (tag.hasPerson(a)) {
                遍历该tag中所有人,从valueSum中删去关系值
        }
}
step2 : 遍历所有tag,若tag中有a和b,则删去关系值。
//一个优化是遍历a和b两个人中人最少的acquaintance

treeMap动态维护每个人的关系最佳者

当涉及qba指令时,需要知道每个人的最佳好友。诚然可以通过每次复杂度为o(n)

的遍历进行维护。但是总归来说效率也依然比较低。因此,我选择了一种天然具有顺序的容器treeMap。对每个人的好友按照value-id键值进行插入排列。

使用treeMap需要自定义一个比较器,这并不难写:

private TreeMap<PersonKey,MyPerson> treeMap = new TreeMap<>(new Comparator<PersonKey>() {
        public int compare(PersonKey p1, PersonKey p2) {
            if (p1.getValue() > p2.getValue()) {
                return 1;
            } else if (p1.getValue() < p2.getValue()) {
                return -1;
            } else {
                if (p1.getId() < p2.getId()) {
                    return 1;
                } else if (p1.getId() == p2.getId()) {
                    return 0;
                } else {
                    return -1;
                }
            }
        }
    });

这里的PersonKey就是单独创建的一个键值类。

hw11的维护与优化

最后一次作业并没有涉及到特殊的优化算法。按照JML完成代码编写就足够。

性能问题——规格和实现的分离

优化是本单元的一个核心。幸运的是本人在3次作业中均未出现性能问题,顺利通过强测。

JML提供给程序员一个代码规格,这个规格规定了一些属性、容器以及方法的逻辑,同时也规定了一些可变与不能够改变的变量。但是JML在编写上并不是唯一的实现方法,也大概率不是最佳的实现方法。因此,我们必须在考虑实际情况下,按照JML规格进行实现的优化。

例如就容器而言,JML只会给出类似Person[]的声明。但这并不意味着我们的代码就必须使用数组的形式,否则我们的代码效率必然很低。我们当然可以选择HashMap等更适合的容器进行存储。

此外,在保证JML的完备性下,我们不一定严格按照JML实现方法,可以自由寻找更高效合适的思路。

Junit测试

本单元中还要求我们对特定的方法进行Junit测试的编写。

我认为主要有2个方面的事情需要我们完成——实现和规格的一致性检查以及数据构造的完备性。

实现与一致性检查

关于这个方面,最好的编写方法是将JML规格逐个进行检查代码的编写。

数据构造完备性

在采取随机的构造条件下,由于Junit的特殊性,可以进行多次检查。即选择合适的时机,对同一组数据的不同位点进行多次检查。这样能覆盖到更多种数据的情况。

单元体会

这一单元的难度相比前两个单元有了适当的降低。核心在于理解JML并学会高效的阅读与代码的翻译编写。JML作为一种保证代码高安全性、高准确率的工具,值得我们深入学习体会。

...全文
48 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
内容概要:本文研究了基于DPWMA调制与正负序分离的ANPC三电平并网逆变器前馈控制策略,旨在解决传统三电平逆变器存在的谐波含量高、电网不平衡工况适应性差及动态响应速度不足等问题。通过采用有源中点箝位(ANPC)三电平逆变器拓扑,结合双极性倍频脉宽调制(DPWMA)、正负序分离锁相技术和电网电压前馈控制,构建了一套一体化的高性能并网控制体系。该体系不仅优化了逆变器的开关动作机制,改善了输出电压电流的谐波特性,而且通过精确的相位同步和扰动补偿,显著提高了系统的动态响应能力和抗扰性能。仿真结果显示,所提出的控制策略能有效降低并网谐波含量,提升锁相精度与系统动态稳定性,确保在复杂电网工况下的高质量稳定并网。 适合人群:具备一定电力电子基础知识和仿真技能的研发人员,尤其是从事新能源发电、储能系统、柔性输电等领域研究的专业人士。 使用场景及目标:①研究和开发高性能并网逆变器,特别是针对大功率、高电能质量要求的应用场景;②探索如何通过先进的调制和控制策略来提高并网逆变器对电网扰动的适应性和响应速度;③为相关领域的学术研究和技术开发提供理论依据和实践指导。 阅读建议:建议读者结合实际的仿真软件(如MATLAB/Simulink)进行实践操作,以便更好地理解和掌握文中提到的各种控制策略的具体实现方法。同时,鼓励读者关注最新的研究成果和发展趋势,不断深化对该领域的认识。

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧