Всегда много путей достичь цель есть. Испробовать их все должны вы.

Магистр Йода, из книги «Ученик джедая. Битва за истину», Джуд Уотсон

Задача коммивояжёра, пожалуй самая известная задача комбинаторной оптимизации. Сводится она к поиску самого короткого маршрута проходящего через все заданные пункты. Долгое время задача коммивояжёра считалась трансвычислительной задачей. Нахождение точного решения методом перебора на классическом компьютере размером с Землю уже при n > 66 вершин, требовало бы время большее, чем время существования вселенной.

Однако есть способы, как значительно ускорить нахождение минимального решения за разумное время. Мы будем говорить о точном решении, но всё же стоит помнить о том, что используя в расчётах арифметику с плавающей запятой хоть и двойной точности, мы можем получить накопление ошибки. Данные ошибки вычислений в некоторых ситуациях могут немного повлиять на конечный результат, но мы с вами постараемся избежать таких узких мест.

В данной работе хочу предложить вам быстрые open-source решения, которые вы можете применять в своих проектах.

Ранее мы уже говорили о способах нахождения точного решения методом динамического программирования и методе ветвей и границ, а также методе целочисленного линейного программирования. Последний и стал основой для текущего решения.

И так приступим.

1. Евклидова задача коммивояжёра

Нет, это не реплика знаменитого квадрата, так выглядит полносвязный граф всего на 200 вершин
Нет, это не реплика знаменитого квадрата, так выглядит полносвязный граф всего на 200 вершин

Вы можете видеть полносвязный симметричный граф на плоскости. Полносвязный означает, что каждая вершина данного графа связана с другой вершиной ребром. Из всего множества данных рёбер мы должны выбрать только n рёбер, где n – число вершин, которые и будут составлять минимальный Гамильтонов цикл.

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

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

Триангуляция Делоне
Триангуляция Делоне

Триангуляция Делоне очень мощный метод, который позволяет покрыть исходный граф симплексами, имеющими максимальное значение минимального угла, что в свою очередь позволяет избегать тонких треугольников. Трудоёмкость данной задачи в худшем случае не превышает O(N log N), что играет нам на руку.

Далее мы будем создавать ‘каркас’ графа, используя триангуляцию Делоне, в котором и будем искать минимальное решение. Некоторые исследователи используют полученный граф для быстрого нахождения приближённого решения, но оно редко бывает оптимальным, так как зачастую не включает лучшее решение. Мы же пойдём дальше, добавив к множеству ‘каркаса’ дополнительный набор рёбер. Процедура будет звучать следующим образом:

Для каждой пары смежных рёбер графа, образованного посредством триангуляции Делоне добавляется ещё одно ребро, соединяющее не смежные вершины данных рёбер.

Граф для поиска минимального решения
Граф для поиска минимального решения

В результате получается граф, который точно содержит минимальное решение. Данный факт получен эмпирическим путём, а затем многократно подтверждён численным моделированием. Однако чёткого математического доказательства у меня пока нет. Возможно кто-то из читателей сможет доказать данную теорему. Укажем, что одно из рёбер для произвольной вершины всегда будет входить во множество ‘каркаса’, так как оно будет минимальным текущим для вершины, а вот второе необязательно.

Минимальный Гамильтонов цикл
Минимальный Гамильтонов цикл

Обратите внимание, как раз тот случай, когда для построения точного минимального пути рёбер графа образованных только триангуляции Делоне недостаточно.

Что нам даёт подход описанный выше?

В полносвязанном симметричном графе содержится (n^2 – n) / 2 рёбер, а в итоговом графе по схеме выше только ~ O(c * n), где с < 10 константа – приближённая верхняя оценка для Евклидовой плоскости, реально несколько меньше 10.

Пример: для n ≤ 20: эффекта нет, но уже при n = 200: нужно приблизительно в десять раз меньше рёбер, а при n = 2000: нужно, примерно, в сто раз меньше рёбер, как если бы мы использовали полносвязный граф. Любое отброшенное ребро значительно уменьшает количество вычислений для любого алгоритма поиска оптимального пути.

В итоговом графе мы можем искать минимальное решение любым известным нам методом. Например, перебором грубой силой, ветвями и границами, муравьиным и др. Однако самый лучший эффект дают методы, использующие линейное программирование.

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

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

В решении целочисленных задач используются вариации симплекс-метода. Алгоритм начинает решение из некой вершины и ‘шагает’ по швам в том направлении, которое соответствует минимальному решению и игнорирует другие не оптимальные направления. Решение находится за конечное число шагов.

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

