可拓展性设计与复杂度控制——OO-UNIT1

于恩泽-22371494 学生 2024-03-21 21:58:59

文章目录

  • 0 前言
  • 1 从整体看架构
  • A.可拓展性分析:
  • B.复杂度控制:
  • 度量分析
  • 2 从迭代看架构
  • 第一次作业
  • 第二次作业
  • 第三次作业
  • 虚空迭代
  • 3 性能优化
  • 4 BUG与HACK
  • BUG
  • HACK
  • 5 心得体会
  • 未来方向
  • 致谢

0 前言

一辆坦克,拥有更大的底盘、更大的炮塔尺寸、更通用的配件组,能让她在遥远的将来,甚至是数十年后,依然活力充沛。

本单元作业围绕表达式化简不断增加难度,一上来就给我整了点小小的OO震撼。本次作业学到的最重要的两点就是可拓展性设计与复杂度控制,以及最大的教训:不要相信后人的智慧

1 从整体看架构

首先上一张UML图(仅展示重要的方法)

img

最终架构为基本项架构,看起来非常简单对吧,是的,它确实非常简单,越简单的东西越不容易坏。

  • Unit管理着基本项
  • Expr拥有一个Arraylist管理Unit
  • Factor指导新的Factor应该如何融入这个架构。
  • Function作为一个工具类专门做代换工作。
  • Parser和Lexer不再赘述。

A.可拓展性分析:

经历了hw1需要remake的惨痛教训后充分设计了可拓展性。

  • 添加新的Factor只需要实现Factor指出的功能,包括运算功能和合并功能两部分,并且在Unit中对它进行拓展屌用,即可融入到架构中。
    • 图中Factor里,黑点标出为运算方法,绿点标出为化简方法,只需要重点做这两类即可。
  • 修改Factor,比如添加xyz新变量、添加abc新常数,只需要在底层Factor修改即可,并不需要修改顶层。
  • 添加新的代换,只需要在Function工具类中添加即可。

B.复杂度控制:

Function与其它因子有本质性区别,单独将其功能提出

  • 目前的Parser复杂度已经很高了
  • 与其它因子相比,它区别很大:它只需要做好代换,不需要合并也不需要运算,更不需要全过程存储

因此,决定将其提出,并且在进Parser之前就完成代换工作。用一张图来展示它是如何构建的:

img

用了一个小递归(规模非常小)来轻松实现传参与代换,最后返回一个代换完成的新表达式。

  • 与此同时,为了解决过程中的混乱,每次添加新因子或进行乘法后,就立刻进行Simplify,整理一遍Expr中的Unit们,减小了计算时的逻辑复杂度。

度量分析

img

  • 认知复杂度(CogC)展示理解难度。图中为排名前4的方法。
    • 排名最靠前的是关于输出的和性能优化的方法。一是因为做了不少优化,二是因为设计了一些复杂逻辑控制来减小输出所用代码行数。因为本身蕴含着太多的逻辑,所以难以理解。
    • 排名第二靠前的是Parser,因为Parser决定着到底匹配哪种因子,所以复杂度会变得很高。而且本人并未对其做任何优化,所有的匹配都挤在那三层Parser中,其实是可以拆出来的。
  • 圈复杂度(v(G)) 展示可测性,也就是需要多少测试才能覆盖。显而易见,它越高,测bug越不易,出问题概率越高。上面的print逻辑太多,分支也很多,出错概率就很高,第二次强测出问题的地方也正是在此。后续应该对这些复杂的逻辑多写一些注释,从注释来跟踪自己代码的逻辑意图。

2 从迭代看架构

第一次作业

首先就是惨痛的第一次迭代。第一次作业制作了一个简单的hashMap<指数,系数>的存储结构:

            if (terms.containsKey(key)) {
                terms.put(key, (sign.multiply(value)).add(terms.get(key)));
            } else {
                terms.put(key, sign.multiply(value));
            }

这个架构最大的好处就是完全不用设计化简结构,直接用hashMap的一对一的特性完成化简。事实证明这个架构几乎完全不能拓展,它最大的好处变成了最大的劣势,存储结构倍锁死,完全无法扩展到新的因子。

解决方案——诶,大半remake。

第二次作业

在橘子bro助教的大力帮助下完成了对存储结构和化简方法部分的remake,也就有了本次作业的定型架构。

考虑到hw1拓展性的吃席,新的架构直接狂点拓展性。

  • 首先是用一个名为Unit的基本项管理各种Factor,这样Expr看到的永远是一堆Unit,进行运算也只会调用Unit的方法,将两层之间完全隔开,方便拓展。
  • 第二是将Factor理应具有的功能全部写进接口,用接口来指导新的因子的加入。
  • 并且用各种Factor而非直接的BigInteger之流来让Unit管理,这样方便因子本身的拓展,比如xyz的加入。

