惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Hacker News
The Hacker News
F
Full Disclosure
Cloudbric
Cloudbric
Blog — PlanetScale
Blog — PlanetScale
W
WeLiveSecurity
N
News and Events Feed by Topic
T
Troy Hunt's Blog
V2EX - 技术
V2EX - 技术
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
B
Blog
GbyAI
GbyAI
C
Check Point Blog
B
Blog RSS Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Recorded Future
Recorded Future
The Last Watchdog
The Last Watchdog
N
News and Events Feed by Topic
T
The Blog of Author Tim Ferriss
O
OpenAI News
V
V2EX
人人都是产品经理
人人都是产品经理
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
WordPress大学
WordPress大学
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Security @ Cisco Blogs
C
Cisco Blogs
Security Latest
Security Latest
S
Security Affairs
V
Visual Studio Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Azure Blog
Microsoft Azure Blog
Last Week in AI
Last Week in AI
AWS News Blog
AWS News Blog
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
PCI Perspectives
PCI Perspectives
博客园_首页
U
Unit 42
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
L
LINUX DO - 热门话题
H
Hacker News: Front Page

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
数字电路实验BLog
xaioka · 2026-06-21 · via 博客园_首页

前言
在完成航班配载系统初次实验后,我自认为已经掌握面向对象程序设计的基础思路,然而数字电路逻辑仿真系统实验,让我对软件架构设计产生了全新、系统的认知。
本次实验区别于前次对现实物理载体的简单建模与数值计算,核心任务为解析结构化文本输入,在内存中构建完整电路拓扑模型。系统需要兼容多种基础逻辑门,包含与门、或门、非门,同时支持多路选择器、译码器等具备多输入、多输出端口的复合功能器件,最终按照信号依赖关系逐级求解全部引脚电平并规范输出运算结果。
两次实验存在明显的设计层级差异:航班配载系统偏向具象实体建模,仅需对客观实物封装属性与计算逻辑;数字电路仿真系统则要求对硬件电路信号传递关系进行抽象建模。开发全过程分为多个迭代阶段:初期采用数组容器存储器件数据,存在查询效率低下、电路构建逻辑臃肿等问题;后续更换哈希映射结构管理电路节点以优化查找性能;初始线性遍历计算会出现信号时序错乱问题,通过引入基于入度的广度优先拓扑排序解决信号依赖运算顺序;依托抽象类、继承与运行时多态机制完成基础门电路向复合芯片的功能拓展。整个开发过程不只是代码编写实践,更是对面向对象三大核心特性综合实践,让我形成了更为规范化的软件设计思维。
一、核心知识点梳理与实践应用
(一)继承与多态:通过抽象层统一规范器件行为
抽象基类
项目开发初期,我曾考虑为每一类逻辑器件独立编写完整实体类,不抽取公共父类。但编码过程中发现,所有门电路、复合芯片存在大量共用属性:唯一器件编号、拓扑排序所需入度值、控制引脚集合、数据输入引脚集合、输出引脚集合、下游关联器件列表。若在每个子类中重复定义上述成员变量,会造成大量代码冗余,后期维护、新增器件的成本大幅提升。
基于该问题,我设计两个顶层抽象类:CircuitElement 电路器件基类、Pin 引脚基类。在CircuitElement中封装全部器件通用属性,同时定义抽象计算方法public abstract void Calculate()。该抽象方法强制所有子类独立实现自身信号运算规则,形成统一调用标准。后续新增多路选择器、译码器等器件时,无需修改顶层调度核心代码,仅新建子类继承抽象基类并重写计算逻辑即可完成功能拓展,直观体现软件设计开闭原则,即对功能扩展开放、对原有核心代码修改关闭。
运行时多态降低模块耦合度
多态机制是电路调度模块的核心支撑。拓扑排序的主循环逻辑如下:

while (!queue.isEmpty()) {
CircuitElement element = queue.poll();
element.Calculate();
// 更新后继器件入度、传递输出电平
}
队列中存储的数据类型统一为抽象父类引用,但程序运行阶段,虚拟机会自动识别引用指向的具体器件子类对象,执行对应器件独有的计算逻辑。调度模块无需区分当前处理对象是基础逻辑门还是复合芯片,仅需按照拓扑序列依次触发计算流程。调度逻辑与各类器件的运算逻辑完全解耦,模块边界清晰,代码可维护性显著提升。
(二)数据结构选型:基于业务场景选择容器
哈希映射替代线性列表优化查询效率
项目首个版本采用ArrayList存储全部电路器件。解析连线关系时,若需要根据器件编号查找上游信号源,必须遍历整个列表完成匹配,查询时间复杂度为 O (N)。小规模输入下性能差异不明显,当电路器件数量增多、连线关系复杂时,反复遍历会造成程序运行卡顿。
重构阶段将器件存储容器更换为HashMap<String, CircuitElement>,以器件唯一编号作为键、器件实例作为值存储。通过get()方法可直接根据编号定位目标器件,单次查询时间复杂度降至 O (1),不仅解决运行效率问题,器件关联构建的代码逻辑也更加简洁清晰。
有序 TreeMap 保障位运算引脚顺序稳定
多路选择器、译码器等复合器件的输出索引由输入引脚电平组合转换为二进制数值得到,引脚排列顺序会直接影响位运算结果。开发初期使用无序 HashMap 存储输入引脚,容器遍历顺序随机,相同输入条件下多次运行会产生不一致的电平输出,调试难度极大。
后续将所有参与位运算的引脚集合替换为TreeMap<Integer, InputPin>,该容器底层基于红黑树实现,会自动按照引脚编号升序排列。通过new ArrayList<>(inputlist.values())获取引脚集合时,引脚始终按 0、1、2…… 有序排列,位运算的数值计算逻辑能够保持稳定,彻底解决输出结果随机错乱的问题。
(三)图论算法:拓扑排序处理信号依赖计算时序
数字电路的图模型抽象
组合逻辑电路不存在反馈回路,器件输出端口连接另一器件输入端口的信号传递关系,可抽象为一张有向无环图(DAG)。电路运算存在严格先后依赖:某器件所有上游输入信号完成计算后,才能执行自身电平求解。若按照随机顺序遍历计算,未赋值的输入引脚会读取空值,运算结果完全失效。
基于入度的 BFS 拓扑排序落地流程
为规范信号计算顺序,采用统计节点入度的广度优先拓扑排序算法,完整流程分为三步:
(1)统一统计入度:文本解析、完整构建电路拓扑后,批量遍历所有器件,统计每个器件输入引脚、控制引脚对应的上游信号源数量,数值即为当前节点入度;
(2)初始化待计算队列:将入度为 0 的器件(仅外接外部输入信号,无前置依赖)存入队列;
(3)逐级传递信号:从队列取出器件执行电平计算,遍历其所有下游关联器件,将当前器件输出电平赋值给对应输入引脚,下游器件入度自减 1;若入度归零,代表全部输入信号就绪,加入队列等待运算。
该算法能够保证任意器件仅在全部前置信号计算完成后执行运算,是整个仿真系统的核心调度逻辑。
(四)文本输入解析:正则表达式标准化格式匹配
结构化文本的正则匹配解析规则
实验给定输入文本格式复杂度较高,例如标识A(2)12-1代表 2 输入端与门、器件编号 12、当前连线对接器件 1 号引脚。若依靠字符串分割、多层条件判断拆分字段,代码分支繁多、可读性差。最终采用正则表达式统一匹配全部器件标识:
String pattern = "([AONXYMSZF])(\(\d+\))?(\d+)\-(\d+)";
借助Pattern与Matcher类分组捕获,一次性提取器件类型、输入端口数量、器件编号、目标引脚编号四类关键信息,大幅简化解析分支逻辑。
预创建占位节点避免拓扑图断裂
解析连线关系时存在时序问题:连线语句会提前引用尚未定义的上游器件,直接读取映射表会返回空对象,造成电路拓扑断裂。为维持图结构完整性,增加防御性处理逻辑:
CircuitElement source = elementmap.get(elementid);
if (source == null) {
source = createElement(elementType, elementid, K);
if (source != null)
elementmap.put(elementid, source);
}
当引用的上游器件未完成定义时,先初始化空白器件实例存入映射表作为占位节点,保证电路连线关系完整;拓扑排序阶段空白节点因无有效输入信号,会自然完成兼容处理,不会中断整体运算流程。
二、实验题量与整体难度分析
本次实验综合难度、开发工作量远高于航班配载实验。仅梳理多路选择器、译码器的真值表规则、引脚定义规范就需要花费大量时间,完整开发调试总时长超过十小时,调试占用绝大多数时间。电路信号具备级联传递特性,单个引脚计算出错会导致下游全部器件电平异常,仅通过输出结果无法快速定位故障点,需要逐行对照真值表排查每一级运算逻辑,调试流程繁琐。
本次开发深刻印证软件项目前期架构设计的重要性。若开发初期未抽象顶层器件父类、未区分控制引脚与数据输入引脚,后期拓展复合芯片时,原有底层存储、调度代码几乎需要全部重构。本次实验完成了硬件电路逻辑向面向对象图模型的完整转化,同时掌握依托图论算法解决业务依赖时序问题的设计思路。
三、系统架构设计迭代分析
阶段一:线性列表存储的初步开发版本
首个开发版本缺少系统化图模型设计,统一使用ArrayList存储全部器件,存在多处设计缺陷:
运行性能不足:电路连线较多时,根据编号查找上游器件需要线性遍历容器,运行效率偏低;
引脚分类缺失:所有引脚统一存入单一列表,未区分控制引脚、数据输入引脚、输出引脚,无法适配多路选择器等控制端与数据端分离的复合器件;
多态机制利用不足:虽然搭建了基础继承结构,但计算方法内存在大量硬编码逻辑,信号传递耦合严重,新增器件的拓展成本极高。
阶段二:重构后哈希映射 + 拓扑排序标准化架构
针对初版架构缺陷完成全量重构,从数据存储、调度算法、运算逻辑三方面优化:
存储容器全面升级
采用 HashMap 管理全局器件,解决编号查询效率问题;引脚集合统一拆分控制引脚、输入引脚、输出引脚三类,全部使用 TreeMap 有序存储,同时兼容基础逻辑门与多端口复合芯片的存储需求。
拓扑排序作为统一调度核心
封装独立拓扑排序计算模块,标准化信号运算时序。新增任意类型器件时,仅需实现自身计算抽象方法,无需改动调度流程,系统拓展性大幅提升。
位运算简化复合器件逻辑
多路选择器电平求解通过位运算简化分支判断,核心实现逻辑如下:
int idx = 0;
for (int i = 0; i < ipcount; i++)
if (inputs.get(i).level)
idx |= (1 << i);
for (int i = 0; i < (1 << ipcount); i++)
oplist.put(startOut + i, new Outpin(startOut + i, i != idx));
读取控制引脚电平组合转换为十进制索引值,仅索引匹配的输出引脚输出低电平,其余引脚统一输出高电平,将硬件真值表转化为简洁位运算逻辑,减少多层 if 分支。
分层输出打印模块
独立封装打印输出模块,通过类型判断对不同器件分组输出,组内按照器件编号升序排列打印内容。当前分支判断逻辑仍存在优化空间,但可稳定完成实验要求的输出规范。
四、开发过程典型问题与解决思路
多轮迭代开发中遇到大量典型程序设计、算法适配问题,整理四类影响最大的故障与对应解决方案:
无序容器导致位运算结果不稳定
初期使用 HashMap 存储输入引脚,容器遍历顺序不固定,控制引脚电平拼接得到的索引数值随机,输出结果每次运行均不相同。更换 TreeMap 有序容器后,引脚按照编号升序读 取,位运算输入顺序固定,运算结果完全稳定。后续涉及二进制权重计算的业务场景,均优先选用有序映射容器。
空指针异常导致程序中断
拓扑排序传递电平阶段,若上游器件未完成计算、引脚未赋值,直接读取电平属性会触发空指针异常,程序直接终止。在所有信号赋值位置增加空值校验逻辑:
if (op != null) {
i.level = op.level;
}
同时规范子类计算逻辑,输入信号缺失时向输出集合存入空对象占位,通过防御性判断规避跨模块调用空指针故障,提升程序运行稳定性。
入度重复统计引发死锁
最初设计在解析单条连线时直接累加目标器件入度,若一个上游器件连接目标器件多个引脚,会重复统计入度数值,器件入度无法归零,永远无法进入运算队列,程序陷入阻塞。修改统计逻辑,待完整电路拓扑构建完成后,全局统一遍历统计全部器件入度,彻底消除重复计数问题。
过度依赖类型判断造成代码冗余
初版代码频繁使用instanceof判断器件类型,业务逻辑与具体实现高度耦合。重构后将全部器件运算行为下沉至子类重写的抽象方法,顶层调度模块仅调用统一抽象接口,实现依赖抽象、不依赖具体实现的设计规范,降低模块耦合。