C++实现银行家算法:死锁避免与资源分配实战
1. 项目概述银行家算法与操作系统的资源管理在操作系统的世界里资源管理是一个永恒的核心话题。想象一下你是一个银行家手里有一笔固定数额的资金而面前有几位客户都向你申请贷款。每个客户都有一个“最大贷款额度”的预期并且会分期来借。你的目标是在满足所有客户最终都能获得其所需贷款的前提下确保在任何时刻你手头的现金都足以应对客户们当前提出的借款请求而不会陷入“客户A等客户B还钱客户B等客户C还钱”的死锁僵局。这就是银行家算法的核心思想一个由Edsger Dijkstra提出的经典死锁避免算法。对于学习操作系统、甚至是任何涉及并发与资源分配领域的开发者而言理解银行家算法不仅是应付考试更是深入理解系统安全状态、资源分配策略和死锁预防机制的绝佳途径。它用严谨的数学模型矩阵和向量描述了多进程竞争有限资源时的系统状态并提供了一个可计算的“安全检查”算法来预判一次资源分配是否会导致系统进入不安全状态可能死锁。本次我们将不仅仅停留在理论层面而是用C从零实现一个完整的银行家算法模拟器。通过这个项目你将能直观地看到算法如何工作如何做出决策并深刻理解那些课本上略显枯燥的矩阵运算背后的实际意义。2. 算法核心原理与数据结构设计银行家算法的运作建立在几个关键概念和数据结构之上。理解这些是编码实现的前提。2.1 核心概念解析资源 (Resource)系统中可供分配的同类资源的总数。例如系统可能有3台打印机、2台扫描仪。我们用一个向量Available来表示当前可用的各类资源数量。进程 (Process)申请和使用资源的单位。每个进程在运行前会声明其所需各类资源的最大需求 (Max)。最大需求矩阵 (Max)一个n*m的矩阵n是进程数m是资源种类数。Max[i][j]表示进程i对资源j的最大需求量。分配矩阵 (Allocation)一个n*m的矩阵表示当前已经分配给每个进程的各类资源数量。需求矩阵 (Need)一个n*m的矩阵表示每个进程还需要的各类资源数量。显然Need[i][j] Max[i][j] - Allocation[i][j]。这是算法的关键它动态反映了进程的剩余需求。可用资源向量 (Available)一个长度为m的向量表示当前系统中每类资源剩余的可分配数量。算法的目标就是确保系统始终处于安全状态。安全状态是指存在一个安全序列进程的执行顺序使得即使每个进程都瞬间申请其最大需求系统仍能按此序列依次为每个进程分配所需资源并使其运行完毕释放资源从而让所有进程都顺利完成。2.2 数据结构C实现我们将使用std::vector来灵活地表示向量和矩阵以适应不同数量的进程和资源。#include iostream #include vector #include algorithm class BankerAlgorithm { private: int processCount; // 进程数 n int resourceTypeCount; // 资源种类数 m std::vectorint available; // 可用资源向量 Available std::vectorstd::vectorint max; // 最大需求矩阵 Max std::vectorstd::vectorint allocation; // 分配矩阵 Allocation std::vectorstd::vectorint need; // 需求矩阵 Need // 计算Need矩阵通常在初始化或分配后调用 void calculateNeed() { need.resize(processCount, std::vectorint(resourceTypeCount)); for (int i 0; i processCount; i) { for (int j 0; j resourceTypeCount; j) { need[i][j] max[i][j] - allocation[i][j]; // 一个基本的健壮性检查需求不应为负数 if (need[i][j] 0) { // 在实际系统中这属于严重配置错误应抛出异常或处理 std::cerr 错误进程 P i 对资源 R j 的分配数超过其最大需求 std::endl; need[i][j] 0; // 临时处理避免后续计算错误 } } } } public: // 构造函数初始化系统状态 BankerAlgorithm(int pCount, int rCount, const std::vectorint avail, const std::vectorstd::vectorint m, const std::vectorstd::vectorint alloc) : processCount(pCount), resourceTypeCount(rCount), available(avail), max(m), allocation(alloc) { calculateNeed(); // 初始化时计算Need矩阵 } // ... 其他成员函数将在后续实现 };注意在构造函数中直接通过max和allocation计算need是一个关键设计。这确保了need矩阵始终与当前分配状态同步。任何对allocation的修改都必须同步更新need和available。2.3 设计思路考量为什么选择std::vector而不是原生数组主要是为了灵活性。在实际的教学或模拟环境中进程和资源的数量可能是运行时输入的。使用vector避免了固定大小的限制也简化了内存管理。此外vector支持方便的拷贝和比较操作这在实现安全检查算法时会很有用。3. 核心算法实现安全检查与资源请求银行家算法主要包含两个部分安全性检查算法和资源请求算法。安全性检查是算法的基石用于判断当前系统状态是否安全。资源请求算法则是在进程提出具体资源申请时模拟分配并调用安全性检查以决定是否批准该请求。3.1 安全性检查算法实现安全性算法旨在寻找一个安全序列。其步骤如下初始化两个向量Work(工作向量初始等于Available) 和Finish(标记进程是否完成初始全为false)。寻找一个满足Finish[i] false且Need[i] Work的进程i。即找到一个尚未完成的进程其剩余需求能被当前可用资源满足。如果找到假设进程i会很快完成并释放资源Work Work Allocation[i]然后设置Finish[i] true。重复步骤2。如果所有进程的Finish[i] true则系统处于安全状态且找到的进程顺序就是一个安全序列。否则系统处于不安全状态。class BankerAlgorithm { // ... 接上文私有成员和构造函数 public: // 安全性检查算法 bool isSafeState(std::vectorint safeSequence) { std::vectorint work available; // 工作向量 std::vectorbool finish(processCount, false); // 完成标记 safeSequence.clear(); safeSequence.reserve(processCount); bool found; // 最多循环 processCount 轮每轮尝试找到一个可执行的进程 for (int count 0; count processCount; count) { found false; for (int i 0; i processCount; i) { // 如果进程i尚未完成并且其需求小于等于当前可用资源 if (!finish[i]) { bool canBeSatisfied true; for (int j 0; j resourceTypeCount; j) { if (need[i][j] work[j]) { canBeSatisfied false; break; } } if (canBeSatisfied) { // 模拟进程i执行完成释放其占有的资源 for (int j 0; j resourceTypeCount; j) { work[j] allocation[i][j]; } finish[i] true; safeSequence.push_back(i); // 将进程加入安全序列 found true; break; // 找到后跳出内层循环开始下一轮寻找 } } } // 如果在一轮中找不到任何一个可以执行的进程说明系统不安全 if (!found) { safeSequence.clear(); // 清空可能部分构建的序列 return false; } } // 所有进程都标记为完成系统安全 return true; } };实操心得安全检查算法的核心是“贪心”寻找。found标志和break的使用是关键。一旦找到一个可满足的进程就立即“假定”它完成更新可用资源然后重新从头开始扫描所有未完成的进程。这是因为释放资源后之前因资源不足而无法运行的进程现在可能可以运行了。如果一轮扫描完都找不到一个可运行的进程说明剩下的进程形成了循环等待系统已进入不安全状态。3.2 资源请求算法实现当进程pid发出一个资源请求向量request时算法需要按以下步骤判断请求有效性检查request必须小于等于该进程的Need[pid]否则视为错误进程申请超过其声明的最大需求。资源可用性检查request必须小于等于当前Available否则让进程等待资源不足。试分配假设分配资源给该进程修改系统状态Available Available - requestAllocation[pid] Allocation[pid] requestNeed[pid] Need[pid] - request安全性检查调用isSafeState检查试分配后的新状态是否安全。决策如果安全则正式批准分配系统状态永久更新。如果不安全则拒绝本次请求并回滚步骤3中的所有状态修改让进程pid等待。class BankerAlgorithm { // ... 接上文 public: // 处理资源请求 enum class RequestResult { GRANTED, DENIED_EXCEEDS_NEED, DENIED_EXCEEDS_AVAILABLE, DENIED_UNSAFE }; RequestResult requestResources(int pid, const std::vectorint request) { // 1. 检查请求是否超过其声明的需求 for (int j 0; j resourceTypeCount; j) { if (request[j] need[pid][j]) { std::cout 拒绝请求进程 P pid 申请的资源超过其声明的需求。 std::endl; return RequestResult::DENIED_EXCEEDS_NEED; } } // 2. 检查请求是否超过当前可用资源 for (int j 0; j resourceTypeCount; j) { if (request[j] available[j]) { std::cout 拒绝请求资源不足进程 P pid 需等待。 std::endl; return RequestResult::DENIED_EXCEEDS_AVAILABLE; } } // 3. 尝试分配修改状态 // 保存旧状态以便回滚 std::vectorint oldAvailable available; std::vectorint oldAllocationPid allocation[pid]; std::vectorint oldNeedPid need[pid]; for (int j 0; j resourceTypeCount; j) { available[j] - request[j]; allocation[pid][j] request[j]; need[pid][j] - request[j]; } // 4. 执行安全性检查 std::vectorint safeSeq; if (isSafeState(safeSeq)) { std::cout 请求批准。系统仍处于安全状态。; if (!safeSeq.empty()) { std::cout 一个可能的安全序列是; for (int p : safeSeq) std::cout P p ; } std::cout std::endl; return RequestResult::GRANTED; // 注意状态已永久更新 } else { // 5. 不安全回滚状态 std::cout 拒绝请求若分配资源系统将进入不安全状态。已回滚。 std::endl; available std::move(oldAvailable); allocation[pid] std::move(oldAllocationPid); need[pid] std::move(oldNeedPid); return RequestResult::DENIED_UNSAFE; } } };注意事项资源请求算法中状态回滚是至关重要的一步。试分配只是在算法的“沙盒”里模拟如果安全检查不通过必须将Available、Allocation[pid]和Need[pid]恢复到请求之前的状态否则系统状态将出现不一致导致后续所有判断错误。这也是为什么我们在修改前要保存旧值。4. 完整模拟器实现与交互演示有了核心算法我们可以构建一个简单的命令行交互程序来模拟整个银行家算法的运行过程。这个模拟器将允许用户初始化系统状态并动态地发起资源请求观察算法的决策过程。4.1 系统状态初始化与展示我们需要一个方法来初始化和直观地展示当前的系统状态。class BankerAlgorithm { // ... 接上文 public: // 打印当前系统状态 void printState() const { std::cout \n 当前系统状态 std::endl; std::cout 可用资源向量 Available: ; for (int val : available) std::cout val ; std::cout std::endl; std::cout \n最大需求矩阵 Max: std::endl; printMatrix(max); std::cout \n分配矩阵 Allocation: std::endl; printMatrix(allocation); std::cout \n需求矩阵 Need: std::endl; printMatrix(need); // 可选立即检查并显示当前是否安全 std::vectorint seq; if (isSafeState(seq)) { std::cout \n当前系统处于【安全状态】。; if (!seq.empty()) { std::cout 安全序列; for (int p : seq) std::cout P p ; } } else { std::cout \n警告当前系统处于【不安全状态】; } std::cout std::endl; } private: void printMatrix(const std::vectorstd::vectorint mat) const { for (int i 0; i processCount; i) { std::cout P i : ; for (int val : mat[i]) std::cout val ; std::cout std::endl; } } // 一个辅助函数用于从用户输入初始化示例 void initializeFromInput() { std::cout 输入进程数: ; std::cin processCount; std::cout 输入资源种类数: ; std::cin resourceTypeCount; std::cout 输入可用资源向量 ( resourceTypeCount 个整数): ; available.resize(resourceTypeCount); for (int val : available) std::cin val; max.resize(processCount, std::vectorint(resourceTypeCount)); allocation.resize(processCount, std::vectorint(resourceTypeCount)); std::cout 输入最大需求矩阵 Max ( processCount x resourceTypeCount ): std::endl; for (int i 0; i processCount; i) { std::cout 进程 P i : ; for (int j 0; j resourceTypeCount; j) { std::cin max[i][j]; } } std::cout 输入分配矩阵 Allocation ( processCount x resourceTypeCount ): std::endl; for (int i 0; i processCount; i) { std::cout 进程 P i : ; for (int j 0; j resourceTypeCount; j) { std::cin allocation[i][j]; } } calculateNeed(); // 计算初始需求矩阵 std::cout 系统初始化完成。 std::endl; } };4.2 主程序与交互循环下面是一个简单的主函数它创建了一个预置的经典示例场景常用于教学并进入一个交互循环允许用户指定进程发起资源请求。#include iostream #include vector #include “BankerAlgorithm.h” // 假设上述类定义在头文件中 int main() { // 使用一个经典示例初始化 // 示例3个进程(P0, P1, P2)竞争3种资源(A, B, C) int pCount 5; int rCount 3; // 总资源向量 (假设) // 可用资源向量 Available std::vectorint avail {3, 3, 2}; // 最大需求矩阵 Max std::vectorstd::vectorint maximum { {7, 5, 3}, // P0 {3, 2, 2}, // P1 {9, 0, 2}, // P2 {2, 2, 2}, // P3 {4, 3, 3} // P4 }; // 已分配矩阵 Allocation std::vectorstd::vectorint alloc { {0, 1, 0}, // P0 {2, 0, 0}, // P1 {3, 0, 2}, // P2 {2, 1, 1}, // P3 {0, 0, 2} // P4 }; BankerAlgorithm banker(pCount, rCount, avail, maximum, alloc); std::cout 银行家算法模拟器启动 (预置经典示例) std::endl; banker.printState(); // 交互循环 int pid; char cmd; do { std::cout \n操作选项: (R)请求资源, (P)打印状态, (Q)退出 std::endl; std::cout 请输入命令: ; std::cin cmd; switch (cmd) { case R: case r: { std::cout 输入请求资源的进程号 (0- pCount-1 ): ; std::cin pid; if (pid 0 || pid pCount) { std::cout 无效的进程号 std::endl; break; } std::vectorint req(rCount); std::cout 输入资源请求向量 ( rCount 个整数): ; for (int val : req) std::cin val; auto result banker.requestResources(pid, req); // 请求处理后打印最新状态 banker.printState(); break; } case P: case p: banker.printState(); break; case Q: case q: std::cout 退出模拟器。 std::endl; break; default: std::cout 未知命令请重新输入。 std::endl; } } while (cmd ! Q cmd ! q); return 0; }运行示例 假设我们使用上述预置数据启动模拟器。初始状态是安全的安全序列可能是P1, P3, P4, P0, P2。输入命令R然后输入进程号1请求向量[1, 0, 2]。算法会检查请求[1,0,2]是否小于等于Need[1][1,2,2]是。是否小于等于Available[3,3,2]是。然后试分配进行安全检查。你会发现分配后系统仍然是安全的例如安全序列变为P1, P3, P4, P0, P2因此请求被批准。接着再让进程4请求[3, 3, 0]。检查Need[4][4,3,1]请求未超需求。但检查Available经过上一步分配后Available可能已变为[2,3,0][3,3,0]超过了可用资源请求会因“资源不足”被立即拒绝状态不发生任何改变。尝试一个会导致不安全的请求让进程0请求[0, 2, 0]。试分配后安全检查算法将找不到安全序列因此请求会被拒绝并且所有状态被回滚。通过这样的交互你可以非常直观地理解银行家算法如何像一个谨慎的管家在每一次资源分配前都进行“沙盘推演”确保整个系统不会滑向死锁的深渊。5. 算法局限性、扩展思考与常见问题实现了一个可运行的银行家算法后我们有必要跳出代码审视其在实际系统中的应用局限并思考可能的扩展方向。同时总结在实现和调试过程中容易遇到的问题。5.1 银行家算法的局限性尽管银行家算法在理论上非常优美但在真实的通用操作系统中却很少被直接使用原因如下需要预先知道最大需求算法要求每个进程在运行前就声明其所需各类资源的最大数量。这对于很多交互式或动态链接库加载的应用程序来说是非常困难甚至不可能的。比如一个文本编辑器很难预先知道用户会打开多少个文件、占用多少内存。进程数量与资源种类固定算法假设进程数和资源种类是固定的。但在真实的动态系统中进程会不断创建和终止资源如USB设备可能被热插拔。资源利用率可能降低为了绝对避免死锁算法会非常保守。它可能拒绝一些实际上不会导致死锁的请求因为安全检查是充分条件而非必要条件从而导致资源闲置利用率下降。开销较大每次资源请求都需要执行一次时间复杂度为 O(n² * m) 的安全性检查n为进程数m为资源种类数。在进程数量很多时开销显著。因此现代操作系统更常采用死锁检测与恢复或死锁预防策略。例如通过定义资源的线性顺序层次分配法来预防循环等待或者定期运行一个图算法来检测系统中是否存在死锁资源分配图一旦发现再采取措施解除如强制终止进程。5.2 扩展思考与优化方向虽然直接应用受限但银行家算法的思想在特定领域仍有价值并且我们的实现可以进一步扩展模拟动态进程可以扩展我们的BankerAlgorithm类增加addProcess和removeProcess方法。添加进程时需要提供其Max向量初始Allocation为0。移除进程时需要将其占用的资源Allocation归还给Available。这更贴近真实场景。资源释放我们实现了请求但没有显式实现释放。可以增加一个releaseResources方法当进程完成任务时调用此方法将其占用的所有资源即Allocation[pid]归还给系统并将该进程的Max和Allocation清零或标记为结束。可视化对于教学而言一个图形界面GUI能极大提升理解效率。可以用 Qt 或 Web 前端绘制资源分配图动态展示Available、Max、Allocation、Need矩阵的变化以及安全检查算法的每一步寻找过程。性能优化安全检查算法可以优化。例如使用一个队列来维护当前可满足的进程集合避免每一轮都进行 O(n) 的完整扫描。5.3 常见问题与调试技巧实录在实现和测试银行家算法时我遇到过以下几个典型问题状态不一致导致安全检查永远失败现象无论怎么初始化isSafeState总是返回false。排查根本原因通常是Need矩阵计算错误或未及时更新。确保Need Max - Allocation这个等式始终成立。每次修改Allocation后必须同步更新Need和Available。一个调试技巧是在printState函数中打印出Max、Allocation和计算出的Need并手动验证几行数据是否正确。代码检查点// 在 requestResources 中试分配后应立即检查 Need 计算 // 可以添加一个断言或调试输出 for (int j0; jresourceTypeCount; j) { assert(need[pid][j] (max[pid][j] - allocation[pid][j])); }安全序列不唯一算法结果不稳定现象同一状态多次运行isSafeState得到的安全序列可能不同。分析这是正常现象不是bug。当有多个进程同时满足Need[i] Work时我们的实现按顺序i从0到n-1选择第一个遇到的。这会导致结果依赖于遍历顺序。安全状态可能有多个安全序列算法找到任何一个都证明系统是安全的。如果你需要确定的序列例如按优先级可以在寻找进程时不直接break而是将所有满足条件的进程加入一个列表然后按特定规则如进程ID、优先级选择一个。请求处理逻辑中回滚不彻底现象拒绝一个不安全的请求后系统的Available似乎变少了。排查这是最危险的bug。问题出在requestResources函数中如果安全检查失败必须将Available、Allocation[pid]和Need[pid]全部恢复到试分配前的状态。我最初的错误是只回滚了Available和Allocation忘记了Need。牢记这三个数据结构是一个整体必须原子性地一起修改或回滚。修正方案如我们代码所示在试分配前保存这三个值的旧状态副本。回滚时整体替换。输入数据导致 Need 矩阵出现负数现象初始化或请求后Need矩阵中某些元素为负数。原因输入数据不合理Allocation的值大于了Max。这违背了银行家算法的基本前提已分配的不可能超过最大需求。处理在构造函数和requestResources的第一步检查中必须加入有效性验证。如果Allocation Max或request Need应视为非法输入直接报错并拒绝而不是继续计算。在我们的calculateNeed中虽然做了检查并置零但这只是一种容错处理更好的做法是在数据入口就严格拦截。通过这个从原理到实现再到问题排查的完整过程银行家算法不再是一个黑盒理论。你不仅能用C实现它更能理解其精妙之处与实用边界。这种通过编码来深化对经典算法理解的方法我个人认为比单纯阅读课本要有效得多。当你自己处理了状态同步、回滚、边界检查这些细节后对“安全状态”、“避免死锁”这些概念的理解会深刻得多。

