Как рассчитать оптимальные маршруты для путешествующего коммивояжера по битоническому туру? - PullRequest
13 голосов
/ 17 мая 2009

ОБНОВЛЕНО

После дополнительного чтения решение может быть дано со следующим отношением повторения:

(a) When i = 1 and j = 2, l(i; j) = dist(pi; pj )
(b) When i < j - 1; l(i; j) = l(i; j - 1) + dist(pj-1; pj)
(c) When i = j - 1 and j > 2, min 1<=k<i (l(k; i) + dist(pk; pj ))

Теперь это начинает иметь смысл, за исключением части C. Как мне определить минимальное значение k? Я полагаю, это означает, что вы можете перебирать все возможные значения k и просто хранить минимальный результат (l (k, i) + dist (pk, pj)?


Да, определенно проблема, которую я изучал в школе. Мы изучаем битонические туры для задачи коммивояжера.

В любом случае, скажем, у меня 5 вершин {0,1,2,3,4}. Я знаю, что мой первый шаг - отсортировать их в порядке увеличения x-координат. Оттуда я немного запутался в том, как это можно сделать с помощью динамического программирования.

Я читаю, что должен просмотреть список отсортированных узлов и поддерживать оптимальные пути для обеих частей (начальный путь и обратный путь). Меня смущает, как я буду рассчитывать эти оптимальные пути. Например, как я узнаю, должен ли я включить данный узел в начальный или обратный путь, поскольку он не может быть в обоих (кроме конечных точек). Вспоминая Фибоначчи в динамическом программировании, вы в основном начинаете с базового варианта и продолжаете двигаться вперед. Я думаю, что я спрашиваю, как бы я начал с проблемой битонического коммивояжера?

Для чего-то вроде чисел Фибоначчи подход к динамическому программированию вполне понятен. Тем не менее, я не знаю, если я просто плотный или что-то еще, но я совсем запутался, пытаясь обернуть голову вокруг этой проблемы.

Спасибо за внимание!

ПРИМЕЧАНИЕ. Я не ищу полных решений, но по крайней мере несколько хороших советов для начала. Например, если бы это была проблема Фибоначчи, можно было бы проиллюстрировать, как вычисляются первые несколько чисел. Пожалуйста, дайте мне знать, как я могу улучшить этот вопрос.

Ответы [ 2 ]

14 голосов
/ 17 июля 2012

Разъяснение по вашему алгоритму.

Рекурсивная функция l(i,j) должна вычислять минимальное расстояние для битового обхода i -> 1 -> j, посещая все узлы, которые меньше i. Итак, решение исходной задачи будет l(n,n)!

Важные примечания:

  1. мы можем предположить, что узлы упорядочены по их координате x и помечены соответствующим образом (p1.x < p2.x < p3.x ... < pn.x). Если они не были заказаны, мы могли бы отсортировать их в O(nlogn) времени.

  2. l(i,j) = l(j,i). Причина в том, что в lhs у нас есть тур i ->...-> 1 -> ... -> j, который является оптимальным. Однако прохождение этого маршрута в обратном направлении даст нам такое же расстояние и не нарушит битоническое свойство.

Теперь простые случаи (обратите внимание на изменения!):

(a) When i = 1 and j = 2, l(i; j) = dist(pi; pj ) = dist(1,2)

Здесь у нас следующий тур: 1->1->...->2. Тривиально это эквивалентно длине пути 1->...->2. Поскольку точки упорядочены по их координате .x, между 1 и 2 нет точки, поэтому соединяющая их прямая линия будет оптимальной. (Выбор любого количества других точек для посещения до 2 приведет к более длинному пути!)

(b) When i < j - 1; l(i; j) = l(i; j - 1) + dist(pj-1; pj)

В этом случае мы должны добраться до j-1, начиная с argmin k (d(k,j) ) = j-1. (Узел, из которого можно достичь j по кратчайшему пути, - j-1). Итак, это эквивалентно туру: i -> ... -> 1 -> .... -> j-1 -> j, что эквивалентно l(i,j-1) + dist(pj-1,pj)!

И, наконец, интересная часть приходит:

(c) When i = j - 1 or i = j, min 1<=k<i (l(k; i) + dist(pk; pj ))

Здесь мы знаем, что нам нужно пройти от i до 1, но нет никакой подсказки в обратном цикле! Ключевая идея здесь заключается в том, что мы должны думать об узле непосредственно перед j на нашем обратном маршруте. Это может быть любой из узлов от 1 до j-1! Предположим, что этот узел k. Теперь у нас тур: i -> ... -> 1 -> .... -> k -> j, верно? Стоимость этого тура l(i,k) + dist(pk,pj).

Надеюсь, вы получили это.

Осуществление.

Вам понадобится двумерный массив, скажем, BT[1..n][1..n]. Пусть i будет индексом строки, j будет индексом столбца. Как нам заполнить эту таблицу?

В первом ряду мы знаем BT[1][1] = 0, BT[1][2] = d(1,2), поэтому у нас осталось только i,j индексов, которые попадают в категорию (b).

В оставшихся строках мы заполняем элементы от диагонали до конца.

Вот пример кода C ++ (не тестировался):

void ComputeBitonicTSPCost( const std::vector< std::vector<int> >& dist, int* opt ) {
  int n = dist.size();
  std::vector< std::vector< int > > BT;
  BT.resize(n);
  for ( int i = 0; i < n; ++i )
    BT.at(i).resize(n);

  BT.at(0).at(0) = 0;  // p1 to p1 bitonic distance is 0
  BT.at(0).at(1) = dist.at(0).at(1);  // p1 to p2 bitonic distance is d(2,1)

  // fill the first row
  for ( int j = 2; j < n; ++j )
    BT.at(0).at(j) = BT.at(0).at(j-1) + dist.at(j-1).at(j);

  // fill the remaining rows
  int temp, min;  
  for ( int i = 1; i < n; ++i ) {
    for ( int j = i; j < n; ++j ) {
      BT.at(i).at(j) = -1;
      min = std::numeric_limits<int>::max();
      if ( i == j || i == j -1 ) {
        for( int k = 0; k < i; ++k ) {
          temp = BT.at(k).at(i) + dist.at(k).at(j);
          min = ( temp < min ) ? temp : min;
        }        
        BT.at(i).at(j) = min;        
      } else {
        BT.at(i).at(j) = BT.at(i).at(j-1) + dist.at(j-1).at(j);
      }
    }
  }

  *opt = BT.at(n-1).at(n-1);
}

3 голосов
/ 17 мая 2009

Хорошо, ключевые понятия в динамическом программном решении:

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

Существенным свойством битонического тура является то, что вертикальная линия в системе координат пересекает сторону замкнутого многоугольника не более двух раз. Итак, что такое битонический тур с двумя точками? Ясно, что любые две точки образуют (вырожденный) битонный тур. Три точки имеют два битовых обхода («по часовой стрелке» и «против часовой стрелки»).

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


Хорошо, вы на правильном пути со своим обновлением. Но теперь, в динамическом программном решении, то, что вы делаете с работой, это снизу вверх : предварительное вычисление и запоминание (не «запоминание») оптимальных подзадач.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...