一. 前言
问题并不复杂, 只是借此机会顺便搞一下excel
的规划求解器, 看看能不能让这个组件还能在2019及以上版本的64位excel中使用.
问题来源于广告书.
二. 问题解析
这是个非线性模型, 需要转换为线性模型.
【教学视频】优化 | 线性化(2): 连续变量 * 0-1变量的线性化
三. 问题的求解
3.1 Excel
excel预置的规划求解器最后能在64位上使用的是2016版, 之后的版本只能在32位上使用.
需要注意的是预置版本为封闭的商业软件, 不可修改, 提供商为: Excel Solver Online Help | solver
有个第三方的开源求解器组件:
OpenSolver for Excel – The Open Source Optimization Solver for Excel
但可惜的是也不继续更新了, 应该和预置版本类似, 都是只能在2016版(64bit)下使用.
还有个对求解器进行优化的dll
, host.kelley.iu.edu/albrightbooks/solver.htm
求解没什么好说的, 相关内容见: Excel规划求解加载项 | Lian
1 | 2 | 3 | |||
---|---|---|---|---|---|
1 | 1526.666667 | 473.3333333 | 0 | 2000 | |
2 | 508.8888889 | 1991.111111 | 0 | 2500 | |
3 | 508.8888889 | 691.1111111 | 0 | 1200 | |
2544.444444 | 3155.555556 | 0 | target: | 6160 |
3.2 ortools
求解并不困难, 对着数学模型敲击即可, 基本上数学模型和代码是互通的.
使用约束求解器来处理.
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
variables = {}
for i in range(1, 4):
for j in range(1, 4):
variables[(i, j)] = model.NewIntVar(0, 2500, "x%i_%i" % (i, j))
model.add(sum(variables[(1, j)] for j in range(1, 4)) <= 2000)
model.add(sum(variables[(2, j)] for j in range(1, 4)) <= 2500)
model.add(sum(variables[(3, j)] for j in range(1, 4)) <= 1200)
# 在运算中, 减少浮点数带来的精度丢失问题
model.add(60 * sum(variables[(i, 1)] for i in range(1, 4)) <= 100 * variables[(1, 1)]) # 数据缩放, x100
model.add(15 * sum(variables[(i, 2)] for i in range(1, 4)) <= 100 * variables[(1, 2)])
model.add(20 * sum(variables[(i, 1)] for i in range(1, 4)) >= 100 * variables[(3, 1)])
model.add(60 * sum(variables[(i, 2)] for i in range(1, 4)) >= 100 * variables[(3, 2)])
model.add(50 * sum(variables[(i, 3)] for i in range(1, 4)) >= 100 * variables[(3, 3)])
# 数据缩放, x100
model.Maximize(
290 * sum(variables[(i, 1)] for i in range(1, 4))
+ 245 * sum(variables[(i, 2)] for i in range(1, 4))
+ 195 * sum(variables[(i, 3)] for i in range(1, 4))
- 200 * sum(variables[(1, j)] for j in range(1, 4))
- 150 * sum(variables[(2, j)] for j in range(1, 4))
- 100 * sum(variables[(3, j)] for j in range(1, 4))
)
status = solver.Solve(model)
print(status)
if status == cp_model.OPTIMAL:
print("objective value:", solver.ObjectiveValue() / 100) # 目标值 / 100, 恢复正常数据
print("sudoku solution:")
results = []
for i in range(1, 4):
data = []
for j in range(1, 4):
data.append(solver.Value(variables[(i, j)]))
results.append(' | '.join([str(x) for x in data]))
print('\n'.join(results))
objective value: 6159.35
sudoku solution:
1526 | 474 | 0
509 | 1991 | 0
508 | 692 | 0
需要注意的是, 在涉及浮点数运算时, 为了降低浮点数精度的影响, 可以对数据进行缩放处理.
四. 小结
还是没办法让2019以上的64位版本的excel启用这个规划求解器组件.