ЛР-02: Транспортная задача#
1) Что это за задача#
Транспортная задача — это специальный случай линейного программирования, где нужно распределить поставки от нескольких источников к нескольким потребителям так, чтобы:
закрыть весь спрос;
не превысить запасы у поставщиков;
получить минимальную суммарную стоимость перевозки.
В отличие от общей производственной задачи, здесь переменные означают не объёмы выпуска, а объёмы перевозок между конкретными парами узлов.
По-человечески это значит так: каждая клетка в транспортной таблице — это отдельный маршрут, и для каждого маршрута мы выбираем свой объём перевозки.
Если есть \(m\) поставщиков и \(n\) потребителей, то переменных становится \(m \cdot n\):
2) Из чего состоит модель#
Поставщики#
Пусть у нас есть запасы:
Здесь \(a_i\) — доступный объём у поставщика \(i\).
Потребители#
Пусть есть спрос:
Здесь \(b_j\) — потребность потребителя \(j\).
Стоимости перевозки#
Для каждой пары \((i, j)\) задаётся стоимость:
Это стоимость доставки одной единицы груза от поставщика \(i\) к потребителю \(j\).
Переменные решения#
Это объём потока по маршруту \((i, j)\).
3) Целевая функция#
Обычно транспортная задача формулируется как задача минимизации полной стоимости:
Здесь каждая перевозка умножается на свою стоимость, а затем все затраты суммируются.
4) Ограничения#
Ограничения по запасам#
Если поставщик должен распределить весь свой запас, то:
Смысл: всё, что есть у поставщика \(i\), должно быть отправлено.
Ограничения по спросу#
Если весь спрос должен быть закрыт, то:
Смысл: каждый потребитель получает ровно столько, сколько ему нужно.
Неотрицательность#
Отрицательная перевозка невозможна.
5) Закрытая и открытая транспортная задача#
Закрытая задача#
Если суммарный запас равен суммарному спросу:
то задача называется закрытой или сбалансированной.
В этом случае можно использовать равенства и интерпретировать задачу буквально: весь груз распределён, весь спрос удовлетворён.
Открытая задача#
Если суммы не совпадают:
то задача открытая.
Чтобы привести её к стандартной транспортной форме, добавляют фиктивный узел.
6) Фиктивный поставщик и фиктивный потребитель#
Если запасов больше, чем спроса#
Когда:
добавляют фиктивного потребителя с потребностью
Смысл: часть груза формально остаётся невостребованной или уходит в резерв.
Если спрос больше, чем запасов#
Когда:
добавляют фиктивного поставщика с запасом
Смысл: недостающий объём покрывается условным источником, который показывает дефицит системы.
Какие брать стоимости для фиктивного узла#
Методически важно заранее договориться о смысле:
Если фиктивный узел означает нейтральный остаток или резерв, часто ставят нулевую стоимость.
Если фиктивный узел означает дефицит, штраф или аварийное покрытие, стоимость можно сделать положительной.
В этой лабораторной главное — корректно сбалансировать задачу и объяснить смысл выбранной стоимости.
7) Матричная постановка для linprog#
Библиотека scipy.optimize.linprog работает с вектором переменных. Поэтому матрицу перевозок нужно развернуть в один длинный вектор.
Если
то для Python удобно использовать развертку по строкам:
Тогда:
вектор цели
c— это те же стоимостиcosts.flatten();ограничения по поставщикам дают строки матрицы
A_eq, где для каждого поставщика суммируются все его исходящие перевозки;ограничения по потребителям дают строки
A_eq, где для каждого потребителя суммируются входящие перевозки;вектор правых частей
b_eqсоставляется из запасов и спроса.
Если по-человечески, Python не хранит красивую таблицу перевозок у себя в голове. Для решателя мы временно превращаем таблицу в одну длинную ленту чисел, решаем задачу, а потом снова собираем ответ обратно в привычную таблицу.
8) Почему здесь удобно использовать linprog#
В этой лабораторной нам важна не табличная техника сама по себе, а корректная постановка и проверяемость результата.
linprog полезен потому, что:
позволяет решить задачу как обычное ЛП;
помогает проверить, что модель действительно собрана правильно;
даёт воспроизводимый результат, который удобно обсуждать в отчёте;
легко расширяется на прикладные сценарии, где есть штрафы, дефицитные маршруты и фиктивные узлы.
9) Как интерпретировать решение#
После решения важно не ограничиваться ответом “минимальная стоимость равна …”.
Нужно ответить на вопросы:
Какие маршруты реально используются?
Какие маршруты остаются нулевыми?
Выполнен ли баланс по строкам и столбцам?
Есть ли перегруженные или дорогие направления?
Если использовался фиктивный узел, что он означает на языке предметной области?
Именно эта интерпретация превращает математический ответ в управленческий вывод.
10) Что изучаем в этой ЛР#
В этой работе целенаправленно изучаем:
построение транспортной модели из прикладной ситуации;
проверку баланса спроса и предложения;
переход к закрытой постановке через фиктивный узел;
подготовку
c,A_eq,b_eq,boundsдляlinprog;анализ полученного плана перевозок и суммарной стоимости.
Не изучаем:
транспортный симплекс;
MODI;
Vogel;
другие табличные эвристики как основной инструмент этой ЛР.
11) Типичные ошибки#
Перепутали строки и столбцы матрицы затрат.
Неверно проверили баланс запасов и спроса.
Забыли добавить фиктивный узел в открытой задаче.
Добавили фиктивный узел, но не объяснили смысл его стоимости.
Неправильно развернули матрицу перевозок в вектор.
В
A_eqпотеряли часть переменных или перепутали индексы.Посмотрели только на целевую функцию и не проверили суммы по строкам и столбцам.
Увидели дорогой маршрут, но не объяснили, почему solver всё равно его использует или избегает.
12) Что должно быть в отчёте#
Таблица запасов поставщиков.
Таблица спроса потребителей.
Матрица затрат.
Проверка баланса задачи.
Если задача открытая — объяснение, какой фиктивный узел добавлен и зачем.
Линейная постановка задачи через \(x_{ij}\).
Векторно-матричная запись для
linprog.Листинг Python-кода.
Итоговая матрица плана перевозок.
Проверка допустимости по строкам и столбцам.
Итоговая стоимость и её содержательная интерпретация.