Почему эти две версии кода Джулии для одной и той же задачи оптимизации практически идентичны и дают разные результаты? - PullRequest
0 голосов
/ 04 апреля 2019

Я пытаюсь выучить Юлию в образовательных целях.Специально я пытался использовать Джулию и пакет JuMP для решения проблем оперативных исследований.

Я смотрел это замечательное видео с YouTube.Парень по имени Филип Томас показывает диатический пример.Однако это видео было создано в 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

Однако я все еще в замешательстве.Согласно документации оба решателя могут решать MILP (смешанные целочисленные линейные задачи).

Почему это происходит?Почему разные решатели возвращают разные результаты, если оба имеют одинаковый профиль?Я пропустил некоторые детали во время перевода кода?

Заранее спасибо.

1 Ответ

1 голос
/ 05 апреля 2019

Как вы уже обнаружили, значение целевой функции одинаково для обоих решений. Какое решение достигнуто, зависит от пути, по которому решатель достиг его.

Различия в пути могут возникать в оптимизаторе Simplex, используемом для решения отдельных подзадач LP. Переключение последовательности переменных или строк может быть достаточным, чтобы оказаться в другой точке набора решений. Некоторые решатели даже используют генератор случайных чисел, чтобы определить, какая переменная сначала входит в базу в алгоритме Simplex (но не GLPK).

Другой причиной достижения другого решения может быть последовательность, в которой ищется дерево поиска целочисленных переменных. На это, в частности, влияет стратегия поиска (например, сначала глубина, а затем ширина).

...