Я пытаюсь изучить Джулию в образовательных целях. В частности, я пытаюсь использовать 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. Вы правы: они прибывают в один и тот же пункт назначения. Но почему они идут разными путями, чтобы достичь этого конечного результата? Объясняется ли это деталями реализации решателя?
Как вы уже обнаружили, значение целевой функции одинаково для обоих решений. Какое решение будет достигнуто, зависит от пути, который прошел решатель, чтобы достичь его.
Различия в пути могут возникать в оптимизаторе Simplex, используемом для решения отдельных подзадач LP. Переключения последовательности переменных или строк может быть достаточно, чтобы оказаться в другой точке набора решений. Некоторые решатели даже используют генератор случайных чисел, чтобы определить, какая переменная первой входит в базу в симплексном алгоритме (но не GLPK).
Другой причиной достижения другого решения может быть последовательность, в которой выполняется поиск целочисленных переменных в дереве поиска. Среди прочего, на это влияет стратегия поиска (например, сначала в глубину, а затем в ширину).
Выглядит как две равные цели, что означает: неуникальные оптимумы. В этом нет ничего плохого.