Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Паскаль / okulov / okulov / chapter3.DOC
Скачиваний:
66
Добавлен:
10.12.2013
Размер:
6.83 Mб
Скачать

3.10. Методы приближенного решения задачи коммивояжера

3.10.1. Метод локальной оптимизации

Попытаемся отказаться от перебора вариантов, рассмотренного в главе 2. Найдем первое решение (первую оценку), а затем попытаемся улучшить оценку, просматривая только соседние города найденного пути.

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

Шаг 2. Пока происходит улучшение решения, выполнять следующий шаг, иначе перейти на шаг 4.

Шаг 3. Для всех пар номеров городов i,j, удовлетворяющих неравенству (1Јi<jЈn), проверить:

di-1,i +di,j +dj,j+1 >di-1,j +dj,i +di,j+1для смежных городов, то есть j=i+1

di-1,i +di,i+1 +dj-1,j +dj,j+1 >di-1,j +dj,i+1 +dj-1,i +di,j+1для несмежных городов.

Примечание. На рисунке даны графические иллюстрации первого и второго неравенств. “Жирными” линиями обозначены участки старых маршрутов, “тонкими” - новых.

Если одно из неравенств выполняется, то найдено лучшее решение. Следует откорректировать ранее найденное решение и вернуться на шаг 2.

Шаг 4. Закончить работу алгоритма.

Для реализации логики (из рассмотренных в предыдущем алгоритме структур данных) достаточно матрицы расстояний (А) и массива для хранения пути коммивояжера (Way). Вид общей логики:

begin

init;{Ввод из файла матрицы расстояний, инициализация глобальных переменных}

one_way;{Поиск первого варианта пути коммивояжера}

local;{Локальная оптимизация}

out;{Вывод результата}

end.

Примечание. Владение технологиями “сверху вниз” и “снизу вверх” - необходимые составляющие структурной парадигмы мышления.

Продолжим уточнение логики. Работа процедурinit, one_way, out достаточно очевидна. Естественным приемом является вынесение ее в самостоятельную часть работы школьников на занятии. Нам необходимо уточнить процедуруlocal. Работаем не с частностями, а на содержательном уровне. Предположим, что мы имеем функцииbest1 иbest2, их параметры - индексы элементов массиваway, определяющих номера городов в пути коммивояжера, а выход естественный - истина или ложь, в зависимости от того, выполняются неравенства или нет. Рассуждаем дальше. Если неравенство выполняется, то нам необходимо изменить путь (соответствующие элементы массиваway). Пока не будем заниматься деталями. Пусть эту работу выполняет процедураswap, ее параметры - индексы элементов массиваway). Эта процедура, вероятно, должна сообщать о том, что она «что-то изменила», ибо нам необходимо продолжать работу до тех пор, пока что-то меняется, происходят улучшения. Итак, логика процедурыlocal.

Procedure local;

var i,j:integer; change:boolean;

<здесь функцииbest1 иbest2, а также процедура swap>;

begin

repeat

change:=false;

for i:=1 to n-1 do

for j:=i+1 to n do

if i=j+1 then begin if best1(i,j) then swap(i,j);end

else if (i=1) and (j=n) then begin if best1(i,j) then swap(i,j); end{Об этой проверке лучше первоначально умолчать, чтобы было о чем спросить, «если совсем будет плохо» - все понимают и нет вопросов}

else if best2(i,j) then swap(i,j);

until not(change);

end.

3.10.2. Алгоритм Эйлера

Этот алгоритм и следующий работоспособны в том случае, если выполняется неравенство треугольника. Его суть в том, что для любой тройки городов i, j, k (между которыми есть связь) выполняется неравенствоdi,j+dj,k>di,k. Рассмотрим идею алгоритма.

Шаг1. Строится каркас минимального веса (алгоритмы Прима или Краскала).

Примечание. Это не есть первое приближение, как в предыдущем алгоритме.

Шаг 2. Путем дублирования каждого ребра каркас преобразуется в эйлеров граф.

Шаг 3. Находим в построенном графе эйлеров цикл.