В нецелочисленном линейном программировании часто алгоритм не желает двигаться именно по ‘швам мяча’, часто он сокращает путь по ‘целине’, хотя и строго линейно. Целочисленное же программирование, за счёт дополнительных усилий (вычислительно не дешёвых) не позволяет отклоняться от прямых маршрутов.

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

Знающий читатель может возразить, что в метафоре должен быть многомерный гиперкуб, мы же будем исходить из того, что мяч проще представить.

Пятимерный гиперкуб (Пентеракт).
Пятимерный гиперкуб (Пентеракт).

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


Раз уж мы высказались за open-source решения, то было бы уместно использовать только те инструменты при реализации, которые с одной стороны были бы в открытом доступе, а с другой имели достаточно высокую производительность.

Ваш покорный слуга сделал ставку на распространённые инструменты, которые показались ему наиболее удобными для реализации текущей задачи:

1.       NetworkX — библиотека, для работы с графами и иными сетевыми структурами. Является свободным программным обеспечением, распространяемым по лицензии BSD.

2.       SciPy — библиотека с открытым исходным кодом, предназначенная для выполнения научных и инженерных расчётов.

3.       Matplotlib — библиотека для визуализации данных двумерной и трёхмерной графикой, распространяется на условиях BSD-подобной лицензии. Она не нужна для расчётов, а используется только для визуализации графов.

4.       Pulp — Открытая библиотека предназначенная для решения задач линейного программирования.

Для решения задач целочисленного (смешанного) линейного программирования, автором применялись следующие решатели:

Все указанные солверы подходят для нахождения решения задачи коммивояжёра. Однако решатели CBC и ORTOOLS, хоть и не являются самыми быстрыми в предложенном списке, имеют бóльшую точность при нахождении решения. А так как автор утверждает, что может получить именно точное решение, то это критично. Хотя решатель ORTOOLS от Google и задействует все ядра процессора, на конкретно этой задаче в разы проигрывает однопоточному консольному решателю CBC от COIN-OR. CBC входит в поставку по умолчанию Pulp, где является основным решателем.

Несколько замечаний по поводу текущего алгоритма:

  • В текущей реализации мне показалось более удачным задавать только список вершин, длину ребра алгоритм рассчитывает самостоятельно (через Евклидову норму). На больших размерностях графа, матрица смежности становится просто монструозной. В данном случае мы используем networkx.Graph(), который сохраняет только нужные рёбра в виде списков смежности.

  • Не допускается задавание задание вершин в виде одной линии, так как для триангуляции Делоне требуется минимум двумерное пространство.

  • Не допускаются две вершины с одинаковыми координатами, так как это не имеет смысла и мешает расчётам. Такие вершины нужно считать, как одну.

  • Алгоритм способен решать задачу, в трёхмерном или выше пространствах. Для этого требуется внести небольшие правки. Однако от увеличения метрики пространства сильно ухудшается качество отсева рёбер, а следовательно и прирост производительности.

Сам метод решения симметричной задачи коммивояжёра мы уже рассматривали в прошлых статьях и здесь только очень кратко повторим основные тезисы:

  • Каждое ребро задающего графа это целочисленная переменная решателя линейных уравнений. Сколько рёбер в графе, столько и переменных. Каждая из таких переменных может принимать только два значения 1 или 0. (Выбираем данное ребро в граф результат или нет). В результате решения количество единиц должно быть равно числу вершин графа.

  • Каждое ребро имеет свою длину и эта длина указывается в целевой функции при соответствующей переменной.

  • Для каждой вершины существует строго два пути: по одному мы приходим в вершину, через другой покидаем её. Мы отражаем это условие в виде линейного условия, что сумма всех рёбер для каждой из вершин равно двум.

  • После решаем систему линейных уравнений. Если в результате решения граф разваливается на несколько подциклов, то вносим дополнительные условия нераспадаемости и снова решаем полученную систему уравнений. 

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

Простой пример решения, модель PuLP
[[ 0  8 34 40 50 71]
 [ 8  0 42 44 50 74]
 [34 42  0 26 54 58]
 [40 44 26  0 30 34]
 [50 50 54 30  0 32]
 [71 74 58 34 32  0]]
