OO-2026 Unit.1 总结
OO Unit 1
1. 架构设计
1. 表达式的解析
Lexer 类:词法分析器
- 输入为字符串,去除空白字符后逐个解析。
- 支持识别:
- 数字
- 变量:
x、y - 运算符:
+ - * ^ ( ) - 关键字:
exp、dx、dy、grad - 函数记号:
f() f{n}() - 条件表达式符号:
[ ] ? :
- 提供
next()和peek()方法供解析器使用。
Parser 类:语法分析器
采用递归下降的方法,依据文法调用不同的
parse方法。各种
parse方法将 token 流按照语法规则解析为Polynomial对象。支持的语法结构:表达式(加减)、项(乘)、因子(数字、幂函数、表达式、指数函数、自定义函数、选择式、求导)
2. 表达式的处理
Polynomial 类:多项式类,内部使用 HashMap<Mono, BigInteger>,键为单项式,值为系数。
提供:
- 基本运算:
Add、Subtract、Multiply、Power、Divide(除数为常数) - 复合:
Substitute(Polynomial p)将变量x替换为多项式p,用于表达式函数调用 - 求导:
DriveX()、DriveY() - 输出:
print()自动处理系数、指数、exp嵌套
- 基本运算:
值得一提的设计:
在构造函数中删除系数为 $0$ 的项,然后所有运算方法按如下设计,便可以保证任意时刻均不会出现系数为 $0$ 的项,无需额外考虑。
public Polynomial(HashMap<Mono, BigInteger> coefficients) { this.coefficients = coefficients; Iterator<Map.Entry<Mono, BigInteger>> iterator = this.coefficients.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Mono, BigInteger> entry = iterator.next(); if (entry.getValue().compareTo(BigInteger.ZERO) == 0) { iterator.remove(); } } } public Polynomial Add(Polynomial x) { HashMap<Mono, BigInteger> results = new HashMap<>(this.coefficients); for (Map.Entry<Mono, BigInteger> entry : x.coefficients.entrySet()) { results.put(entry.getKey(), results.getOrDefault( entry.getKey(), BigInteger.ZERO).add(entry.getValue())); } return new Polynomial(results); }
Mono 类:单项式类,表示形式:x^a * y^b * exp(Poly),其中 exp(Poly) 表示指数函数,参数为多项式。
- 提供:
- 乘法:
Multiply(Mono) - 复合:
Substitute(Polynomial) - 求导:
DriveX()、DriveY()(使用链式法则) - 输出:
print()配合系数输出
- 乘法:
Function,RecFunction 类:函数类,用于存储和调用函数。
3. 架构设计的评价
类图

代码规模

可以看出,代码的主要部分为 parser 的解析部分与 Polynomial,Mono 的计算部分。
复杂度

可以看出,由于实现的类较少,每个类 WMC 较大。