对于化简方法的重构,选择了每次新加入Unit时就进行化简,保证Expr管理的始终是一个最简表达式。通过对Arraylist的两两遍历完成对是否可以合并的判断。为了可读性,这里返回一条消息(是的,就直接返回一条字符串,我自己都觉得抽象),告诉合并器到底是能不能合并,以及合并后是不是0.

值得一提的时判断两个表达式是否相等可以直接:

                for (Unit unit : ((Expr) o).getFactors()) {
                    if (!this.factorList.contains(unit)) {
                        return false;
                    }
                }

因为contains会去调用equals方法,而对于Unit和因子的equals,我们已经完成了重写,这样它就会按着我们的意图走。

这次作业由于重构花费了不少时间,但是exp的轻松添加以及第三次作业的轻松完成都说明了这个REMAKE是值得的。

第三次作业

基本没有动架构

  • 函数定义的嵌套——只需要在读函数表达式时也调用工具类Function即可。
  • 求导——我将求导视为和加、乘法一样的运算方法,让expr一层层往下调用即可。

如果有一个好的架构的话,本次作业应该在一小时内就可以完成。

虚空迭代

  • 新的因子:假如出现了sin,cos,本架构只需添加新的sin,cos类,并且实现Factor中规定的运算方法和合并方法,就能够添加进Unit像其它因子一样管理。在Unit中略加修改使得它能够适应带sin,cos的运算即可完成后端。最后,只需要修改Parser和Lexer使得它能够读入这种因子,并且设计针对sin,cos的新的打印方法即可。
  • 旧有因子扩展:假如出现xyz多种变量,只需要修改varFactor内部,放入一个HashMap或者两个Arraylist进行存储,并且在本类中修改乘法等方法即可完成,将修改复杂度限制在最低。

3 性能优化

性能优化我有两不做,性能太好的我不做,因为太累;性能太差的我不做,因为没用。

于是在考虑了性价比之后选择了进行因子判断去括号,以及公因数提取。

贴合基本项的架构选择用基本项的size来判断是否是因子:

    public boolean isFactor() {
        int size = 0;
        if (!this.getCoefficient().getNum().equals(BigInteger.ONE))
            size++;
        if (this.getVarFactor().getExponent().compareTo(BigInteger.ZERO) > 0)
            size++;
        if (!this.getExpFactor().getExpr().getFactors().isEmpty())
            size++;
        return (size <= 1);
    }

与此同时,利用提取公因数的方法进行exp内部的进一步化简,不必担心tle,因为BigInteger自带的gcd方法是辗转相除,时间复杂度为o(1),相比我们其它地方的耗时简直九牛一毛。为了防止负优化,我选择计算提取前后的可变长度,因为x和exp不管怎么都不会变短的,只需要管系数、*号、括号以及^即可,计算方法如下:

x,e的项:可变长度 = 系数length + 1 
常数: 可变长度 = length
if(只有一个基本项 && 该基本项是因子) 
then 可变长度 + 0 
else 可变长度 + 2

通过比较即可决定是否提取。

4 BUG与HACK

BUG

  • 在写的时候因为深浅克隆搞出过不少乱子,尤其是addAll的浅克隆,一定要多加注意。

  • 三次作业出现的bug共计1处,主要原因为将exp((-x))中的-x误当作因子。至于为什么有评测机也没测出来,是因为当时评测机没做完,只做了正确性检测,压根没测文法,于是就开席了,下次做评测机一定设计好工期(XD)。

  • hw3在hack时发现很多同学TLE,主要是因为:exp的cost非常非常小,非常令人费解。以至于第三次作业很多同学直接TLE,甚至overflow,可能是因为存储和运算结构没设计好吧。

HACK

每次都被分到铜墙铁壁房,不太走运,HACK体验不太妙,但是做评测机确实是一个很有意思的过程。

自hw2开始与LTC同学合作完成开源高性能自动化评测机。

  • 针对递归数据生成经常超cost甚至会把python本身干爆的情况,在进行过程中cost测算的同时,选择用随机数加经典概率模型控制生成表达式的概率即可(因为是递归回来才知道cost,所以难免爆cost,最后重新生成即可)
  • 针对无法测tle的情况,我们选择了新开子进程,并且在外部做计时的方法进行测试。在这种思路下,我们甚至能够做异步多进程评测(如下就是双进程评测),每次生成多组数据分别展开评测,成倍提升评测效率,2进程已经能够250s测完8人程序1000组数据。