tsp:
MINIMIZE
8*x00 + 34*x01 + 40*x02 + 50*x03 + 71*x04 + 42*x05 + 44*x06 + 50*x07 + 
74*x08 + 26*x09 + 54*x10 + 58*x11 + 30*x12 + 34*x13 + 32*x14 + 0
SUBJECT TO
_C1: x00 + x01 + x02 + x03 + x04 = 2
_C2: x00 + x05 + x06 + x07 + x08 = 2
_C3: x01 + x05 + x09 + x10 + x11 = 2
_C4: x02 + x06 + x09 + x12 + x13 = 2
_C5: x03 + x07 + x10 + x12 + x14 = 2
_C6: x04 + x08 + x11 + x13 + x14 = 2
VARIABLES
0 <= x00 <= 1 Integer
0 <= x01 <= 1 Integer
0 <= x02 <= 1 Integer
0 <= x03 <= 1 Integer
0 <= x04 <= 1 Integer
0 <= x05 <= 1 Integer
0 <= x06 <= 1 Integer
0 <= x07 <= 1 Integer
0 <= x08 <= 1 Integer
0 <= x09 <= 1 Integer
0 <= x10 <= 1 Integer
0 <= x11 <= 1 Integer
0 <= x12 <= 1 Integer
0 <= x13 <= 1 Integer
0 <= x14 <= 1 Integer

x00 (1, 0) = 1.0 weight = 8
x01 (2, 0) = 1.0 weight = 34
x05 (2, 1) = 1.0 weight = 42
x12 (4, 3) = 1.0 weight = 30
x13 (5, 3) = 1.0 weight = 34
x14 (5, 4) = 1.0 weight = 32
Result = 180

Шаг 1
Шаг 1
tsp:
MINIMIZE
8*x00 + 34*x01 + 40*x02 + 50*x03 + 71*x04 + 42*x05 + 44*x06 + 50*x07 + 
74*x08 + 26*x09 + 54*x10 + 58*x11 + 30*x12 + 34*x13 + 32*x14 + 0
SUBJECT TO
_C1: x00 + x01 + x02 + x03 + x04 = 2
_C2: x00 + x05 + x06 + x07 + x08 = 2
_C3: x01 + x05 + x09 + x10 + x11 = 2
_C4: x02 + x06 + x09 + x12 + x13 = 2
_C5: x03 + x07 + x10 + x12 + x14 = 2
_C6: x04 + x08 + x11 + x13 + x14 = 2
_C7: x00 + x01 + x05 <= 2
_C8: x12 + x13 + x14 <= 2
VARIABLES
0 <= x00 <= 1 Integer
0 <= x01 <= 1 Integer
0 <= x02 <= 1 Integer
0 <= x03 <= 1 Integer
0 <= x04 <= 1 Integer
0 <= x05 <= 1 Integer
0 <= x06 <= 1 Integer
0 <= x07 <= 1 Integer
0 <= x08 <= 1 Integer
0 <= x09 <= 1 Integer
0 <= x10 <= 1 Integer
0 <= x11 <= 1 Integer
0 <= x12 <= 1 Integer
0 <= x13 <= 1 Integer
0 <= x14 <= 1 Integer

x00 (1, 0) = 1.0 weight = 8
x01 (2, 0) = 1.0 weight = 34
x07 (4, 1) = 1.0 weight = 50
x09 (3, 2) = 1.0 weight = 26
x13 (5, 3) = 1.0 weight = 34
x14 (5, 4) = 1.0 weight = 32
Result = 184

Шаг 2
Шаг 2

Евклидова задача коммивояжёра подходит для расчёта маршрута полёта квадрокоптеров. Использование кратчайшего маршрута вполне применимо в движении в автоматическом режиме. Например, посещение определённых точек местности при аэрофотосъёмке с малой высоты, доставка большого числа небольших объектов множеству адресатов (почта).

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

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

Проиллюстрируем:

Обход графа на 200 вершин: A) вдоль оси x; B) минимальный Гамильтонов цикл;
Обход графа на 200 вершин: A) вдоль оси x; B) минимальный Гамильтонов цикл;

На приведённом выше изображении путь, полученный благодаря решению задачи коммивояжёра, примерно в 6 раз короче. Чем больше вершин в графе, тем выше данное соотношение.

Зависимость времени работы алгоритма от размера входных данных
Зависимость времени работы алгоритма от размера входных данных