print函数由于考虑了各种输出优化,故认知复杂度较高。lexer.next函数需要识别各种 token,分支较多,故认知复杂度较高。- 大部分方法的 ev 不高(next 方法除外,需要设置全局 token),说明代码较好的实现了封装要求。
总结
- 我本单元作业的架构可能与大部分同学的架构有所差异:我并没有实现
Factor接口,对每种因子新建一个类进行存储和管理,而是选择将所有因子都以多项式的结果进行存储和管理。这直接导致我在本单元中仅使用较少的类,而在多项式类中方法繁多。我认为两种架构各自有其可取之处:- 对于使用
Factor接口的架构,其能清晰的表征每种因子的结构,通过接口方法实现统一管理和计算。同时其将解析和运算两个步骤分离,实现功能解耦,能更好的支持选择因子中的短路要求。 - 对于采用多项式存储的架构,其破坏了因子的原始结构,但是天然支持统一管理和计算。但其将解析和运算同步执行,实现选择式因子时会引入额外的限制,破坏代码的一致性。
- 对于使用
- 在架构设计中有一个纠结的点,我认为函数的定义解析并不应该放在
parser类中,但是函数的定义解析需要同时用到parser类和lexer类的方法,而且使用的是同一个 lexer,所以最后还是将函数的定义解析放在parser类中,函数类只实现了函数的存储和调用,这种功能分离的架构实在有些奇怪。
2. 迭代过程
1. 第一次迭代
通过对数学意义上的表达式结构进行建模,完成单变量多项式的括号展开,初步体会层次化设计的思想的应用和工程实现。
- 使用
lexer和parser类解析表达式,之后的迭代中添加新的功能,也需要在lexer类内部添加新的关键字,parser类内部添加新的解析方法,后面不再赘述。 parser类使用递归下降的方法解析表达式,所有解析方法的返回值均为多项式,最后直接调用多项式的输出方法。- 此时尚未实现单项式类,多项式采用二元组 $(k,c_k)$ 方式表示 $c_k x^k$,使用 HashMap 维护。
2. 第二次迭代
通过对数学意义上的表达式结构进行建模,完成包含指数函数、自定义函数和新增逻辑判断结构的多项式展开与化简,进一步体会层次化设计的思想,并初步接触符号计算中的等价性判定问题。
- 指数函数:需要新增单项式类,多项式类需要重构。单项式类采用 $x^a exp(poly)$ 方式存储。单项式和多项式都需要支持一些基本的运算。输出逻辑中也涉及到单项式和多项式的相互调用。
- 逻辑判断结构:笔者认为这个结构反映的是代码的一个重要功能,即判断两个单项式、多项式的等价关系。这个功能不仅用于逻辑判断结构,也可以帮助我们合并同类项,优化表达式长度。我采用重写
equals和hash方法从而实现判等,同时在代码中很小心的删除 0 系数项,避免其带来影响。在强测阶段因为未短路未选择分支而产生计算冗余,而笔者的架构中并未实现解析与计算分离,而是解析后立即返回多项式结果。为解决该问题只能采取一个下策,在parser方法中传入参数calc表示是否需要计算,如果不需要就只进行形式上的解析占位,然后返回空多项式。 - 自定义函数:实现自定义函数类,解析时调用
parser类的方法,将得到的多项式结果存储下来。多项式和单项式类实现substitude方法 ,调用函数时将因子代入多项式即可。
3. 第三次迭代
通过对数学意义上的表达式结构进行建模,完成包含求导运算、递推函数等复杂结构的多变量表达式的展开与化简,深度体会层次化设计的思想及其在复杂问题中的应用。
- 求导运算:之前的架构能实现将所有因子转换为多项式,于是只需要在多项式类、单项式类实现求导方法,相互调用即可支持求导运算。
- 多变量:单项式类改为 $x^ay^bexp(poly)$ ,修改单项式的所有方法即可,注意不要遗漏。
- 递推函数:递推函数与自定义函数并无太大差异,新增一个
recFunction类用于存储和查询即可。
总体而言,第三次迭代并没有新增较为复杂的功能,只需在之前的架构上稍加修改即可。这也是我耗时最短的一次作业。
4. 可能的迭代场景
- 新增对数函数,处理方法同指数函数。
- 支持虚数运算,可能需要实现虚数类,然后替换原来使用的
BigInteger类 。 - 调整指数函数的底数,比如可以设置为 $2^{\text{factor}},4^{\text{factor}}$ 。这可能需要将 Mono 结构中的指数部分改为用 HashMap 存储。不仅如此,我们可以通过变换底数来缩短表达式的长度,自然的引入新的优化问题。
3. bug 分析
在 hw1 中我曾尝试搭建评测机进行代码测试,但发现随机数据并不能有效找出代码在边界情况下的错误,效率较低,因此在后续的作业中放弃了此种做法。
在后续的作业中,我的 hack 思路主要是先结合自己在编写代码过程中认为可能出现的问题(比如表达式等价关系判断)构造一些数据。此外我也会阅读代码容易产生问题的地方(比如输出函数exp括号的问题),针对此构造数据。
hw1
- 我在本地测试中发现,在多项式类的实现中,每次运算结束我会删除系数为 0 的项,以优化运算性能。但在输出时忽略了在多项式为空时需要输出 $0$ 这个问题。这个问题与架构设计无关,而是没有考虑一些边界情况的特殊处理。
- 在互测中未发现其他问题。
hw2
- 在选择式因子中,我的代码没有忽略未选择分支的求值,从而产生巨大的性能损失。
- 在互测的过程中发现部分同学的代码不能正确的判断表达式的等价关系(比如 $exp(0)$ 和 $1$),我认为这可能是没有忽略系数为 $0$ 的项,从而导致 hash 失效。
hw3
- 在互测中发现部分同学在迭代过程中没有考虑所有受到影响的方法,比如有一位同学会输出
exp(x*y),这明显是没有考虑到多变量加入对因子判定的影响。 - 此外,发现部分同学优化没有控制复杂度,针对性的构造了
exp嵌套的数据。
4. 优化策略
- 输出时比较常见的优化,比如 $x^1$ 省略指数为 $x$,系数为 $1$ 的项省略系数,移除 $0$ 项
- 输出多项式时,将正项放在前面可以减少一个字符(‘-’)的长度。这在 hw1 可能不太起眼,但是在后面的作业中多项式层层嵌套,性能可以优化不少。事实上,即使满足正确性要求,hw1 中不添加该优化会被发配到 B 房...
- $\text{exp(Poly)}$ 可以写成 $\text{exp(Poly/c)}^c$,即提取系数的公因数,这里我只考虑了最大公因数。有同学证明实际上 gcd/1,....10 这十个数都需要才能得到最优的结果。
- 由于没有进一步实现因式分解等较为复杂的优化,代码的简洁性与正确性未受到较大影响。
5. 大模型相关
本单元作业中主要在检查代码正确性的方面使用了大模型工具,其余部分(代码编写,性能优化)均由自己完成,具体而言:
在 hw1 中使用大模型工具生成评测工具,进行测试。该评测机只能实现数据的纯随机生成,测评结果并不令人满意。
在完成作业后和互测过程中,使用大模型工具分析代码中的错误。大模型很容易找到输出格式的各种错误(我认为这些都是代码编写过程中产生的细节问题,比如结果为0时输出空字符串),也能找到性能方面的问题(比如提供cost计算方法,其能发现选择表达式未短路造成的性能影响)。当然,其结果不一定准确,也会产生一些错误的分析过程。
总体而言,我认为大模型已经能较好的解决目前作业的各种要求。
6. 心得体会
- 代码的鲁棒性:本单元作业保证了输入数据合法,因而无需过多考虑。但在实际开发过程中,数据的正确性是无法保证的,因而需要加以考虑。此外,从测试的角度出发,应该关注其在各种边界情况下代码的稳定性。
- 代码架构与迭代:在体系开发过程中,各个类存在复杂的依赖关系,在迭代开发的过程中一定要完整考虑对于各种类,各种方法的影响。这也同时体现了架构的重要性,一个易于扩展的架构需要做到的就是在迭代的过程中修改尽可能少的代码就能兼容新的代码。
- 优化要适度:在本单元的作业中我仅实现了一些简单的性能优化,故在 hw1,hw3 中均未被hack。在互测的过程中,我发现那些优化的部分往往会引入各种各样的问题,很容易就被 hack。因而在追求优化性能的同时也要考虑其带来的潜在风险。
7. 未来方向
这里吐槽一下,第一次实验课代码解析表达式的方式是通过加减号分隔处理,似乎并没有采用递归下降的方法。我在 hw1 中也采用了类似的方式,但发现其并不能很好的处理多层嵌套的复杂表达式,只能被迫重构代码。感觉可以替换成更标准的递归下降的形式,给出一个大致的架构方便同学们学习。
思考题
- 检查输入是否符合要求:严格检查 lexer 提供的 token 是否符合预期。对于空白符的处理,可以实现一个函数跳过可能的空白符。
- 计算合法输入的 cost:类似 toPoly 方法实现一个计算 cost 的函数,不需要进行多项式运算。
相关推荐
弱电网下光伏并网逆变器序阻抗建模仿真复现扫频法【序阻抗判断稳定性】
内容概要:本文聚焦于弱电网条件下光伏并网逆变器的序阻抗建模与稳定性分析,系统阐述了基于扫频法的阻抗特性仿真与复现方法。通过构建正负序阻抗模型,深入剖析逆变器在弱电网环境中的动态响应特性,结合锁相环、电流环等关键控制环节对系统阻抗的影响机制,采用序阻抗稳定性判据评估多机并网或复杂电网条件下的系统稳定裕度。研究在Simulink平台中实现激励信号注入、频率响应采集与阻抗曲线绘制,为高比例新能源接入背景下并网系统的稳定性分析提供了可靠的仿真手段与理论支撑,尤其适用于SCI级别论文成果的复现与拓展验证。; 适合人群:电力电子、新能源并网技术及相关领域的硕士/博士研究生、科研人员,以及从事光伏逆变器、微电网稳定控制开发的工程技术人员。; 使用场景及目标:①掌握扫频法在电力电子装置阻抗建模中的具体实施流程;②理解并应用基于序阻抗的稳定性判据进行系统稳定边界分析;③用于高水平学术论文(如SCI期刊)中阻抗建模与稳定性分析部分的仿真复现与方法验证; 阅读建议:建议读者结合文中所述方法与提供的参考文献,自主搭建Simulink仿真模型,逐步完成扰动信号设计、数据采集、傅里叶分析及阻抗拟合全过程,深入探究控制参数变化对系统阻抗特性和稳定性的具体影响,强化理论与实践的融合能力。
三相并网逆变器虚拟阻抗+统一有源阻尼策略SVPWM+SPWM调制仿真
内容概要:本文档系统性地介绍了三相并网逆变器的核心控制技术及其在复杂电网环境下的仿真应用,重点涵盖虚拟阻抗与统一有源阻尼策略在SVPWM和SPWM调制方式下的实现方法,旨在提升逆变器的稳定性与电能质量。内容包括无功功率扰动孤岛检测、中点电位平衡控制、低电压穿越(LVRT)、模型预测控制(MPC)、正负序分离、电网前馈控制等关键技术,并扩展至风储联合调频、微电网下垂控制、虚拟同步发电机(VSG)、构网型逆变器等多种典型电力系统应用场景。所有技术均配有基于Simulink/Matlab平台的完整仿真模型、参考文献及说明文档,强调理论分析与仿真实践的深度融合,突出科研中“借力”与创新思维的重要性。; 适合人群:电气工程、自动化、新能源等相关专业的研究生、科研人员及从事电力电子与可再生能源系统开发的工程师;具备MATLAB/Simulink仿真基础和电力系统专业知识的技术人员。; 使用场景及目标:①开展三相并网逆变器控制策略的研究与优化设计;②复现高水平学术论文中的控制算法;③完成毕业课题、科研项目或工程仿真验证;④掌握虚拟阻抗、有源阻尼、SVPWM/SPWM调制、MPC、VSG等核心技术的建模与实现方法;⑤支撑微电网、构网型变流器等前沿方向的科研攻关。; 阅读建议:建议按照资源目录结构化学习,优先选择与自身研究方向契合的内容模块,结合提供的仿真模型、参考文献和说明材料进行实操演练,注重从理论推导到仿真实现的全过程理解,充分利用共享网盘资源深化技术掌握,提升科研效率与创新能力。
chromedriver-win32-159.0.8133.0(Canary).zip
chromedriver-win32-159.0.8133.0(Canary).zip
计算机“101计划”《人工智能引论》教学大纲+课件+实训案例
计算机“101计划”《人工智能引论》教学大纲+课件+实训案例
语音输入法离线语音输入转文字工具2.0支持双档本地识别模型,中英混说精准转写,纯离线运行保护隐私,让打字更高效.zip
大家好,我是大飞哥。日常打字时,频繁切换输入法、断网无法语音转写、或担心语音数据被传至云端,是许多办公族和内容创作者的共同痛点。这款离线语音输入工具 ,正是为此量身打造的 Windows 桌面语音辅助利器。它的核心使命就是按住快捷键说话,松开文字自动上屏,全程本地离线识别,让您在任何可输入文字的窗口内都能实现高效、私密的语音输入,彻底告别网络依赖与隐私焦虑。 在核心功能上,软件内置了两档本地语音识别模型,通过主界面下拉框即可随时切换。默认的“离线·标准”模式响应极快,支持中英混说且自带标点,非常适合日常聊天与文档撰写;而“离线·精准”模式则对纯中文、方言口音及长段落口述的识别准确度更高。更贴心的是,它具备实时预览与一键取消机制,说话过程中若发现说错,按下 Esc 即可取消,一个字都不会误输入;若目标窗口不支持直接写入,文字会自动存入剪贴板,方便您按 Ctrl+V 粘贴,确保每次发声都不白费。 这款工具在具体场景中的表现十分亮眼。对于办公白领,在填写表格、撰写邮件或使用微信、QQ 沟通时,无需频繁切换窗口,按住右键说话即可快速完成信息录入;对于程序员与文字工作者,在代码编辑器或文档中口述注释与段落,能显著提升创作效率;对于经常出差的商务人士,在飞机、高铁等断网环境下,它依然能稳定工作,不经过任何服务器,真正做到了“说了什么只有自己知道”,私密性极佳。 使用体验方面,软件将极简操作贯彻到底。解压后双击 大飞哥语音输入,右下角托盘出现蓝色话筒图标即代表准备就绪。用户只需将光标点进目标输入框,按住右 Alt说话,说完松开,文字便会自动写入。主界面提供了设置快捷键与开机自动启动选项,方便用户按习惯自定义组合键;关闭主窗口并不会退出程序,而是缩至托盘持续待命,彻底退出只需右键托盘或点击主界面按钮,逻辑清晰,零门槛上手。