with multiprocessing.Pool(processes=2) as pool:
        async_result1 = pool.apply_async(compare, (jar_names, checker,))
        async_result2 = pool.apply_async(compare, (jar_names, checker,))
        try:
            isSame1,stdin1 = async_result1.get(timeout)
            isSame2,stdin2 = async_result2.get(timeout)
  • 针对数据覆盖不足,可以手动写一些抽象数据放进评测机的specialData区,每次先生成这些东西再去随机生成,比如1-1,dx(exp(exp(exp(exp(exp(exp(exp(x^2))))))))
  • 针对评测太慢的情况,我的建议是上linux,实测有5-10倍的性能提升。

5 心得体会

最大的教训:不能相信后人的智慧!!!

HW1偷懒快速完成作业时得意洋洋地说相信后人的智慧,结果几天后就吃到了回旋镖。

设计程序时一定要充分设想可拓展性,课程组用两个作业让我充分记住了这一点,HW1到2很难说不是精心设计过的,很棒的作业,给我带来了印象深刻的经验教训。痛定思痛设计出的HW2架构具有非常良好的可扩展性以及非常简单的总体结构,可以说是是非常令我自我满意了,但是细节上可以优化的还很多,期待下次作业写出更好的架构。

复杂度控制的设计更像是给代码结构减负,防止承担太多混乱工作,避免快速成为石山代码。本次作业虽然没有因此吃瘪,但是在之后越来越复杂的代码中,还是要尤其注意这一点。

未来方向

很喜欢课程组HW1到HW2的过渡,但是建议课程组平衡一下HW2和HW3的难度,这两个工作量有点过于不均了。

致谢

  • 感谢橘子bro学长对于架构的指导

  • 感谢LTC同学主导设计的评测机

  • 感谢课程组精心设计的迭代过程

...全文
111 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
内容概要:本文围绕基于三重移相控制(TPS)的双有源桥(DAB)高频隔离DC-DC变换器开展系统研究,重点构建了其在Simulink环境下的高精度仿真模型。研究全面涵盖SPS单相移相、DPS双重重移相与TPS三重移相等多种控制策略的建模、实现与能对比,深入分析不同模式下变换器的功率传输特、软开关实现条件及功率回流问题,旨在提升DAB在交直流混合微电网、能量路由器、多端口柔互联装置等场景中的转换效率与动态响应能力。通过对ZVS(零电压切换)条件的精确控制与移相角参数的优化,有效降低了开关损耗,增强了系统整体能效与运行稳定。该仿真模型具有良好的可扩展,适用于复杂电能转换系统的科研验证与工程开发。; 适合人群:电力电子、电气工程及其自动化等相关专业的硕士研究生、博士生、科研人员以及从事新能源变换器、柔输配电系统设计的工程技术人员。; 使用场景及目标:①掌握双有源桥DAB变换器的基本工作原理及其在高频隔离场合的核心优势;②深入理解三重移相控制策略的设计机理、控制自由度分配及其在效率优化中的关键作用;③构建并调试可用于科研论文撰写、项目申报或实际系统验证的高保真Simulink仿真模型,支撑理论分析与实验对比。; 阅读建议:建议结合MATLAB/Simulink平台进行动手实践,重点关注主电路拓扑搭建、移相控制模块设计、驱动信号时序配置及ZVS实现条件的仿真观测,推荐通过对比SPS、DPS与TPS三种模式的稳态与动态响应曲线,深入掌握各控制策略的适用边界与优化方向。
【重要提示】本资源设置为0积分下载,若非0积分请勿轻易下载 亲爱的CSDN用户: 首先感谢你点进这个资源页面。我需要提前说明一个重要情况: 本资源原本已设置为“0积分下载”,即作者希望完全免费共享。但CSDN平台有时会根据文件的下载热度、文件大小、用户权限等因素,自动将部分资源的积分调整为非0数值(如1积分、2积分、5积分等)。这是平台系统的自动行为,而非作者本人的设定。 因此,如果你当前看到该资源的下载所需积分不是0(例如显示为1、2、3……),请谨慎决定是否下载。 如果你按照非0积分支付并下载后发现资源内容不符合预期、链接失效,或者实际上该资源本应是免费的,作者无法为此承担积分损失或退还操作。强烈建议:仅在页面显示为0积分时进行下载。 另外,本资源描述中并未直接提供具体的下载地址或外部链接,因为它本身是一个通过CSDN官方上传通道提交的文件/内容包。如果你看到描述中没有外部网盘地址,这是正常的——资源文件应通过CSDN内置的“下载”按钮获取。若因平台积分显示异常导致你支付了积分,请优先联系CSDN客服咨询积分退还政策,作者没有权限修改平台自动设定的积分值。 感谢你的理解与支持。技术分享本应开放,但受限于平台规则,特此提醒如上。祝学习进步!

301

社区成员

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

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