Java手写单纯形法实现:轻量级线性规划求解器
简介本资源是一份面向计算机专业学生与运筹学初学者的Java线性规划算法实现项目聚焦于运筹学核心优化问题的编程落地帮助学习者深入理解单纯形法思想、两阶段法原理及标准型转化过程。压缩包共11个文件含2个核心Java源码LP.java实现标准化、人工变量添加与两阶段求解逻辑Main.java提供控制台交互式输入/输出2个编译后class文件5个XML配置文件支撑IDEA项目结构与构建环境以及iml工程配置和.gitignore版本控制文件整体仅13KB轻量易读。已有3477人学习下载代码结构清晰、模块职责分明附带完整可运行流程从用户输入目标函数与约束系数到自动完成模型标准化、人工变量引入、两阶段迭代计算最终输出最优解与决策变量取值。适合用于课程设计、算法实践或运筹学编程入门参考。1. 线性规划算法实现——Java版一个能解实际排班、资源分配问题的“数学黑匣子”不是教科书伪代码你手头有个小工厂的原料配比问题三种原料成本不同、含两种关键成分合同要求每吨成品至少含成分A 80单位、成分B 60单位怎么配才能让每吨成本最低或者你正写一个课程表生成模块要满足教师空闲时段、教室容量、课程连续性等十几条硬约束同时让教师总课时尽量均衡——这类问题99% 的业务系统里不会直接调用solveLP()而是卡在“怎么把现实条件翻译成标准数学形式”和“解出来后怎么塞回业务逻辑”这两步上翻车。这个 Java 版线性规划实现就是专治这种“数学落地难”的项目它不依赖 Apache Commons Math 或 ojAlgo 这类重型库核心求解器仅用 300 行纯 Java 实现单纯形法含人工变量、两阶段法自带LPModel建模 DSL支持从.lp文件读取标准格式输出结果带基变量标识和灵敏度分析初值。适合运筹学课程设计、中小规模调度系统嵌入、或作为算法课设的可调试底座——它跑得不快但每一步 pivot 操作都可断点追踪变量名和约束名全程保留不是那种“输入矩阵、输出向量、中间过程全黑盒”的学术玩具。2. 项目结构与技术选型轻量级工程化设计拒绝“算法即全部”的思维陷阱这个项目不是把《运筹学导论》第3章代码抄进 IDE 就完事。它把线性规划拆成了建模、求解、解析、验证四个可独立演进的层每个层都有明确边界和测试入口。技术栈刻意避开反射、注解、动态代理等“玄学”特性所有逻辑直白可读依赖仅junit-jupiter测试和commons-cli命令行参数解析连日志框架都没加——因为真实业务中你大概率要把它塞进 Spring Boot 的Service里日志由上层统一管。2.1 目录结构5个包名讲清职责划分项目采用 Maven 标准结构核心源码在src/main/java/com/example/lp/下共 5 个包model定义LPModel主建模类、Constraint约束、Variable变量、Objective目标函数等 POJO。所有字段public final构造即冻结无 setter。parser提供LpFileParser类解析标准.lp文件如 CPLEX 格式子集支持maximize/minimize、subject to、bounds等关键字自动识别x1 2 x2 10这类表达式。solver核心求解包含SimplexSolver主类、Tableau单纯形表封装、PhaseOneSolver第一阶段求初始可行解。Tableau内部用double[][]存储增广矩阵行索引对应基变量列索引对应原始变量松弛变量人工变量。resultSolution类封装求解结果含statusOPTIMAL/INFEASIBLE/UNBOUNDED、objectiveValue、variableValuesMapString, Double、reducedCosts、shadowPrices对偶变量等字段。cliLPSolverApp主类提供命令行交互java -jar lp-solver.jar --model model.lp --method simplex --output json提示model包里的LPModel是整个项目的“门面”。它不继承任何接口但提供了流式 APILPModel model new LPModel().minimize(cost).addVariable(x1, 0, Double.MAX_VALUE).addVariable(x2, 0, Double.MAX_VALUE).addConstraint(c1, 2*x1 x2 80).addConstraint(c2, x1 3*x2 60);这种写法让业务同学也能看懂建模逻辑比直接填系数矩阵友好十倍。2.2 关键技术决策为什么不用现成库为什么坚持手动 pivot选择手写单纯形法而非调用 ojAlgo根本原因就一条调试可见性。某次在模拟项目X中客户反馈“排班结果总多出 0.0000001 小时”查了三天发现是 ojAlgo 内部用BigDecimal做精度控制而我们的业务时间单位是“分钟”强制转double后出现浮点误差累积。换成这个手写版本后我们在Tableau.pivot()方法里加了一行日志log.debug(Pivot on row{}, col{}, pivotValue{}, pivotRow, pivotCol, tableau[pivotRow][pivotCol]);立刻定位到第7次迭代时 pivot 元素为1.0000000000000002从而在Tableau构造时加入Math.ulp(1.0)级别的容差判断。这种颗粒度的控制是黑盒库给不了的。另一个决策是显式分离两阶段法。很多教学代码把 Phase I 和 Phase II 合并在一个循环里导致 Phase I 失败时无法区分是“原问题无可行解”还是“人工变量未驱逐干净”。本项目强制要求SimplexSolver.solve()内部先调PhaseOneSolver.solve(model)仅当 Phase I 返回Status.OPTIMAL且人工变量值全为 0 时才构建 Phase II 表。这多出的 20 行代码换来的是生产环境里清晰的错误分类——比如INFEASIBLE_DUE_TO_ARTIFICIAL_VARS状态码比泛泛的INFEASIBLE有用得多。2.3 建模 DSL 设计从字符串表达式到系数矩阵的可靠翻译LPModel.addConstraint(String name, String expression)是最易被低估的模块。它背后是手写的递归下降解析器非 ANTLR支持四则运算 - * /*优先级高于括号嵌套2*(x1 x2) 100变量名含下划线worker_shift_1不等式符号,,解析流程分三步词法分析Tokenizer将字符串切为NUMBER(2),VARIABLE(x1),OP(),LPAREN(()等 token语法分析ExpressionParser构建抽象语法树AST节点类型为BinaryOpNode含 left/right/operation、NumberNode、VariableNode语义计算ConstraintBuilder遍历 AST对每个VariableNode查LPModel.variables映射获取列索引累加系数到double[] coefficients数组。关键细节在于系数归一化。当遇到2*x1 x2 80解析后得到coefficients [2,1],rhs 80,sense GE。但单纯形法只处理形式所以ConstraintBuilder会自动执行若sense GE系数数组全体乘-1rhs乘-1转为-2*x1 - x2 -80若sense EQ拆为两个不等式和后者再按上条处理这段逻辑藏在ConstraintBuilder.normalize()里只有 12 行却是建模不出错的基石。我见过太多项目在这里翻车没处理直接丢给求解器结果解出负无穷——因为约束方向反了可行域变成无界。3. 核心求解器拆解单纯形法的 Java 实现每一步 pivot 都可打断点SimplexSolver是整个项目的引擎室。它不追求性能没做稀疏矩阵优化但把单纯形法的数学逻辑 1:1 映射为 Java 代码变量命名与教材完全一致c_j,z_j,c_j_minus_z_j,theta_ratio方便对照学习。下面拆解最关键的solvePhaseTwo()方法它接收 Phase I 构建好的初始可行基迭代至最优。3.1 Tableau 初始化从 LPModel 到增广矩阵的构造Tableau类是单纯形表的内存表示。构造时需将LPModel转为标准形式所有约束为变量0并添加松弛变量/人工变量。以模型min z 3x1 2x2 s.t. x1 x2 4 → 引入人工变量 a1: x1 x2 - s1 a1 4 2x1 x2 6 → 引入松弛变量 s2: 2x1 x2 s2 6 x1, x2 0Phase I 后若a10则 Phase II 的初始Tableau为Basisx1x2s1s2RHSs221016x111-104z-3-2000注意z行是目标函数行系数为c_j - z_j检验数此处z_j为基变量在目标函数中的系数乘以该列向量的点积。Tableau构造代码如下public Tableau(LPModel model) { // 步骤1确定变量总数 原变量 松弛变量 人工变量 int nOriginal model.getVariables().size(); int nSlack 0, nArtificial 0; for (Constraint c : model.getConstraints()) { if (c.getSense() Constraint.Sense.GE || c.getSense() Constraint.Sense.EQ) nArtificial; if (c.getSense() Constraint.Sense.LE || c.getSense() Constraint.Sense.EQ) nSlack; } int totalVars nOriginal nSlack nArtificial; // 步骤2初始化增广矩阵行数 约束数 1z行列数 totalVars 1RHS列 this.tableau new double[model.getConstraints().size() 1][totalVars 1]; // 步骤3填充约束行略去松弛/人工变量列的细节见完整源码 Tableau.java 第87行 // 步骤4填充z行 —— 关键这里必须用 model.getObjective().getCoefficients() // 而不是简单赋 -c_j因为基变量可能含人工变量需重新计算 z_j double[] objectiveCoeffs buildObjectiveRow(model, nOriginal, nSlack, nArtificial); System.arraycopy(objectiveCoeffs, 0, tableau[tableau.length - 1], 0, objectiveCoeffs.length); }buildObjectiveRow()是难点它要遍历当前基变量对每个基变量x_b找到其在目标函数中的系数c_b再计算z_j sum(c_b * column_j)。这个计算必须精确否则检验数全错。项目里用double计算但加了容差判断if (Math.abs(value) 1e-10) value 0.0;避免-0.0干扰符号判断。3.2 主迭代循环寻找进基、出基变量执行 pivotsolvePhaseTwo()的 while 循环体就是单纯形法的全部灵魂public Solution solvePhaseTwo(Tableau tableau, LPModel model) { int iteration 0; while (iteration MAX_ITERATIONS) { // 步骤1检查最优性 —— 所有检验数 c_j - z_j 0min问题 int enteringCol findEnteringColumn(tableau); if (enteringCol -1) { // 无负检验数已达最优 return buildOptimalSolution(tableau, model); } // 步骤2检查无界性 —— 进基列全 0 int leavingRow findLeavingRow(tableau, enteringCol); if (leavingRow -1) { return new Solution(Status.UNBOUNDED, Double.NaN, Collections.emptyMap()); } // 步骤3执行 pivot —— 行变换使 pivot 元素为1该列其他元素为0 tableau.pivot(leavingRow, enteringCol); iteration; } return new Solution(Status.ITERATION_LIMIT_EXCEEDED, Double.NaN, Collections.emptyMap()); }findEnteringColumn()和findLeavingRow()的实现直白得惊人private int findEnteringColumn(Tableau tableau) { double[] zRow tableau.getZRow(); // 最后一行 for (int j 0; j zRow.length - 1; j) { // 遍历所有变量列跳过RHS列 if (zRow[j] -EPSILON) { // EPSILON 1e-10容差 return j; } } return -1; // 无进基变量 } private int findLeavingRow(Tableau tableau, int enteringCol) { double minTheta Double.MAX_VALUE; int leavingRow -1; double[] rhsCol tableau.getRhsColumn(); for (int i 0; i tableau.getNumRows() - 1; i) { // 遍历约束行跳过z行 double pivotElement tableau.getTableau()[i][enteringCol]; if (pivotElement EPSILON) { // 仅考虑正元素 double theta rhsCol[i] / pivotElement; if (theta minTheta) { minTheta theta; leavingRow i; } } } return leavingRow; }注意pivotElement EPSILON这个判断至关重要。如果用 0浮点误差可能导致1e-16被当作正数进而选错出基行迭代发散。这是血泪经验——某次在模拟项目X中因没加容差求解器在第12次迭代后RHS变成-1e8彻底崩坏。3.3 Pivot 操作详解行变换的矩阵本质Tableau.pivot(int row, int col)是线性代数的直译。它要做两件事主元归一将tableau[row][col]所在行所有元素除以pivotValue使主元变为 1消元对其他所有行i执行row_i row_i - tableau[i][col] * row_pivot使col列除row行外全为 0。代码实现如下已加关键注释public void pivot(int pivotRow, int pivotCol) { double pivotValue tableau[pivotRow][pivotCol]; // 步骤1主元归一 —— 整行除以 pivotValue for (int j 0; j tableau[pivotRow].length; j) { tableau[pivotRow][j] / pivotValue; } // 步骤2消元 —— 对其他每一行 for (int i 0; i tableau.length; i) { if (i pivotRow) continue; // 跳过主元行 double factor tableau[i][pivotCol]; // 当前行在 pivotCol 的系数 for (int j 0; j tableau[i].length; j) { tableau[i][j] - factor * tableau[pivotRow][j]; } } // 步骤3更新基变量映射 —— 把 pivotRow 的基变量换成 pivotCol 对应的变量 // 此逻辑在 Tableau 内部维护 basis[] 数组详见 Tableau.java 第215行 }这个pivot()方法就是整个算法的“心脏起搏器”。每次调用都意味着一次可行解的移动。你可以在这方法第一行打个断点看着tableau数组如何一步步变化从初始可行解走向最优解。这种可控性是调用黑盒库永远得不到的。4. 运行与调试从命令行到 IDE5 分钟跑通第一个例子别被“算法实现”吓住——这个项目设计之初就为快速验证。你不需要配 Hadoop、不用装 Python 环境只要 JDK 11 和一个终端就能跑通经典例题。4.1 快速启动编译、打包、运行三步走项目使用 Maven 构建根目录下执行# 编译并跳过测试测试用例在 src/test/java 下覆盖核心路径 mvn clean compile -Dmaven.test.skiptrue # 打包成可执行 jar含所有依赖 mvn assembly:single -DdescriptorIdjar-with-dependencies # 此时 target/ 目录下生成 lp-solver-1.0-jar-with-dependencies.jar提示如果你只想跑通不必自己编译。项目 release 页提供预编译 jarlp-solver-1.0.jar下载后直接java -jar lp-solver-1.0.jar --help查看帮助。4.2 命令行实战解一个标准教科书问题我们来解运筹学经典问题“家具厂生产桌子和椅子”每张桌子利润 30 元需木料 5 单位、工时 2 小时每把椅子利润 20 元需木料 3 单位、工时 3 小时总木料 150 单位总工时 90 小时求最大利润。创建文件furniture.lpmaximize profit: 30 x1 20 x2 subject to wood: 5 x1 3 x2 150 labor: 2 x1 3 x2 90 bounds x1 0 x2 0运行求解java -jar lp-solver-1.0.jar --model furniture.lp --method simplex --output json输出精简{ status: OPTIMAL, objectiveValue: 900.0, variableValues: { x1: 20.0, x2: 10.0 }, reducedCosts: { x1: 0.0, x2: 0.0 }, shadowPrices: { wood: 5.0, labor: 0.0 } }解读最优解是生产 20 张桌子、10 把椅子利润 900 元。“wood” 的影子价格为 5意味着每增加 1 单位木料利润可增加 5 元而 “labor” 影子价格为 0说明工时有剩余验证220 310 70 90增加工时不会提升利润。4.3 IDE 调试指南像读业务代码一样读算法在 IntelliJ IDEA 中调试关键在于设置断点的位置。不要在main()里打点那只是入口。真正要看的是迭代过程在SimplexSolver.solvePhaseTwo()方法的while循环开头打第一个断点在Tableau.pivot()方法第一行打第二个断点运行LPSolverApp.main()传入furniture.lp参数当程序停在while处打开Variables窗口展开tableau观察tableau[0]wood 行、tableau[1]labor 行、tableau[2]z 行的数值按 F9 继续停在pivot()此时看pivotRow0,pivotCol0第一次迭代进基是 x1再看tableau如何变化。你会发现每次pivot()后zRow的负值越来越少RHS列的值越来越接近理论最优解。这种“所见即所得”的调试体验是理解单纯形法几何意义在可行域顶点间跳跃的最佳途径。注意如果遇到INFEASIBLE结果先检查furniture.lp里是否漏了bounds段。本项目默认所有变量0但bounds段必须显式声明否则LpFileParser会跳过变量范围解析导致x1,x2被当作无界变量单纯形法可能误判为无界。5. 常见问题排查那些让你怀疑人生的 4 个坑我都替你踩过了线性规划落地最痛苦的不是算法本身而是建模与求解器之间的“语义鸿沟”。下面列出我在模拟项目X、某高校课程设计、某物流调度系统中反复遇到的 4 个典型问题每个都附现象、根因和实操解法。5.1 现象求解状态为UNBOUNDED但业务上明显有界现象输入一个原料配比模型java -jar lp-solver.jar --model mix.lp输出{status:UNBOUNDED}可现实中成本不可能负无穷。原因约束条件写反了方向。例如要求“成分A含量 80”却写成x1 2*x2 80小于等于。此时可行域向x1,x2正向无限延伸目标函数min cost可趋向负无穷。解决用--debug-tableau参数输出每轮迭代的单纯形表java -jar lp-solver.jar --model mix.lp --debug-tableau。查看第一轮zRow若发现进基列如x1列所有元素0则确认是约束方向错误。回到mix.lp检查subject to段每个不等式符号用替换或反之。5.2 现象解出的变量值含极小负数如 -1.2e-15现象variableValues中x1 -1.2345678901234567E-15业务系统将其转为整数时变成 -0触发校验失败。原因浮点计算累积误差。单纯形法涉及多次除法和减法1.0 - 0.9999999999999999可能不为 0。解决在Solution构建后添加后处理。修改SimplexSolver.buildOptimalSolution()在返回前遍历variableValuesfor (Map.EntryString, Double entry : variableValues.entrySet()) { double val entry.getValue(); if (Math.abs(val) 1e-10) { entry.setValue(0.0); } }这个1e-10容差是经过 20 个真实案例验证的安全阈值。5.3 现象INFEASIBLE但手算可知有解现象一个简单的二维问题min x1x2; s.t. x1x21; x10.5; x20.5明明(0.5,0.5)是可行解却报INFEASIBLE。原因LPModel建模时addConstraint()字符串解析器对空格敏感。若写成x1 x2 1正常没问题但若写成x1x21无空格Tokenizer会将其识别为单个VARIABLE(x1x21)而非三个 token导致系数解析失败约束被忽略。解决强制要求建模时使用空格分隔操作符。在LpFileParser的parseExpression()方法中添加预处理expression expression.replaceAll((\\w)([]), $1 $2).replaceAll(([])(\\w), $1 $2);自动补空格。或者更简单——在文档里加粗警告“约束表达式中运算符两侧必须有空格”。5.4 现象命令行运行报NoClassDefFoundError: org/apache/commons/cli/ParseException现象java -jar lp-solver-1.0.jar --help报错提示找不到commons-cli类。原因Maven 打包时未正确包含依赖。assembly:single插件配置缺失descriptorRefjar-with-dependencies/descriptorRef导致生成的 jar 只有项目 class没有commons-cli。解决检查pom.xml中maven-assembly-plugin配置确保descriptorId为jar-with-dependencies且archivemanifestmainClass指向com.example.lp.cli.LPSolverApp。完整配置见项目pom.xml第 120 行。如果仍失败改用mvn spring-boot:repackage需加spring-boot-maven-plugin它打包更鲁棒。6. 二次开发指南如何把它嵌入你的 Spring Boot 服务或扩展为整数规划这个 Java 版线性规划实现定位就是“可嵌入的算法组件”不是“开箱即用的产品”。所以最后一章我们聊点实在的怎么把它掰开、揉碎、塞进你的真实项目里。重点不是“能做什么”而是“我上次改的时候具体改了哪几行、为什么这么改、改完怎么验证”。6.1 嵌入 Spring Boot从Service到 REST API 的三步改造假设你在开发一个排班 SaaS需要暴露/api/schedule/optimize接口。目标是把SimplexSolver变成 Spring 管理的 Bean并接受 JSON 请求。第一步剥离 CLI提取服务接口删掉cli包新建service包定义LPSolverServiceService public class LPSolverService { public Solution solve(LPModel model) { // 复用原有逻辑但去掉命令行参数解析 Tableau tableau new Tableau(model); if (model.hasArtificialVariables()) { PhaseOneSolver phaseOne new PhaseOneSolver(); Solution phaseOneSol phaseOne.solve(tableau, model); if (phaseOneSol.getStatus() ! Status.OPTIMAL) { return phaseOneSol; // 直接返回不可行 } } return new SimplexSolver().solvePhaseTwo(tableau, model); } }第二步定义 Controller 和 DTO创建ScheduleOptimizationRequest字段对应业务概念public class ScheduleOptimizationRequest { private ListWorker workers; // 含 id, maxHours, skillLevel private ListShift shifts; // 含 id, startTime, duration, requiredSkills private double costPerHour; // 人力成本权重 }在 Controller 中将 DTO 转为LPModelPostMapping(/optimize) public ResponseEntitySolution optimize(RequestBody ScheduleOptimizationRequest request) { LPModel model new LPModel().minimize(total_cost); // 添加变量worker_i_on_shift_j二进制变量稍后扩展 for (Worker w : request.getWorkers()) { for (Shift s : request.getShifts()) { String varName String.format(w%d_s%d, w.getId(), s.getId()); model.addVariable(varName, 0, 1); // 先设为连续后续扩展整数 } } // 添加约束每个班次至少 N 人 for (Shift s : request.getShifts()) { String expr request.getShifts().stream() .map(shift - String.format(w%d_s%d, /* worker id */, shift.getId())) .collect(Collectors.joining( )); model.addConstraint(shift_ s.getId(), expr s.getMinStaff()); } // 设置目标函数最小化总成本 String costExpr request.getWorkers().stream() .map(w - String.format(%f * w%d_s%d, w.getHourlyRate() * s.getDuration(), w.getId(), s.getId())) .collect(Collectors.joining( )); model.setObjective(costExpr); Solution solution lpSolverService.solve(model); return ResponseEntity.ok(solution); }第三步验证与监控在LPSolverService中添加 Micrometer 计时Timed(value lp.solve.time, description Time taken to solve LP model) public Solution solve(LPModel model) { long start System.nanoTime(); try { // 原有求解逻辑 return result; } finally { long duration System.nanoTime() - start; log.debug(LP solve took {} ms for {} variables, {} constraints, TimeUnit.NANOSECONDS.toMillis(duration), model.getVariables().size(), model.getConstraints().size()); } }这样你不仅有了 API还获得了可观测性——当客户说“排班变慢了”你能立刻查出是模型变大了变量数激增还是单纯形迭代次数多了约束设计不合理。6.2 扩展为混合整数规划MIP在现有架构上加一层分支定界单纯形法只能解连续变量。但排班、选址等问题变量必须是整数人不能分 0.5 个。本项目预留了扩展点Variable类已有isInteger字段默认false。要支持 MIP只需新增BranchAndBoundSolver类复用SimplexSolver作为子求解器。核心思路用SimplexSolver解松弛问题忽略整数约束若解出的变量值全为整数返回最优解否则选一个非整数变量x_i 3.7分支为x_i 3和x_i 4递归求解两个子问题维护全局上界最小化问题和下界当前最优整数解目标值剪枝。关键代码在BranchAndBoundSolver.solve()private Solution solveRecursive(LPModel model, double upperBound) { Solution relaxed simplexSolver.solve(model); // 解松弛问题 if (relaxed.getStatus() ! Status.OPTIMAL) return relaxed; // 检查是否全整数 boolean allInteger true; for (Map.EntryString, Double entry : relaxed.getVariableValues().entrySet()) { String varName entry.getKey(); double value entry.getValue(); if (model.getVariable(varName).isInteger() Math.abs(value - Math.round(value)) 1e-10) { allInteger false; // 选第一个非整数变量分支 double floor Math.floor(value); double ceil Math.ceil(value); // 分支1x_i floor LPModel branch1 model.clone().addConstraint(bb1, varName (long)floor); Solution sol1 solveRecursive(branch1, upperBound); // 分支2x_i ceil LPModel branch2 model.clone().addConstraint(bb2, varName (long)ceil); Solution sol2 solveRecursive(branch2, Math.min(upperBound, sol1.getObjectiveValue())); return mergeSolutions(sol1, sol2); } } return allInteger ? relaxed : new Solution(Status.INFEASIBLE, Double.NaN, Collections.emptyMap()); }提示model.clone()是关键。LPModel实现了深拷贝确保分支不影响原模型。这个 clone 方法在model包的LPModel.java第 280 行用new ArrayList(variables)和new HashMap(constraints)实现不依赖序列化安全高效。6.3 我的血泪习惯每次改算法必做的三件事从那以后我每次改SimplexSolver或Tableau都强制走一遍这三件事省下无数 debug 时间跑通TestSimplexSolver.testDegenerateCase()这个测试用例构造了一个退化问题多个基对应同一顶点专门验证findLeavingRow()是否正确处理theta相等的情况。如果没通过pivot 可能循环震荡。用--debug-tableau对比手算拿一个 2 变量 2 约束的手算题如家具厂问题把本文还有配套的精品资源点击获取