Существует важное применение задачи коммивояжёра, где не подходит описанный симметричный алгоритм - это расчёт дорожных расстояний. Так как дорожная сеть, как правило, состоит из криволинейных участков местности, а также односторонних участков пути, мы не можем использовать Эвклидову задачу коммивояжёра. В таком случае нужно использовать ассиметричную задачу коммивояжёра.

Ассиметричная задача примеяется так же для поиска кратчайшей общей суперпоследовательности, которая в сою очередь используется при секвени́ровании ДНК, а так же в сжатии данных.

2. Асимметричная задача коммивояжёра

Мы будем использовать описание задачи в формулировке Данцига, Фултона, Джонсона.

Решать будем так же с помощью целочисленного линейного программирования.

От себя добавим только одно изменение: мы не будем закладывать все условия, гарантирующие исключение подтуров. Если мы укажем в условии решения сразу все ограничения, а их число близко к 2^n, то задача становится практически не решаемой при сколько-нибудь значимых n.

Вместо этого предлагаю разбить задачу на ряд более простых подзадач, последовательное решение которых приведёт нас к результату гораздо быстрее. То есть мы будем добавлять только те ограничения, которые будут необходимы на текущем этапе для конкретной задачи. После чего будем перезапускать решатель с вновь добавленными ограничениями до получения финального результата. Собственно, этот же подход мы уже применяли для нахождения решения симметричной задачи.

Основная мысль подхода: Несколько мелких задач решаются намного быстрее одной большой!

Линейные условия задаются практически точно так же как в симметричной задаче. Однако нужно понимать, что в ассиметричной задаче прямой и обратный пути не обязательно равны. Более того, прямой или (и) обратный путь может отсутствовать.

Потому для каждой вершины мы задаём сразу два условия:

  • Сумма исходящих рёбер для каждой вершины должна быть равна единице.

  • Сумма входящих рёбер для каждой вершины должна быть равна единице.

А в условии нераспадаемости, альтернативы звучат так:

  • Сумма переменных каждого найденного подцикла не должна превышать количества вершин, его составляющих минус один.

  • Сумма внешних исходящих связей для каждого найденного подцикла не меньше единицы.

  • Сумма внешних входящих связей для каждого найденного подцикла не меньше единицы.

Мы так же выбираем условие с наименьшим числом задействованных переменных.

В отличие от Эвклидовой задачи коммивояжёра, достаточно сложно подобрать адекватные тестовые данные для визуализации процесса решения ассиметричной задачи. Но так как данная задача является более общей для симметричного алгоритма, то можно использовать те же наборы исходных данных. Результаты получаются аналогичные, но гораздо медленнее. Думается, что удаление части рёбер и загрубление рёбер до целого значения является вполне удачным примером демонстрации работы алгоритма.

Пошаговый пример решения:

Симметричная матрица на сто вершин, разрежена на 2/3, удалены случайные рёбра в обоих направлениях.

В сети довольно сложно найти датасеты для проверки предложенного ассиметричного алгоритма. Примеры, что мне удавалось найти в основном на соединение точек на карте, но там симметричные наборы. Наиболее подходящий набор получен на ветхой страничке Гейдельбергского университета. Самый большой граф из их набора на 443 вершины обработался за 9 минут на домашнем компьютере в одном потоке.

Буду рад, если сообщество в комментариях поможет с поиском иных источников релевантных данных.


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

Возможно ли получить решение длиной меньше L?

На каждом шаге алгоритм возвращает величину, меньше которого невозможно построить минимальный Гамильтонов цикл. С каждым шагом постепенно увеличивая это значение. То есть, если на каком-то шаге алгоритм вернёт минимальную нижнюю оценку больше L, то это означает, что невозможно получить решение короче этой оценки.

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


Благодарю за внимание!

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

Ссылка GitHub

* Заголовок статьи содержит заявку на то, что задачу коммивояжера, возможно решать за полиномиальное время от числа вершин. Целочисленное линейное программирование является NP-трудной задачей. В среднем разрешается за полиномиальное время, но у неё есть и плохие случаи, когда решение вырождается в O(2^n). Однако в практических задачах такое не происходит, кроме как в специально подобранных условиях. Сложности с решением возникают только в задачах с регулярными повторяющимися элементами одинаковыми по величине. На случайных или слегка рандомизированных выборках время решения стремится к полиному по времени от размера входных данных.


P.S. Таков путь!

