Поиск кратчайшего пути

В разделе математики под названием "теория оптимизации" есть несколько задач, связанных с расчётом траектории движения объектов. В прошлом я уже делал разбор решения транспортной задачи на примере оптимизации доставки товаров со складов в магазины. Теперь же давайте попробуем использовать Microsoft Excel для решения другой классической задачи из того же раздела - прокладки кратчайшего маршрута из пункта А в пункт Б через несколько промежуточных точек транспортной сети. Частным случаем такой задачи ещё называют "задачу коммивояжера", когда нам нужно по кратчайшему маршруту обойти все точки, не заходя ни в одну из них дважды, а затем вернуться обратно в пункт отправления.

Таблица расстояний

Первое, что нам потребуется для создания модели - это, конечно же, таблица расстояний между пунктами нашей транспортной сети или матрица смежности, как ещё её называют в математике:

Таблица расстояний

Это квадратная таблица со списком городов в первом столбце и этими же городами в шапке, где на пересечении строки и столбца хранится расстояние между соответствующими пунктами. 

Сразу уточним, что:

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

Для удобства работы с таблицей, давайте сразу конвертируем её в динамическую "умную" сочетанием клавиш Ctrl+T или командой Главная - Форматировать как таблицу (Home - Format as Table) и на вкладке Конструктор (Design) дадим ей имя Расстояния.

Модель маршрута

Теперь можно набросать модель маршрута, которую мы потом будем оптимизировать.

Предположим, что мы хотим выехать из Москвы и посетить в любом порядке следующие пункты: Владимир, Рязань, Ярославль, Муром, Клин. После чего вернуться обратно в Москву, в точку старта. Визуально это можно представить вот такой простой таблицей: 

Список промежуточных пунктов и карта Bing

Если захочется добавить наглядности, то можно нанести эти точки на карту и отобразить её прямо в Excel. Для этого можно воспользоваться бесплатной надстройкой Bing Maps через Главная - Надстройки (Home - Add-ins). После вставки карты нужно выделить список наших пунктов и нажать на кнопку Показать местоположения (Show locations) в правом верхнем углу, чтобы отобразить наши города на местности.

Поскольку Excel удобнее при оптимизации работать с числами, а не с текстом, так что давайте присвоим каждому промежуточному пункту уникальный номер - целочисленный ID от 1 до 5, соответственно. Обратите внимание, что это не порядок, в котором мы будем проходить эти пункты, а просто код каждого города.

Рассчитаем длину маршрута, если мы решим посещать эти города для начала именно в таком (очевидно не самом оптимальном) порядке. Для этого нам потребуется найти расстояние от предыдущего до текущего пункта, т.е. число с пересечения строки и столбца нашей матрицы связности Расстояния. Лучше всего для этого подойдет классическая функция ИНДЕКС (INDEX) в связке с функциями ПОИСКПОЗ (MATCH):

Cчитаем расстояния между пунктами

Первый аргумент этой функции - это наша матрица расстояний, а второй и третий - это номер строки и столбца, с пересечения которых мы хотим извлечь значение расстояния. Эти номера мы вычисляем с помощью функций ПОИСКПОЗ, находя позицию текущего и предыдущего городов в первом столбце и в шапке таблицы соответственно.

Останется лишь подсчитать общую длину получившегося маршрута, используя функцию СУММ (SUM):

Считаем общую длину маршрута

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

Оптимизация

Теперь - самое интересное и главное. В каком же порядке нам нужно посетить эти 5 городов, чтобы общая длина маршрута оказалась минимальной? Т.е. другими словами, в каком именно порядке в зелёных ячейках должны располагаться, не повторяясь, целые числа от 1 до 5, чтобы в красной ячейке был минимум?

Для этого нам придётся использовать "тяжелую артиллерию" - встроенную в Microsoft Excel оптимизационную надстройку Поиск решения (Solver). Чтобы её подключить, воспользуемся кнопкой Надстройки Excel на вкладке Разработчик (Developer - Excel Add-ins):

Подключаем надстройку "Поиск решения" (Solver)

После подключения надстройки на вкладке Данные (Data) в правом верхнем углу должна появиться новая кнопка - Поиск решения (Solver), которая нам и нужна.

В открывшемся окне необходимо задать следующие настройки:

Поиск решения

  • в качестве целевой ячейки задать розовую ячейку с общей длиной маршрута, уточнив ниже, что мы хотим её минимизировать;
  • в качестве изменяемых ячеек переменных задать пять зелёных ячеек с номерами промежуточных пунктов, которыми мы "играем" на входе в нашу модель;
  • нажимая на кнопку Добавить (Add) задать три ограничения для зелёных ячеек - они должны быть <=5, они должны быть целыми и они должны быть разными, т.е. не повторяться;
  • в выпадающем списке в нижней части окна выбрать алгоритм эволюционного поиска решения.
После нажатия на кнопку Найти решение (Solve) Microsoft Excel начнёт поиск оптимального маршрута, который будет продолжаться до тех пор, пока в течение какого-то промежутка времени (он задаётся в Параметрах) найденный результат уже не удастся улучшить. Тогда поиск остановится и мы получим сообщение о результатах:

Сообщение о результатах оптимизации

Если результат вам не нравится, то в этом окне можно вернуться к исходным значениям до оптимизации. Если нравится, то сохранить их на листе или же сохранить их как один из сценариев (кнопка Сохранить сценарий) для последующего анализа и сравнения, например, с другими сценариями. Доступ ко всем сохранённым сценариям можно получить на вкладке Данные - Анализ "что если" - Диспетчер сценариев (Data - "What if" analysis - Scenario manager).

Но с ходу очевидно, что подобранный вариант гораздо лучше исходного - 1209 км вместо 1879!

Ссылки по теме



Max
24.10.2025 16:33:15
Николай, спасибо огромное. Как всегда блестяще изложен материал.

Немного скуфской духоты от меня: так любимое всеми выражение "самый оптимальный" бессмысленно, т.к. оптимум подразумевает уникальность. Не бывает "самых" и "не самых" оптимальных решений - оптимальное решение только одно. Спасибо, извините )))
27.10.2025 21:30:16
Спасибо на добром слове!
Насчёт "самый оптимальный" - согласен, масло масляное :D
MCH
14.11.2025 12:25:56
Применительно к задаче коммивояжера можно говорить о глобальном оптимуме и о локальных оптимумах.
Можно получить достаточно неплохое решение относительно быстро, но которое не будет глобальным оптимумом.

В реальных задачах данного типа часто бывает необходимость относительно быстро найти решение, которое будет отличатся от оптимального не более чем на несколько процентов. В условиях того что временные затраты на поиск "самого оптимального" (в терминологиях данной статьи) решения будут очень большие.
Задача коммивояжера относится к классу NP-трудных задач, и относится к числу трансвычислительных уже при относительно небольшом числе городов (>66), если решать ее полным перебором всех перестановок.
MCH
13.11.2025 11:37:45
Попробовал найти оптимальный маршрут проходящий через все города из таблицы смежности (196 городов)
"Поиск решения" спотыкается, долго считает и застрял в локальном оптимуме и не может улучшить решение и это решение далеко от лучшего почти в два раза, путь составил - 109401

Альтернативное решение, используемое жадные алгоритмы + 2-opt оптимизацию + 3-opt оптимизацию, смогло найти путь длиной - 57177 (не обязательно, что данное решение является глобальным оптимумом)
Различные алгоритмы по реализации задачи коммивояжера публиковал здесь

Найденный маршрут на исходных данных из примера длиною в 57177 можно посмотреть здесь

UPD: решение алгоритмом LKH- 56990
Наверх