Решение задач оптимизации в коде
Роль этой главы в разделе
Это завершающий мост между математической моделью и рабочим инструментом. Главная цель — переносить постановку в код осознанно — понимать, откуда взялись массивы коэффициентов, что означают статусы солвера и как проверять корректность ответа.
В инженерной практике этот переход чаще всего ломается из-за ошибок в коде — неверный знак неравенства, порядок коэффициентов или границы переменных. Поэтому акцент здесь на дисциплине постановки, проверках и интерпретации результата.
Если после главы вы можете воспроизвести путь "текст задачи -> матрицы -> решение -> проверка ограничений -> вывод для бизнеса", значит раздел освоен на рабочем уровне.
Ручной симплекс и метод потенциалов нужны для понимания алгоритма. В проектах ЗЛП решают библиотеками — они масштабируются на тысячи переменных и используют устойчивые реализации (двухфазный старт, pivoting, иногда метод внутренней точки).
Эта статья — мост между постановкой и Python для анализа данных.
Что вы задаёте солверу
- Вектор
c— коэффициенты цели (дляmaxвlinprog— минус коэффициенты). A_ub,b_ub— каждая строка —a₁x₁ + … ≤ b.A_eq,b_eq— равенства (если есть).bounds— нижняя/верхняя граница каждой переменной (часто(0, None)).- Проверка знаков — ограничение
≥в задачеminчасто переводят в≤умножением на −1 (см. введение).
// Из текста задачи в массивы для солвера (пекарня: max 3x1 + 2x2)
цель_c := [-3, -2] // linprog ищет min, для max — минус коэффициенты
ограничения_A := [[2, 1], [1, 1]]
правая_часть_b := [100, 80]
границы := [(0, бесконечность), (0, бесконечность)]
ответ := Солвер_Линейный(цель_c, ограничения_A, правая_часть_b, границы)
если ответ.статус = "оптимум" то
для каждого i проверить A[i]·x ≤ b[i]
вывести x1, x2, прибыль := 3·x1 + 2·x2
иначе
разобрать: несовместно / неограничено / ошибка постановки
конец
Справочно на Python (scipy.optimize.linprog) — ниже в статье.
Интерактив перед запуском кода
Сначала подберите рабочую постановку в браузере, потом перенесите эти коэффициенты в linprog.
Рекомендуемая последовательность —
- Настройте цель и ограничения в интерактиве.
- Убедитесь, что найден хотя бы один допустимый план.
- Зафиксируйте значения
c1,c2,b1,b2и проверочный план(x1, x2). - Только после этого переносите данные в массивы
c,A_ub,b_ubв Python.
Play ITЗагрузка интерактивного демо…
Как использовать результат после демо —
- если интерактив показывает недопустимость, код почти наверняка тоже вернёт
infeasible; - при расхождении интерактива и кода проверьте знаки и порядок коэффициентов в матрицах;
- если решение в коде отличается немного, это часто вопрос численной точности и формата округления.
Ниже в разделе linprog используйте эти же числа, чтобы быстро убедиться, что пайплайн "модель -> солвер -> интерпретация" работает корректно.
SciPy — linprog
Функция scipy.optimize.linprog принимает задачу в форме минимизации —
min cᵀx
при A_ub x ≤ b_ub
A_eq x = b_eq
lb ≤ x ≤ ub
Для max Z = cᵀx передают c_min = -c.
Пример — станки и сырьё
Та же задача, что в введении —
max 3x₁ + 2x₂
2x₁ + x₂ ≤ 8
x₁ + 2x₂ ≤ 8
x₁, x₂ ≥ 0
Код ITЗагрузка примера кода…
method="highs" (по умолчанию в новых SciPy) — современный солвер; внутри не «ваша» симплекс-таблица с листа, но двойственные оценки доступны в res (см. документацию версии).
На что смотреть в ответе
| Поле | Смысл |
|---|---|
success | найден ли оптимум |
x | значения переменных |
fun | значение минимизируемой цели |
slack | запасы по неравенствам A_ub x ≤ b |
message | несовместность, неограниченность и т.д. |
Всегда подставляйте x в исходные ограничения — как после ручного симплекса.
Ограничение "не меньше" (диета из введения)
Задача — min 40x₁ + 30x₂ при 2x₁ + x₂ ≥ 12, x₁ + 3x₂ ≥ 18, x ≥ 0.
Умножаем каждое ≥ на −1, получаем форму ≤ для linprog —
−2x₁ − x₂ ≤ −12
−x₁ − 3x₂ ≤ −18
c = [40, 30]
A_ub = [[-2, -1], [-1, -3]]
b_ub = [-12, -18]
bounds = [(0, None), (0, None)]
res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method="highs")
# res.fun — минимальная стоимость; res.x — порции x₁, x₂
Частая ошибка — забыть минус у b_ub — солвер сообщит о несовместности или неверном плане.
Сообщения linprog и что они значат
message (типично) | Действие |
|---|---|
| Optimization successful | подставить x, сравнить с ручным примером |
| The problem is unbounded | проверить знаки и лишние ограничения |
| The problem is infeasible | противоречивые строки A, баланс supply/demand |
| Iteration limit | упростить модель, сменить метод, проверить масштаб коэффициентов |
Постановка из "бизнес-языка"
Алгоритм перевода —
- Назвать переменные (
x₁— объём продукта A). - Записать линейную цель.
- Каждое ограничение → строка
A, элементb. - Границы
x→bounds(по умолчаниюx ≥ 0). - Выбрать
minилиmax(знакc).
Нелинейные тарифы, целые количества серверов, бинарные "включить/выключить" — не linprog; нужны milp (целочисленное) или другие солверы.
Google OR-Tools
Библиотека OR-Tools (ortools.linear_solver) подходит для LP/MIP в продакшене —
- выбор backend — GLOP (LP), CBC, SCIP (MIP);
- удобно для назначения, маршрутизации, расписаний.
Типичный паттерн — solver = pywraplp.Solver.CreateSolver("GLOP"), переменные NumVar, ограничения Add, цель Maximize.
Документация Google OR-Tools — эталон для инженерных задач; энциклопедию не дублируем построчно, но после раздела 3.12 вы понимаете, что задаёте солверу.
Скелет OR-Tools (LP)
Код ITЗагрузка примера кода…
Здесь NumVar(0, inf) — то же, что bounds=(0, None) в SciPy; Add — строка A; Maximize — цель без смены знака (в отличие от linprog, где max через min(-c)).
Excel / LibreOffice Solver
Тот же ЗЛП — ячейки — переменные, формула — цель, ограничения через "Solver". Полезно аналитикам; для CI/CD и больших данных — код.
Когда что использовать
| Ситуация | Инструмент |
|---|---|
| Учебный разбор, 2–5 переменных | графика, симплекс |
| Скрипт, прототип, ≤ сотни переменных | scipy.optimize.linprog |
| MIP, логистика, планирование | OR-Tools, Gurobi, CPLEX |
| ML-обучение (нелинейно) | PyTorch / JAX, не LP |
Ограничения численного решения
- Округление — почти оптимальные
xмогут чуть нарушать ограничения на1e-9— в продакшене округляют и проверяют. - Плохая шкала — коэффициенты
10⁻⁶и10⁹в одной задаче — нормируйте единицы. - Несовместность —
success=False— пересмотрите модель, не "крутите M" как на бумаге.
Инженерный workflow для production-модели
- Формализация — собрать модель в явных структурах данных (переменные, ограничения, коэффициенты).
- Валидация — автотесты на маленьких сценариях с известным ответом.
- Решение — запуск солвера с тайм-аутом и логированием статуса.
- Проверка результата — допустимость, значение цели, sanity-check бизнес-ограничений.
- Мониторинг — хранить метрики оптимизаций (время, gap, частота infeasible).
Такой pipeline делает оптимизацию повторяемой и безопасной для CI/CD на уровне команды.
Когда переходить от LP к MIP
Переход обязателен, если переменные имеют логический или целочисленный смысл —
- число серверов, машин, сотрудников;
- бинарный выбор "включить/не включить";
- пороговые условия и дискретные пакеты ресурсов.
LP часто даёт быстрый ориентир, но дробные ответы в таких задачах нельзя внедрять напрямую без целочисленного этапа.
Чувствительность (связь с двойственностью)
В ответе linprog (зависит от версии SciPy) могут быть двойственные значения ограничений — аналог yᵢ* из статьи 6 — насколько изменится оптимум при bᵢ → bᵢ + Δ. Для малых Δ в ЛП часто ΔZ ≈ yᵢ* · Δbᵢ. Это основа what-if анализа в Excel Solver и в планировании мощностей.
Связь с разделом
| Тема в коде | Теория |
|---|---|
A_ub, b_ub | стандартная форма Математическое программирование — введение и постановка задач |
slack в ответе | двойственность Двойственность в линейном программировании |
| сеть supply/demand | транспортная Транспортная задача |
dp[t][s] | Беллман Динамическое программирование и уравнение Беллмана |