Комментарии (23)


  1. WASD1
    19.11.2023 14:58
    +4

    Хочу уточнить правильно ли я вас понял: если есть граф, который можно расположить на плоскости, при этом "веса рёбер" должны быть в точности равны расстояниям - то для такого случая задача коммивояжёра выродится в полиномиальную.

    Если это так - то для читателей лучше если предварительыне условия применения метода будут прописаны в начале статьи в явном виде. А не стыдливые "*" (как будто читаешь договор где тебя хотят обмануть) расставлять.


    1. rebuilder Автор
      19.11.2023 14:58

      Не совсем так, задача коммивояжера в общем виде остаётся NP-сложной, я это не оспариваю. Но в практических задачах с линейным программированием, нужно очень сильно постараться, чтобы заставить решатель уйти в экспоненту. И это не зависит от того, определена на плоскости она или нет. Это примерно, как свести QSort к O(n^2), а разбивая задачу на подзадачи мы очень сильно снижаем эту вероятность в целом.

      Однако и полином полиному рознь. Мы за счёт дополнительных ухищрений значительно уменьшаем сложность задачи. Но если для Эвклидовой плоскости мне встречался алгоритм и с лучшей скоростью работы, то вот за асимметричную задачу такого сказать не могу.

      Звёздочка как раз и говорит, что бывают исключения.


      1. WASD1
        19.11.2023 14:58
        +2

        Извините чего-то не понимаию.

        Вы предлагаете:
        1. Провести триангуляцию Делоне (https://ru.wikipedia.org/wiki/Триангуляция_Делоне - работает для графов на плоскости).
        2. Что-то сней сделать.

        Как мне п.1 делать? Для произвольного графа этот алгоритм не определён.


        1. rebuilder Автор
          19.11.2023 14:58

          Для симметричной задачи граф задаётся набором точек на плоскости, мы не задаём матрицу смежности. Затем для этих точек мы и строим триангуляцию Делоне. Расстояния рассчитывается только для нужных рёбер через Евклидову норму.

          Вы можете не использовать предложенную мной эвристику и задать полносвязаную матрицу смежности, алгоритм съест и так, но выигрыша в скорости будет мало.


          1. WASD1
            19.11.2023 14:58
            +3

            Для симметричной задачи граф задаётся набором точек на плоскости, мы не
            задаём матрицу смежности. Затем для этих точек мы и строим триангуляцию
            Делоне.

            Это же и значит - вы рассматриваете графы которые можно расположить на плоскости, где "длина ребра = эвклидова метрика"? Так?

            ПС
            Я не понимаю, зачем на первый мой вопрос вы ответили "нет".
            Класс графов представимых на плоскости - крайне узкий, в него не входят:
            - дорожные сети
            - электорехника \ микроэлектроника
            - навигация (вообще всё, где можно двигаться с разной скорость)
            - молекулярная структура
            Да сложнее придумать что в ваш класс графов входит.


            1. rebuilder Автор
              19.11.2023 14:58

              Это же и значит - вы рассматриваете графы которые можно расположить на плоскости, где "длина ребра = эвклидова метрика"? Так?

              Да это правда, но не вся.

              У меня сложилось впечатление, что вы невнимательно прочитали статью.

              Для Эвклидовой плоскости, да, используется эвристика с триангуляцией Делоне. А для задач, что вы перечислили, используется другой алгоритм – ассиметричный. Вот уже этот алгоритм стравляется со всеми задачами, он так же описан в статье, хотя сильно медленнее.


              1. WASD1
                19.11.2023 14:58

                А для задач, что вы перечислили, используется другой алгоритм – ассиметричный.

                Возможно. Но наверное таких как я много - желающих проглядеть статью и понять о чём она.

                И секция в которой объйснялось бы что вообще в статье (по аналогии с "Abstract") - мне кажется была бы очень полезно.


            1. GidraVydra
              19.11.2023 14:58
              -1

              Молекулярная структура-то каким образом представима на плоскости? То, что на бумажке рисуют - это проекция.


              1. rebuilder Автор
                19.11.2023 14:58

                В работе описаны два алгоритма, первый для плоскости он быстрее, второй для общей задачи коммивояжера (в том числе для молекулярной структуры), он медленнее, но так же решается точно.

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


              1. WASD1
                19.11.2023 14:58
                +1

                Класс графов, представимых на плоскости крайне узкий, в него НЕ входят:

                ...


                1. GidraVydra
                  19.11.2023 14:58

                  Понял, я не не заметил)


  1. vadimr
    19.11.2023 14:58

    Как верно замечено выше, задача коммивояжёра формально ставится не для евклидовой плоскости (независимо от того, симметричная она или асимметричная). Она же в своих практических применениях не о реальных путешествиях по земной поверхности. Поэтому не выйдет укорачивать путь, соединяя несмежные вершины.

    Да и даже на реальной Земле из условного Норильска в Омск может выйти быстрее добраться через Москву, чем напрямую. Так что триангуляция вам ничем не поможет.


    1. rebuilder Автор
      19.11.2023 14:58

      Евклидова задача коммивояжера только частный случай общей задачи, который возможно решать значительно быстрее. На мой взгляд, глупо отказываться от возможности получить преимущество, в конкретной области применения.

      И да, целочисленное линейное программирование по схеме в статье, переваривает точно дорожные графы на сотню – другую вершин.


      1. vadimr
        19.11.2023 14:58

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

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


        1. rebuilder Автор
          19.11.2023 14:58

          Тут вся загвоздка как раз в том, что у многих читающих нет понимания, как работает линейное программирование. У меня самого ушло много времени на то, что осознать, как это функционирует. В статье через метафору мяча попробовал донести, но видимо неудачно.

          Не может быть ни каких ‘забрести в вершину’, мы не перебираем комбинации. Работаем только с множествами, и соединяем множества, через линейные уравнения. В данном случае множества это найденные циклы в графе.


          1. vadimr
            19.11.2023 14:58
            +1

            Когда вы оцениваете вычислительную сложность алгоритма, то нельзя просто так пользоваться эквивалентными преобразованиями представления графа. Чтобы построить такое эквивалентное представление, вам тоже потребуется вычислительная работа. Какова сложность построения вашего мяча? Она сама по себе экспоненциально зависит от размерности задачи. А обычные применения задачи коммивояжёра (например, в экономике) строятся в пространствах очень высоких размерностей.


            1. rebuilder Автор
              19.11.2023 14:58

              Размерность многомерного пространства и правда будет очень высокой. Пример: для направленного графа на 200 вершин = 39800 мерное пространство. Но это уже сложность решателя линейных уравнений, его для этого и делают.


              1. vadimr
                19.11.2023 14:58
                +1

                Тонкость в том, что надо сначала ещё эту матрицу 39800x39800 построить. Это какое O от 200?

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


                1. rebuilder Автор
                  19.11.2023 14:58

                  Нет, тут всё проще. 39800 переменных – это и есть n^2-n, от 200 вершин.

                  Изначально решается матрица 39800 x 400. На следующих шагах, она станет немного больше по высоте, но не так уж сильно (в 2-4 раза). Шагов за 15-30 (в худшем случае n/2) алгоритм находит решение такого размера. Так что всё не как уж и печально. Кроме того солверы обычно наделяют различными методами, ускоряющими решения.


  1. Lozkins
    19.11.2023 14:58
    +2

    Интересный материал, спасибо! Это уже не первая ваша статья по TSP и в большинстве случаев недостает сравнения. Когда речь идет об одном алгоритме, кажется, что эта наилучший метод решения и других не существует. Но так ли это? Даже сравнение с алгоритмами из предыдущих ваших статей добавило бы объективности, учитывая громкость названия текущей статьи.

    По поводу сценария - https://math.uwaterloo.ca/tsp/data/ml/monalisa.html - может будет полезно на будущее. Кроме этого, localsolver делает заявку на лидерство по решению TSP: https://www.localsolver.com/benchmark/localsolver-vs-gurobi-traveling-salesman-problem-tsp Можно также взять данные из их benchmark'a. Конечно, сравнивать эвристический подход с точным это как бои без правил, но конкурентность предлагаемого подхода оценить можно.

    У ortools есть собственный решатель задач vrp, в частном случае - это tsp. Можно так же сравнить с этой библиотекой.


    1. rebuilder Автор
      19.11.2023 14:58

      Спасибо! С удовольствием читал ваши статьи, многое почерпнул для себя.

      Возможно, мне не пока не достаёт лоска в изложении своих идей, но от этого они не становятся хуже. Придерживаюсь того, что человек, которому интересна освещённая тема обязательно проверит код на практике.

      Соревноваться с эвристическими методами бессмысленно, тут вы правы.


  1. domix32
    19.11.2023 14:58

    Ну, что, ждём найденные суперперестановки для n > 5


    1. rebuilder Автор
      19.11.2023 14:58

      Звучит как вызов. Теоретически наверно можно замахнуться на n = 6, но это уже на грани.