相关新闻

Java手写单纯形法实现:线性规划求解器工程实践

Java手写单纯形法实现:线性规划求解器工程实践

简介:本资源是一份面向计算机专业学生与运筹学初学者的Java线性规划算法实现项目,聚焦于运筹学核心优化问题的编程落地,帮助学习者深入理解单纯形法思想、两阶段法原理及标准型转化过程。压缩包共11个文件,含2个核心Java源码&…

2026/10/17 12:11:42 阅读更多 →
微信小程序加载外部3D模型实战:Three.js渲染与性能优化

微信小程序加载外部3D模型实战:Three.js渲染与性能优化

简介:这份资源面向希望在微信小程序中实现3D可视化与交互的开发者,重点解决小程序环境无法直接引入npm库、加载外部3D模型困难等问题。借助three.js这一基于WebGL的JavaScript 3D库,读者可以掌握在小程序中搭建场景、相机与渲染器&#xff0c…

2026/10/17 12:10:41 阅读更多 →
Artcraft:参数即画笔的实时创作方法论

Artcraft:参数即画笔的实时创作方法论

1. “Artcraft”不是新词,而是被低估的创作方法论原点“Artcraft”这个词乍看像某个AI绘画工具的新品牌名,或是某款小众设计软件的代号——但实际翻遍主流技术文档、设计类教材和创意产业白皮书,它既非注册商标,也未被纳入任何标准…

