Excel Solver多循环耗时过长,求年度收益优化提速方案
提速Excel Solver循环求解年度收益最大化问题的可行方案
我明白你现在的痛点——用VBA循环调用Solver处理365天的小时数据,结果要跑4-5小时,确实太影响效率了。结合你的业务场景和现有代码,我整理了几个针对性的提速方案,你可以逐一尝试:
一、先优化VBA代码与Excel运行环境
这些都是立竿见影的小改动,不用调整核心逻辑:
- 删除冗余的Solver设置:你的代码里连续调用了两次
SolverOk,这完全是重复操作,直接删掉第二次调用,一次就足够初始化求解器设置了。每次SolverReset和SolverOk都有额外开销,能省则省。 - 关闭屏幕刷新与事件触发:在循环开始前加上这两行代码,循环结束后再恢复:
这能大幅减少Excel界面渲染的耗时,尤其是循环次数多的时候。Application.ScreenUpdating = False Application.EnableEvents = False ' 循环代码... Application.ScreenUpdating = True Application.EnableEvents = True - 禁用自动计算:如果你的工作表有大量联动公式,每次Solver求解后自动计算会拖慢速度。循环前设置:
循环结束后改回Application.Calculation = xlCalculationManualxlCalculationAutomatic,甚至可以在每次SolverSolve后只手动计算必要的区域(比如Range("V13").Calculate),而不是全表计算。
二、优化Solver引擎与参数
这是影响求解速度的核心因素:
- 检查是否可以切换到线性求解器:如果你的收益-成本约束和目标函数都是线性的(没有平方、指数、对数这类非线性运算),直接把
Engine:=1改成Engine:=2(单纯形LP引擎)。线性求解器的速度比GRG非线性快得多,单次求解时间可能直接砍到原来的1/5甚至更少,这是最值得优先验证的点! - 调整GRG非线性引擎的参数:如果必须用非线性引擎,调用
SolverOptions来优化迭代效率,比如:
适当提高收敛阈值(比如从默认的0.0001调到0.001)可以减少不必要的迭代次数;如果你的数据量级稳定,关闭自动缩放也能节省时间。SolverOptions Convergence:=0.001, Iterations:=1000, AutoScale:=False
三、优化数据结构与求解逻辑
- 用固定小工作表承载单日模型:每次在大表中动态定位行号,Solver需要额外解析引用地址。你可以新建一个名为
DailyModel的小工作表,把单日的输入数据(G、D列等)复制到这个表的固定区域,约束和变量都用这个表的固定单元格引用。循环时只需要更新DailyModel的数据,调用Solver求解后再把结果复制回原表对应行。这样能减少Solver解析动态引用的开销。 - 尝试批量处理多天数据(业务允许的话):如果相邻几天的决策没有跨天约束(比如没有库存结转、产能累积这类关联),可以尝试合并几天(比如7天)作为一个求解单元,减少循环次数。不过这得看你的业务逻辑是否允许,要是每天决策完全独立,这步收益不大,但如果有部分共享约束,能有效减少总求解时间。
四、切换到Python方案(长期最优解)
你之前说没找到有效的Python实现,其实用scipy.optimize就能完美替代Excel Solver,而且速度快很多,还能并行处理。给你一个简化的框架:
线性场景示例代码(如果你的模型是线性的)
import pandas as pd from scipy.optimize import linprog from joblib import Parallel, delayed # 用来并行处理,提速更明显 # 读取年度数据 df = pd.read_excel('你的数据文件.xlsx') # 定义单日求解函数 def solve_single_day(day_data, f3_val, f6_val, f12_val): # 目标函数:最大化收益,转成最小化负收益(linprog默认求最小) # 这里需要替换成你V13单元格对应的收益计算公式,把它拆解成变量H、I、J的线性组合 c = -day_data['收益计算系数'].values # 示例,实际要根据你的公式推导 # 整理约束条件:A_ub @ x <= b_ub,A_eq @ x = b_eq A_ub = [] b_ub = [] # H <= G 的约束:x0 <= G值 for g in day_data['G']: A_ub.append([1, 0, 0]) b_ub.append(g) # I <= D 且 I >=0 的约束 for d in day_data['D']: A_ub.append([0, 1, 0]) b_ub.append(d) A_ub.append([0, -1, 0]) b_ub.append(0) # J <= F3、J <= M、J >=0 的约束 for m in day_data['M']: A_ub.append([0, 0, 1]) b_ub.append(min(f3_val, m)) A_ub.append([0, 0, -1]) b_ub.append(0) # 其他约束(比如N列、O列的约束)按同样逻辑整理 # 变量边界 bounds = [(0, None), (0, None), (0, None)] # H、I、J的上下限,根据实际调整 # 求解 res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs') return res.x # 读取全局参数(F3、F6、F12这些固定值) f3 = df.loc[2, 'F'] # 假设F3在第3行F列,实际根据你的表调整 f6 = df.loc[5, 'F'] f12 = df.loc[11, 'F'] # 按天拆分数据(每24行一天) daily_blocks = [df.iloc[i:i+24] for i in range(0, len(df), 24)] # 并行求解(用所有CPU核心,速度翻倍) results = Parallel(n_jobs=-1)(delayed(solve_single_day)(block, f3, f6, f12) for block in daily_blocks) # 把结果写回原表 for idx, block in enumerate(daily_blocks): df.loc[block.index, ['H', 'I', 'J']] = results[idx] # 保存结果 df.to_excel('求解结果.xlsx', index=False)
如果是非线性模型,就用scipy.optimize.minimize,定义好目标函数和约束条件即可。Python的优势在于,求解器效率更高,还能并行处理365天的数据,把总耗时压缩到几十分钟甚至更短。
优先建议你先尝试前两步(VBA优化+切换线性引擎),这两步不需要改太多代码,就能快速看到效果;如果还是达不到预期,再考虑数据结构优化或者切换到Python方案。
内容的提问来源于stack exchange,提问作者Omur
相关产品推荐
相关产品推荐