相关新闻

3步快速搭建国标视频监控平台:wvp-GB28181-pro完整部署教程

3步快速搭建国标视频监控平台:wvp-GB28181-pro完整部署教程

3步快速搭建国标视频监控平台:wvp-GB28181-pro完整部署教程 【免费下载链接】wvp-GB28181-pro 基于GB28181-2016、部标808、部标1078标准实现的开箱即用的网络视频平台。自带管理页面,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。…

2026/10/18 10:10:58 阅读更多 →
设备检测全面指南:技术方法、标准体系与智能检测趋势

设备检测全面指南:技术方法、标准体系与智能检测趋势

设备检测全面指南:技术方法、标准体系与智能检测趋势导读:设备检测是保障工业设备安全运行、提升生产效率的核心环节。随着全球检测市场规模持续扩大和AI智能检测技术快速发展,设备检测正从传统的人工巡检向智能化、数字化、预测性维护方向转…

2026/10/17 7:29:41 阅读更多 →
PyTorch分布式训练数据加载优化:DataLoader调优与WebDataset实战

PyTorch分布式训练数据加载优化:DataLoader调优与WebDataset实战

1. 项目概述:当数据加载成为分布式训练的瓶颈 在PyTorch分布式数据并行(DDP)训练中,我们常常把目光聚焦在模型分发的同步、梯度聚合的通信开销上,想尽办法优化NCCL通信。然而,一个更隐蔽却同样致命的性能瓶…

2026/10/18 5:05:29 阅读更多 →

最新新闻

Stable Diffusion跨注意力图实现零样本图像分割的方法与实践

Stable Diffusion跨注意力图实现零样本图像分割的方法与实践

1. 这个思路是怎么来的:当扩散模型撞上分割任务1.1 为什么说AI看图会“瞎猜”:从三个真实痛点说起图像分割在计算机视觉里一直是个很微妙的工作。检测是告诉你“这里有个盒子”,分割是告诉你“这个盒子的边界到底在哪”。前者是粗活&#xff…

2026/10/18 10:10:40 阅读更多 →
Spark性能调优实战:从RDD、Shuffle到数据倾斜全解

Spark性能调优实战:从RDD、Shuffle到数据倾斜全解

Apache Spark 这几年在大数据计算里几乎成了标配。做日志清洗用它,跑离线报表用它,搞实时特征也离不开它。我在实际项目里用它处理过几十TB的日志数据,也帮同事排查过各种 OOM 和任务卡死的问题。很多人觉得 Spark 上手容易,无非是…

2026/10/18 10:10:40 阅读更多 →
Apache Spark核心机制与PySpark实战:从DAG调度到性能调优

Apache Spark核心机制与PySpark实战:从DAG调度到性能调优

手头又攒了一轮粉丝留言,最多的一个就是"能不能出一篇Apache Spark的完整教程"。说实话,Spark这个框架在数据工程圈子里几乎是默认的标配技能,不管是离线报表、实时数仓,还是机器学习特征工程,十有八九都会落…