Шаг 4. Эйлеров цикл преобразуем в гамильтонов цикл (или маршрут коммивояжера). Метод преобразования: последовательность вершин эйлерова цикла сокращается так, чтобы каждая вершина графа в получившемся цикле встречалась ровно один раз.

Шаг 5. Закончить работу алгоритма. Получено приближенное решение задачи коммивояжера.

Покажем , что стоимость приближенного решение CostAp не превосходит удвоенной стоимости оптимального решенияCostBet. Пусть стоимость каркаса -CostFr. ТогдаCostFr<CostBet, так как при удалении из оптимального пути коммивояжера ребра получаем каркас с весом не большим, чемCostAp. Из правила построения эйлерова графа получаем, что вес построенного эйлерова цикла 2*СostFr. Неравенство треугольника обеспечивает результат: следующую оценку шага 4 -CostAp<2*CostFr, а значит,CostAp<2*CostBet.

Рассмотрим пример.

Для исходных данных, приведенных в п. 2.1.7, на рисунке жирными линиями выделен каркас минимального веса. Тонкие линии добавляются на шаге 2. Получаем эйлеров граф. Затем эйлеров цикл (маршрут) преобразуется в путь коммивояжера (шаг 4). Согласно неравенству треугольника мы получаем:d2,9+d9,8>d2,8; d2,8+d8,1>d2,1; d2,1+d1,7>d2,7. Суммируя левые и правые части неравенств, получаем:d2,9+d9,8+d8,1+d1,7>d2,7.Для рассмотренного примераCostAp равна 202. Это~1.28*CostBet.

Структуры данных. Помимо названных ранее массивов А - матрица расстояний и массива Way - хранение искомого пути, требуется «что-то» для хранение описания каркаса. Пусть это будет массивB (B:array[1..Nmax,1..Nmax] of boolean). ЭлементB[i,j], равныйtrue,говорит о том, что ребро(i,j) графа принадлежит каркасу.

Общая логика.

Begin

init;{ввод описания - матрица расстояний; инициализация данных}

solve;{решатель}

out;{вывод результата}

end.

Процедуры init иoutочевидны (должны писаться «на автомате»). Уточняем процедуруsolve.Первый «набросок».

Procedure solve;

var ?

<процедуры и функции>;

begin

init_solve;{инициализация переменных процедурыsolve}

find_tree;{построение каркаса}

eiler_way;{поиск эйлерова цикла}

komm_way;{поиск пути коммивояжера}

end;

Прежде чем продолжать уточнение, необходимо определить структуры данных этой части логики и взаимодействие ее составных частей. Во-первых, при построении каркаса необходимо иметь список ребер, отсортированный в порядке возрастания их весов (алгоритмы Краскала и Прима). Следовательно, на входе процедурыfind_tree матрица расстоянийA, на выходе - B, рабочие структуры - массив для хранения списка ребер. Внутренние процедуры: формирование списка ребер и сортировки. Продолжим рассмотрение. Эйлеров цикл необходимо где-то хранить, пусть это будет массивSt (St:array[1..n*(n-1) div 2] of byte.Количество не нулевых элементов вSt - значение переменнойCount.Эти величины описываются в разделе переменных процедурыsolve, их инициализация - в процедуреinit_solve.Процедуру поиска эйлерова цикла сделаем рекурсивной, поэтому первый вызов изменится -eiler_way(1). Выбор начальной вершины при поиске эйлерова цикла не имеет значения.

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

procedure eiler_way(v:byte);

var j:integer;

begin

for j:=1 to N do

if b[v,j] then begin b[v,j]:=false;b[j,v]:=false;eiler_way(j);end;

inc(count);St[count]:=v;{заносим номер вершины в эйлеров цикл}

end;

procedure komm_way;

var s:set of 1..Nmax;

i,j:integer;

begin

i:=0;s:=[];

for j:=1 to Count do{исключаем повторяющиеся номера вершин из эйлерова цикла}

if Not(St[j] in s) then begin

inc(i);way[i]:=St[j];s:=s+[St[j]];

end;

end;

Соседние файлы в папке okulov