Почему эти две версии кода Julia для одной и той же задачи оптимизации практически идентичны и дают разные результаты?

Я пытаюсь изучить Джулию в образовательных целях. В частности, я пытаюсь использовать Julia и пакет 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

Я использую текущую версию Джулии (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 (смешанные целочисленные линейные задачи).

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

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

Выглядит как две равные цели, что означает: неуникальные оптимумы. В этом нет ничего плохого.

sascha 04.04.2019 00:13

Хорошо, спасибо @sascha. Вы правы: они прибывают в один и тот же пункт назначения. Но почему они идут разными путями, чтобы достичь этого конечного результата? Объясняется ли это деталями реализации решателя?

Pedro Delfino 04.04.2019 23:32
Загадки Python - Генерация простых чисел!
Загадки Python - Генерация простых чисел!
Обычно существует несколько способов решения задач даже пограничной сложности. Как же определить оптимальное и эффективное решение?
0
2
182
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

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

Другие вопросы по теме