2026/10/18 10:10:40 阅读更多 →
货运SaaS多租户架构设计与核心模块源码解析

货运SaaS多租户架构设计与核心模块源码解析

货运行业做SaaS,和做普通企业管理系统最大的区别在哪里?我先把话说在前面:不是功能堆得多,而是架构扛得住。一套系统要同时服务几十上百家物流公司,每家公司看到的都是自己的订单、自己的司机、自己的账单,…

2026/10/18 10:10:40 阅读更多 →
FDOA定位中的GDOP:布站几何如何决定定位精度?

FDOA定位中的GDOP:布站几何如何决定定位精度?

简介:面向无线定位与信号处理方向研究者的 MATLAB 实现,围绕 FDOA(到达频率差)定位中的 GDOP(几何精度衰减因子)计算展开。脚本覆盖从 FDOA 测量数据预处理、频差计算、距离估计到接收器几何布局建模、GDOP…

2026/10/18 10:10:40 阅读更多 →
JavaWeb学籍管理系统:Servlet-JSP-JDBC三层实战与答辩通关指南

JavaWeb学籍管理系统:Servlet-JSP-JDBC三层实战与答辩通关指南

简介:本资源是一套完整可用的JavaWeb学籍管理系统课程设计项目,面向计算机专业本科生及Java初学者,用于完成期末大作业、课程设计实践或Web开发入门训练。系统涵盖学生信息管理、班级维护、成绩录入与查询等核心教务功能,采用JSPS…

2026/10/18 10:09:40 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →