为什么针对同一优化问题的这两个Julia代码版本几乎相同,并产生不同的结果?

时间:2019-04-03 21:56:01

标签: optimization julia solver glpk julia-jump

我正出于学习目的尝试学习Julia。特别是,我尝试使用Julia和JuMP软件包来解决运营研究问题。

我正在观看youtube上很棒的video。一个名叫菲利普·托马斯(Philip Thomas)的人显示了一个教义的例子。但是,该视频是2014年制作的。此后,朱莉娅得到了发展。

他使用了以下代码:

#=
We are going to the thrift store and need 99 cents. What is the least amount of
weight we need to carry?

i.e. a knapsack problem

We specify that you need at least 99 cents - does the answer change if you need exact change?
=#

using JuMP
using Cbc # Open source solver. Must support integer programming.

m = Model(solver=CbcSolver())

# Variables represent how many of each coin we want to carry
@defVar(m, pennies >= 0, Int)
@defVar(m, nickels >= 0, Int)
@defVar(m, dimes >= 0, Int)
@defVar(m, quarters >= 0, Int)

# We need at least 99 cents
@addConstraint(m, 1 * pennies + 5 * nickels + 10 * dimes + 25 * quarters >= 99)

# Minimize mass (Grams)
# (source: US Mint)
@setObjective(m, Min, 2.5 * pennies + 5 * nickels + 2.268 * dimes + 5.670 * quarters)

# Solve
status = solve(m)

println("Minimum weight: ", getObjectiveValue(m), " grams")
println("using:")
println(round(getValue(pennies)), " pennies") # "round" to cast as integer
println(round(getValue(nickels)), " nickels")
println(round(getValue(dimes)), " dimes")
println(round(getValue(quarters)), " quarters")

他的代码返回以下结果:

Minimum weight: 22.68 grams
using:
0.0 pennies
0.0 nickels
0.0 dimes
4.0 quarters

我正在使用当前版本的Julia(1.0)。此外,我正在使用最新版本的JUMP。当前版本的Julia和上面的代码在语法上有所不同。经过反复试验,我能够正确翻译代码以使其在Julia 1.0中运行:

#=
We are going to the thrift store and need 99 cents. What is the least amount of
weight we need to carry?
i.e. a knapsack problem
We specify that you need at least 99 cents - does the answer change if you need exact change?
=#

using JuMP
using GLPK
using Cbc # Open source solver. Must support integer programming.

model = Model(with_optimizer(GLPK.Optimizer))

# Variables represent how many of each coin we want to carry
@variable(model, pennies >= 0, Int)
@variable(model, nickels >= 0, Int)
@variable(model, dimes >= 0, Int)
@variable(model, quarters >= 0, Int)

# We need at least 99 cents
@constraint(model, 1 * pennies + 5 * nickels + 10 * dimes + 25 * quarters >= 99)

# Minimize mass (Grams)
# (source: US Mint)
@objective(model, Min, 2.5 * pennies + 5 * nickels + 2.268 * dimes + 5.670 * quarters)

# Solve
optimize!(model)

println("Minimum weight: ", objective_value(model), " grams")
println("using:")
println(round(value(pennies)), " pennies") # "round" to cast as integer
println(round(value(nickels)), " nickels")
println(round(value(dimes)), " dimes")
println(round(value(quarters)), " quarters")

有趣的是终端返回的结果:

Minimum weight: 22.68 grams
using:
0.0 pennies
0.0 nickels
0.0 dimes
4.0 quarters

如您所见,关于最小重量的最终结果保持不变。但是,关于要获取哪种硬币的决定从10角变为4角。

除了语法上的更改外,我还修改了求解器,因为起初我无法运行Cbc。

此后,我通过以下简单修改再次更改为Cbc:

model = Model(with_optimizer(Cbc.Optimizer))

在上面的修改之后,“代码翻译”更接近于原始语言,并选择了Cbc作为求解器。奇怪的是,程序返回了预期的结果:

Minimum weight: 22.68 grams
using:
0.0 pennies
0.0 nickels
10.0 dimes
0.0 quarters

但是,我仍然感到困惑。根据{{​​3}},两个求解器都可以求解MILP(混合整数线性问题)。

为什么会这样?如果不同的求解器具有相似的配置文件,为什么它们返回的结果也不同?我在代码翻译过程中错过了一些细节吗?

谢谢。

1 个答案:

答案 0 :(得分:1)

您已经发现目标函数的值对于两个解决方案都是相同的。达到哪种解决方案取决于求解器到达该解决方案的路径。

在用于解决单个LP子问题的Simplex优化器中可能会出现路径差异。切换变量或行的顺序可能足以结束解决方案集的另一点。一些求解器甚至使用随机数生成器来确定哪个变量首先输入Simplex算法中的基数(而不是GLPK)。

达到不同解决方案的另一个原因可能是搜索整数变量的搜索树的顺序。搜索策略(例如,深度优先vs广度优先)会影响搜索结果。