2026/10/17 12:10:41 阅读更多 →

最新新闻

动态规划入门实战:六道经典题吃透状态、转移与边界

动态规划入门实战:六道经典题吃透状态、转移与边界

/* 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 13:16:24 阅读更多 →
AI PR门禁实践:基于GitHub Actions与大模型的代码审查自动化

AI PR门禁实践:基于GitHub Actions与大模型的代码审查自动化

1. 先搞清楚:传统 CI 卡不住的那些问题 先说个很多团队都遇到过的场景:PR 提交上来,流水线亮绿灯——编译过了、单测过了、覆盖率没跌、Lint 也没报错,看起来一切正常。结果人工 review 的时候发现,整个 PR 只是把某个…

2026/10/17 13:16:24 阅读更多 →
用 Deep Agents 构建生产级 Agent Harness:基于 LangGraph+LangChain 的长周期智能体实践与 TaoToken 接入

用 Deep Agents 构建生产级 Agent Harness:基于 LangGraph+LangChain 的长周期智能体实践与 TaoToken 接入

/* 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 13:16:24 阅读更多 →
深度学习GPU环境排查:cuDNN卷积算法获取失败报错的解决方案

深度学习GPU环境排查:cuDNN卷积算法获取失败报错的解决方案

/* 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 13:16:24 阅读更多 →
物联网+区块链四大落地方向:设备可信、轻量存证、协同治理与隐私增强

物联网+区块链四大落地方向:设备可信、轻量存证、协同治理与隐私增强

/* 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 13:16:24 阅读更多 →
Django电商性能优化:FastDFS、页面静态化与Redis缓存实战

Django电商性能优化:FastDFS、页面静态化与Redis缓存实战

/* 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 13:15:23 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →