Решение задач оптимизации генетическими алгоритмами на примере задачи коммивояжера

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

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык русский
Дата добавления 16.04.2014
Размер файла 65,3 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

Размещено на http://www.allbest.ru/

Санкт-Петербургский Государственный Политехнический Университет

кафедра прикладной математики

Курсовая работа по методам оптимизации:

«Решение задач оптимизации генетическими алгоритмами

на примере задачи коммивояжера»

Студент Кацман Виктор, группа 33601/2

  • Оглавление
    • Введение
    • 1. Постановка задачи
      • 1.1 Общее описание задачи
      • 1.2 Формальная постановка
    • 2. Общее описание генетического алгоритма
      • 2.1 Формализация задачи для решения генетическим алгоритмом
      • 2.2 Общая схема алгоритма
      • 2.3 Подробный разбор некоторых элементов алгоритма
    • 3. Описание алгоритмов решения задачи
      • 3.1 Алгоритм полного перебора
      • 3.2 Алгоритм динамического программирования
      • 3.3 Генетический алгоритм
    • 4. Описание исследований и их результатов
      • 4.1 Перечень тестировавшихся алгоритмов
      • 4.2 Тест на задачах малой размерности
      • 4.3 Тест на задачах средней размерности
      • 4.4 Тест на задачах высокой размерности
      • 4.5 Тест для исследования «алгоритма повторных применений» генетического алгоритма
    • Выводы
    • Литература
    • Введение
    • Данная работа посвящена исследованию возможности решать дискретные оптимизационные задачи с помощью генетических алгоритмов. Исследования проводились на примере решения «несимметричной незамкнутой задачи коммивояжера» - известной комбинаторной NP-трудной задачи, для которой наилучший известный на данный момент точный алгоритм работает с асимптотикой O(2^n). В ходе исследований результаты работы генетического алгоритма сравнивались с результатами работы с решением «несимметричной незамкнутой задачи коммивояжера» динамическим программированием по таким параметрам, как время работы, точность результата и объем используемой памяти на различных входных данных.

1. Постановка задачи

1.1 Общее описание задачи

Коммивояжер - странствующий торговец должен объехать N городов. Известны затраты на переезд между i-ым и j-ым городами, которые заданы в виде матрицы C = (c[i][j]), i = 1..N, j = 1..N. Коммивояжер должен объехать все города один раз. Требуется определить, в каком порядке следует объезжать города, чтобы суммарные затраты были минимальны.

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

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

1.2 Формальная постановка

На вход получаем матрицу затрат C = (c[i][j]), i = 1..N, j = 1..N, c[i][j] - затраты на переезд из i-го города в j-ый, c[i][j] неотрицательно, ограниченно; при желании запретить переезд из i-го города в j-ый следует сделать c[i][j] максимально большим.

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

1. В качестве переменных выбираем элементы матрицы переездов X = (x[i][j]), i = 1..N, j = 1..N, x[i][j] = {0,1}; x[i][j] = 1, если переезд из i-го города в j-ый включается в маршрут и x[i][j] = 0, если переезд из i-го города в j-ый не включается в маршрут. Получаем следующую задачу:

2. В качестве переменных выбираем элементы «вектора номеров городов» X = (x[i]), i = 1..N, x[i] ; ЃНj ? k: x[j] ? x[k]. Получаем следующую задачу:

2. Общее описание генетического алгоритма

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

2.1 Формализация задачи для решения генетическим алгоритмом

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

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

2.2 Общая схема алгоритма

НАЧАЛО

Создать начальную совокупность структур (популяцию)

Оценить каждую структуру

остановка := FALSE

ПОКА НЕ остановка ВЫПОЛНЯТЬ

НАЧАЛО

Применить оператор отбора

ПОВТОРИТЬ (F(размер популяции)) РАЗ

НАЧАЛО

Выбрать две структуры (родители) из множества предыдущей итерации

Применить оператор кроссовера к выбранным структурам и получить две новые структуры (потомки)

Оценить эти новые структуры

Поместить потомков в новое поколение

КОНЕЦ

Применить оператор мутации

ЕСЛИ С(популяция) ТО остановка := TRUE

КОНЕЦ

КОНЕЦ

Комментарии:

F(размер популяции) - функция от размеров текущей популяции, возвращает неотрицательное целое число.

С(популяция) - функция от текущей популяции, проверяет, сошлась ли она или нет, возвращает true/false.

2.3 Подробный разбор некоторых элементов алгоритма

1. Создание начальной популяции:

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

2. Оператор кроссовера:

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

3. Оператор мутации:

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

4. Оператор отбора:

На этапе отбора нужно из всей популяции выбрать определённую её долю, которая останется «в живых» на этом этапе эволюции и примет участие в следующем этапе развития. Критерий отбора, как правило, в основном использует функцию приспособленности, но при этом также может использовать некоторые особенности конкретной особи.

Комментарии:

Функция приспособленности - целевая функция для данного вектора (особи).

3. Описание алгоритмов решения задачи

3.1 Алгоритм полного перебора

Заключается в полном переборе всевозможных маршрутов коммивояжера и выборе самого оптимального из них. Данный алгоритм позволяет получить точное решение задачи, использует O(N^2) памяти (память нужна для матрицы С и одного вектора Х). Однако данный алгоритм является очень трудоемким по времени работы, поскольку всего существует N! различных маршрутов, и использовать его имеет смысл только при N < 15. В данной работе он использовался для тестирования результатов других алгоритмов при маленьких N.

//solution_t - структура для хранения решений

solution_t base_solve::solve ()

{

res->size = N;

res->min = UINT_MAX;

for (int i = 1; i <= res->size; i++) {res->result[i] = i;}

rightSolve (1, res->size);

return res;

}

void base_solve::recursion (l, r)

{

arr = res->result;

if (l == r)

{

rest = 0;

for (i = 1; i < G.size; i++)

{

rest += G.graph[arr[i]][arr[i+1]];

}

if (rest < res->min)

{

res->min = rest;

}

return;

}

for (i = l; i <= r; i++)

{

v = arr[l]; arr[l] = arr[i]; arr[i] = v;

rightSolve (l+1, r);

v = arr[l]; arr[l] = arr[i]; arr[i] = v;

}

return;

}

3.2 Алгоритм динамического программирования

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

Для «задачи коммивояжера» удобно выбрать следующие подзадачи и пересчет (способ перехода от одной подзадачи к другой):

Подзадачи: для подмножества городов S из {1..N}, и j ? S, обозначим через D[S,j] длину кратчайшего пути, проходящего через все города S и заканчивающегося в j.

Пересчет: D[S,j] =

Данный алгоритм позволяет получить точное решение задачи, использует O(N*2^N) памяти (память нужна для матриц C и D, а вариантов множества S - 2^N) и работает за O(N*2^N), так как должны заполниться все ячейки матрицы D. Таким образом, с помощью динамический алгоритм решения «задачи коммивояжера» можно использовать при N < 25. В данной работе он использовался для оценки точности результатов генетического алгоритма.

//B - матрица D

//Bj - матрица, в которой хранятся индексы последних городов на соответствующих маршрутах в D

solution_t *dinamic_solve::solve ()

{

res->size = N;

res->min = UINT_MAX;

B_length = 1 << res->size;

for (i = 1; i < B_length; i++)

{

for (j = 1; j <= res->size; j++)

{

B[i][j] = -1;

}

}

B[1][1] = 0;

k = 1;

for (i = 1; i <= res->size; i++)

{

B[k][i] = 0;

Bj[k][i] = i;

k <<= 1;

}

res->min = INT_MAX;

for (i = 1; i <= res->size; i++)

{

cr = rightSolve(B_length-1, i);

if (cr < res->min)

{

res->min = cr;

last = i;

}

}

res->result[res->size] = last;

j = B_length-1;

for (i = 1; i < res->size; i++)

{

j = j^(1 << (last - 1));

last = Bj[j][last];

res->result[res->size - i] = last;

}

return res;

}

dinamic_solve::rightSolve(unt mask,unt last)

{

&rest = B[mask][last];

&resj = Bj[mask][last];

if(rest == -1)

{

mask ^= (1 << (last-1));

rest = UINT_MAX;

for (unt i = 1; i <= res->size; i++)

{

if (mask & (1 << (i-1)))

{

unt cr = rightSolve(mask, i) + G.graph[i][last];

if (cr < rest) {rest = cr; resj = last; Bj[mask][last] = i;}

}

}

}

return rest;

}

3.3 Генетический алгоритм

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

1. Вид элемента популяции:

В качестве элемента популяции взята переменная в виде «вектора номеров городов». Данный выбор представляется удачным, поскольку он использует минимально возможный объем памяти O(N) (меньшим объемом невозможно описать путь коммивояжера через N городов) и позволяет производить за линейное время такие операции, как вычисление на нем функции приспособленности, кроссовер, мутация и т.д.

2. Создание начальной популяции:

Для создания начальной популяции создается набор из (2N - 2) особей, из которых затем отбираются N лучших.

Набор создается так:

· Первые N особей представляют собой маршруты, полученные из циклического маршрута обхода городов в порядке возрастания индекса (за исключением перехода из последнего в первый) выбрасыванием пути между двумя соседними городами

// generation - массив популяции

// generation[i]->result - маршрут i-ой особи

for (i = 0; i < N; i++)

{

for (j = 0; j < i; j++)

{

generation[i]->result[j + 1] = N + 1 - i + j;

}

for (j = i; j < N; j++)

{

generation[i]->result[j + 1] = j - i + 1;

}

}

· Последующие (N - 2) особи представляют собой маршруты, начинающиеся из последнего города и проходящие затем через несколько городов в порядке убывания индексов; далее маршрут включает в себя первый город и оставшиеся города в порядке возрастания индексов

// generation - массив популяции

// generation[i]->result - маршрут i-ой особи

for (i = N; i < 2*N - 2; i++)

{

i0 = i - N + 2;

for (j = 0; j < i0; j++)

{

generation[i]->result[j + 1] = i0 - j;

}

for (j = i0; j < N; j++)

{

generation[i]->result[j + 1] = j + 1;

}

}

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

Оценка трудоемкости работы алгоритма: O(N^2).

3. Оператор кроссовера:

Перед описанием используемых в данной работе операторов кроссовера приведено описание процедуры построения «сжатых» родителей.

Процедура заключается в следующем: для пары родителей находятся общие участки их маршрутов (участки, которые присутствуют у обоих родителей) и стягиваются в один элемент, эквивалентный городу. Получается два новых маршрута, не содержащих одинаковых участков, из которых рождается потомство. Далее стянутые элементы раскладываются в исходные участки маршрута и попадают в потомство (для этого используется отдельная «процедура раскладывания»). В результате получается, что потомство содержит в себе все общие черты родителей. Также, поскольку «сжатые» родители содержат меньше элементов, чем исходные родители - городов, процедура кроссовера будет работать быстрее.

Процедура построения «сжатых» родителей состоит из следующих этапов:

· Построение ссылок - векторов[1..N], содержащих на i-ом месте позицию города с i-ым индексом в исходном маршруте. Делается одновременно для обоих родителей.

// prnt1/2->result - маршрут первого / второго родителя

// links_to_vertices1/2 - вектор ссылок для первого / второго родителя

links_size = N;

for (i = 1; i <= links_size; i++)

{

links_to_vertices1[prnt1->result[i]] = i;

links_to_vertices2[prnt2->result[i]] = i;

}

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

// zip_prnt - структура данных, содержащая информацию о сжатых участках

// zip_prnt[i].vertex - массив, содержащий информацию о городах, входящих в i-ый элемент

// сжатого родителя

// prnt1/2->result - маршрут первого / второго родителя

// links_to_vertices1/2 - вектор ссылок для первого / второго родителя

// zip_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему исходным маршрутам

counter = 0;

vertex_counter = 0;

zip_size = 0;

for (i = 2; i <= links_size + 1; i++)

{

zip_prnt[counter].size = 1;

zip_prnt[counter].number = counter;

zip_prnt[counter].vertex[0] = prnt1->result[i - 1];

zip_links_1[counter] = counter;

vertex_counter = 0;

zip_numbers[i - 1] = counter;

while ((i <= links_size) &&

(prnt1->result[i] == prnt2->result[links_to_vertices2[prnt1->result[i - 1]] + 1]))

{

zip_prnt[counter].size++;

zip_prnt[counter].vertex[++vertex_counter] = prnt1->result[i++];

zip_numbers[i - 1] = counter;

}

counter++;

zip_size++;

}

counter = 0;

vertex_counter = 0;

for (i = 2; i <= links_size + 1; i++)

{

zip_links_2[counter] = zip_numbers[links_to_vertices1[prnt2->result[i - 1]]];

while ((i <= links_size) &&

(prnt2->result[i] == prnt1->result[links_to_vertices1[prnt2->result[i - 1]] + 1]))

{

i++;

}

counter++;

}

Данная процедура использует линейное время и память; в связи с этим она асимптотически не усложняет оператор кроссовера и её использование кажется очень удачным.

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

// zip_prnt - массив, содержащий информацию о сжатых участках

// g_son1/2->result - маршрут первого / второго потомка

// zip_res_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему исходным маршрутам

counter1 = 1, counter2 = 1;

for (i = 0; i < zip_size; i++)

{

current_zip1 = zip_res_links_1[i];

current_zip2 = zip_res_links_2[i];

for (j = 0; j < zip_prnt[current_zip1].size; j++)

{

g_son1->result[counter1++] = zip_prnt1[current_zip1].vertex[j];

}

for (j = 0; j < zip_prnt[current_zip2].size; j++)

{

g_son2->result[counter2++] = zip_prnt1[current_zip2].vertex[j];

}

}

Для исследования работы генетического алгоритма использовались следующие варианты кроссоверов (все варианты для двух родителей):

· Кроссовер по одной точке. Выполняем процедуру построения «сжатых» родителей. Проходим по элементам родителей, начиная от первого в порядке их следования в маршруте. Находится первый несовпадающий у родителей элемент. Два полученных в результате такого кроссовера потомка будут копиями родителей, за исключением двух элементов. Во-первых - первого несовпадающего элемента, замененного первым несовпадающим элементом другого родителя, во-вторых: элементом, на месте которого стоял элемент с индексом равным индексу первого несовпадающего элемента другого родителя (так, чтобы в маршруте потомков встречались все города).

// zip_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему исходным маршрутам

// zip_res_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему результирующим маршрутам потомков

// zip_size - размер сжатого родителя

if (zip_size == 1)

{

выход; //родители совпадают

}

for (i = 0; i < zip_size; i++)

{

zip_res_links_1[i] = zip_res_links_2[i] = -1;

}

i = 0;

while (zip_links_1[i] == zip_links_2[i])

{

zip_res_links_1[i] = zip_res_links_2[i] = zip_links_1[i++];

}

pnt1 = zip_res_links_2[i] = zip_links_1[i], i2 = i + 1;

pnt2 = zip_res_links_1[i] = zip_links_2[i], i1 = i + 1;

while (zip_links_1[i1] != pnt2)

{

zip_res_links_1[i1] = zip_links_1[i1++];

}

zip_res_links_1[i1++] = pnt1;

while (i1 < zip_size)

{

zip_res_links_1[i1] = zip_links_1[i1++];

}

while (zip_links_2[i2] != pnt1)

{

zip_res_links_2[i2] = zip_links_2[i2++];

}

zip_res_links_2[i2++] = pnt2;

while (i2 < zip_size)

{

zip_res_links_2[i2] = zip_links_2[i2++];

}

Выполняем процедуру «раскладывания».

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

· Кроссовер по двум точкам. Выполняем процедуру построения «сжатых» родителей. Проходим по элементам родителей, начиная от первого в порядке их следования в маршруте. Находится первый несовпадающий у родителей элемент. Два полученных в результате такого кроссовера потомка будут копиями родителей, за исключением двух элементов. Во-первых - первого несовпадающего элемента, замененного первым несовпадающим элементом другого родителя, во-вторых: элементом, на месте которого стоял элемент с индексом равным индексу первого несовпадающего элемента другого родителя (так, чтобы в маршруте потомков встречались все города). Теперь проходим по элементам родителей, начиная от последнего в порядке обратном порядку их следования в маршруте. Находится первый несовпадающий у родителей элемент. Два полученных в результате такого кроссовера потомка будут копиями родителей, за исключением двух элементов. Во-первых - первого несовпадающего элемента, замененного первым несовпадающим элементом другого родителя, во-вторых: элементом, на месте которого стоял элемент с индексом равным индексу первого несовпадающего элемента другого родителя (так, чтобы в маршруте потомков встречались все города).

// zip_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему исходным маршрутам

// zip_res_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему результирующим маршрутам потомков

// zip_size - размер сжатого родителя

if (zip_size < 6)

{

выход; //родители слишком похожи, используем другой оператор кроссовера

}

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

for (i = 0; i < zip_size; i++)

{

zip_res_links_1[i] = zip_res_links_2[i] = -1;

}

i = 0;

while (zip_links_1[i] == zip_links_2[i])

{

zip_res_links_1[i] = zip_res_links_2[i] = zip_links_1[i++];

}

pnt1 = zip_res_links_2[i] = zip_links_1[i], i2 = i + 1;

pnt2 = zip_res_links_1[i] = zip_links_2[i], i1 = i + 1;

while (zip_links_1[i1] != pnt2)

{

zip_res_links_1[i1] = zip_links_1[i1++];

}

zip_res_links_1[i1++] = pnt1;

while (i1 < zip_size)

{

zip_res_links_1[i1] = zip_links_1[i1++];

}

while (zip_links_2[i2] != pnt1)

{

zip_res_links_2[i2] = zip_links_2[i2++];

}

zip_res_links_2[i2++] = pnt2;

while (i2 < zip_size)

{

zip_res_links_2[i2] = zip_links_2[i2++];

}

i = zip_size - 1;

while (zip_res_links_2[i] == zip_res_links_1[i]) {i--;}

pnt1 = zip_res_links_2[i] = zip_links_1[i]; i2 = i - 1;

pnt2 = zip_res_links_1[i] = zip_links_2[i]; i1 = i - 1;

while (zip_links_1[i1] != pnt2){i1--;}

zip_res_links_1[i1++] = pnt1;

while (zip_links_2[i2] != pnt1){i2--;}

zip_res_links_2[i2++] = pnt2;

Выполняем процедуру «раскладывания».

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

· Кроссовер по частям. Выполняем процедуру построения «сжатых» родителей. Строим вектора ссылок - вектора[1..N], содержащие на i-ом месте позицию элемента с i-ым индексом в исходном маршруте (вектора ссылок строятся одновременно для обоих родителей).

// links_to_vertices1/2 - вектор ссылок для первого / второго родителя

// zip_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему исходным маршрутам

for (i = 0; i < zip_size; i++)

{

links_to_vertices1[zip_links_1[i]] = i;

links_to_vertices2[zip_links_2[i]] = i;

}

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

// links_to_vertices1/2 - вектор ссылок для первого / второго родителя

// zip_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему исходным маршрутам

// zip_res_links_1/2 - вектора с индексами, указывающими на элементы zip_prnt в порядке,

// отвечающему результирующим маршрутам потомков

// zip_size - размер сжатого родителя

for (i = 0; i < zip_size; i++)

{

zip_res_links_1[i] = zip_res_links_2[i] = -1;

}

flag = 0;

for (i = 0; i < zip_size; i++)

{

if (zip_res_links_1[i] == -1)

{

if (flag == 0)

{

flag = 1;

j = i;

zip_res_links_1[i] = zip_links_2[j];

while (zip_links_1[i] != zip_links_2[j])

{

j = links_to_vertices1[zip_links_2[j]];

zip_res_links_1[j] = zip_links_2[j];

}

}

else

{

flag = 0;

j = i;

zip_res_links_1[i] = zip_links_1[j];

while (zip_links_2[i] != zip_links_1[j])

{

j = links_to_vertices2[zip_links_1[j]];

zip_res_links_1[j] = zip_links_1[j];

}

}

}

}

flag = 0;

for (i = 0; i < zip_size; i++)

{

if (zip_res_links_2[i] == -1)

{

if (flag == 0)

{

flag = 1;

j = i;

zip_res_links_2[i] = zip_links_1[j];

while (zip_links_2[i] != zip_links_1[j])

{

j = links_to_vertices2[zip_links_1[j]];

zip_res_links_2[j] = zip_links_1[j];

}

}

else

{

flag = 0;

j = i;

zip_res_links_2[i] = zip_links_2[j];

while (zip_links_1[i] != zip_links_2[j])

{

j = links_to_vertices1[zip_links_2[j]];

zip_res_links_2[j] = zip_links_2[j];

}

}

}

}

Выполняем процедуру «раскладывания».

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

Все три вида оператора кроссовера используют O(N) памяти и работают за O(N) (все процедуры, используемые в кроссовере - линейные).

4. Оператор мутации:

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

· Мутация для одной особи по одному городу («циклическая»): выбирается произвольный город с индексом V и некоторое натуральное число K, не большее N. Выбранный город перемещается на K позиций по зацикленному маршруту особи (из последнего города маршрут ведет в первый).

// vec->result - маршрут мутирующей особи

// new_vec->result - маршрут мутировавшей особи

vertex = vec->result[V];

put_place = (V + K <= vec->size) ? (V + K) : ((V + K) % vec->size);

if (K > N - V)

{

for (i = V + 1; i <= N; i++)

{

new_vec->result[i-1] = vec->result[i];

}

new_vec->result[N] = vec->result[1];

K = K - (N - V) - 1;

for (i = 1; i <= K; i++)

{

new_vec->result[i] = vec->result[i+1];

}

new_vec->result[K + 1] = vertex;

for (i = K + 2; i < V; i++)

{

new_vec->result[i] = vec->result[i];

}

}

else

{

for (i = 1; i < V; i++)

{

new_vec->result[i] = vec->result[i];

}

for (i = V + 1, cnt = V + K; i <= cnt; i++)

{

new_vec->result[i-1] = vec->result[i];

}

new_vec->result[V + K] = vertex;

for (i = V + K + 1; i <= N; i++)

{

new_vec->result[i] = vec->result[i];

}

}

· Мутация для одной особи по одному городу («не циклическая»): выбирается произвольный город с индексом V и некоторое натуральное число K, не большее расстояния от города с индексом V до последнего города в маршруте особи. Выбранный город перемещается на K позиций по маршруту особи.

// vec->result - маршрут мутирующей особи

// new_vec->result - маршрут мутировавшей особи

vertex = vec->result[V];

for (i = 1; i < V; i++)

{

new_vec->result[i] = vec->result[i];

}

for (i = V + 1, cnt = V + K; i <= cnt; i++)

{

new_vec->result[i-1] = vec->result[i];

}

new_vec->result[num V + K] = vertex;

for (i = V + K + 1; i <= N; i++)

{

new_vec->result[i] = vec->result[i];

}

· Мутация для одной особи по двум городам («циклическая»): вызываются две «циклические» мутации для одной особи по одному городу для разных городов.

· Мутация для одной особи по двум городам («не циклическая»): вызываются две «не циклические» мутации для одной особи по одному городу для разных городов.

· Мутация для одной особи по двум городам («смешенная»): вызываются две «циклическая» и «не циклическая» мутации для одной особи по одному городу для разных городов.

Выбранные операторы мутаций имеют временную трудоемкость O(N) и требуют O(N) памяти.

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

5. Оператор отбора:

Отбор происходит исключительно по значению функции приспособленности: отбираются N наилучших маршрутов. Реализация основана на выборе N-ой статистики выборки из значений функции приспособленности (трудоемкость O(M)) и последующем отборе особей, значение функции приспособленности для которых не больше N-ой статистики (трудоемкость O(M)). M - количество особей перед отбором, в данной реализации алгоритма M N^2.

Алгоритм поиска N-ой статистики:

partition (arr, l, r)

{

x = arr[r];

i = l - 1;

for (j = l; j < r - 1; j++)

{

if (arr[j] <= x)

{

i++;

if (i != j)

{

arr[j] += arr[i];

arr[i] = arr[j] - arr[i];

arr[j] = arr[j] - arr[i];

}

}

}

i++;

if (i != r){ arr[r] += arr[i]; arr[i] = arr[r] - arr[i]; arr[r] = arr[r] - arr[i];}

return i;

}

rand_partition (arr, l, r)

{

i = rand () % (r - l) + l;

if (i != r){ arr[r] += arr[i]; arr[i] = arr[r] - arr[i]; arr[r] = arr[r] - arr[i];}

return partition (arr, l, r);

}

choose_i_min (arr, l, r, i)

{

if (l == r) return arr[l];

q = rand_partition (arr, l, r);

k = q - l + 1;

if (i == k) return arr[k];

else if (i < k) return choose_i_min (arr, l, q - 1, i);

else return choose_i_min (arr, q + 1, r, i - k);

}

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

6. Критерий сходимости популяции:

В исследованиях использовалось два критерия сходимости популяции: по прохождении определенного числа итераций и по достижению модулем разности между минимальным значением функции приспособленности и N-ой статистики выборки из функций приспособленности всех особей числа меньшего некоторого ? > 0.

7. Код основной части алгоритма:

solution_not_found = 1;

while (solution_not_found)

{

for (i = 0; i < cur_size; i++) {arr[i] = generation[i]->min;}

k_min = choose_i_min (arr, 0, cur_size - 1, G.size);

с = 0;

for (i = 0; i < cur_size; i++)

{

if (generation[i]->min <= k_min && с <= G.alloc*2)

{

ext = generation[i];

generation[i] = generation[с];

generation[с++] = ext;

}

}

cur_size = с;

//crossover

for (i = 0; i < cur_size; i++)

{

for (j = i+1; j < cur_size; j++)

{

if (!isEqual(generation[i], generation[j]))

{

if (crossover(generation[i], generation[j], &(generation[с]), &(generation[с + 1])))

с += 2;

}

}

}

cur_size = с;

//mytations

gsl min_genmin = UINT_MAX;

for (i = 0; i < cur_size; i++) {

if (min_genmin > generation[i]->min) {min_genmin = generation[i]->min;}

}

if (mytations)

{

for (i = 0; i < cur_size; i++)

{

if (generation[i]->min == min_genmin) continue;

ext3 = mytation (generation[i], ext1, ext2);

ext = generation[i];

generation[i] = ext3;

ext3 = ext;

}

}

min_genmin = UINT_MAX;

for (i = 0; i < cur_size; i++) {

if (min_genmin > generation[i]->min) {min_genmin = generation[i]->min; solution_ind = i;}

}

if (min_genmin + eps > k_min) solution_not_found = 0;

}

8. Оценки алгоритма:

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

· O(N^3) памяти: O(N) требуется для хранения одной особи, максимально возможный размер популяции (перед отбором) составляет O(N^2).

· O(N^3) времени на одну итерацию (наибольшее время уходит на кроссовер: требуется O(N^2) операций кроссовера, трудоемкость каждой из которых O(N))

Оценка трудоемкости всего алгоритма зависит от выбранного критерия сходимости популяции: в случае выбора первого критерия - O(N^3 * M), где M - число итераций; в случае второго критерия теоретическая оценка затруднительна в связи с разнообразным набором возможных входных данных, практическая оценка будет приведена ниже.

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

4. Описание исследований и их результатов

4.1 Перечень тестировавшихся алгоритмов

Таблица 4.1

Номер алгоритма

Описание

1

Алгоритм полного перебора

2

Алгоритм динамического программирования

3

Генетический алгоритм с кроссовером «по частям», «цикличекой» мутацией для двух городов и 1 итерацией

4

Генетический алгоритм с кроссовером «по частям», «цикличекой» мутацией для двух городов и 2 итерациями

5

Генетический алгоритм с кроссовером «по частям», «цикличекой» мутацией для двух городов и 5 итерациями

6

Генетический алгоритм с одноточечным кроссовером, «цикличекой» мутацией для одного города и вторым критерием сходимости

7

Генетический алгоритм с одноточечным кроссовером, «не цикличекой» мутацией для одного города и вторым критерием сходимости

8

Генетический алгоритм с одноточечным кроссовером, «цикличекой» мутацией для двух городов и вторым критерием сходимости

9

Генетический алгоритм с одноточечным кроссовером, «не цикличекой» мутацией для двух городов и вторым критерием сходимости

10

Генетический алгоритм с одноточечным кроссовером, «смешенной» мутацией для двух городов и вторым критерием сходимости

11

Генетический алгоритм с двухточечным кроссовером, «цикличекой» мутацией для одного города и вторым критерием сходимости

12

Генетический алгоритм с двухточечным кроссовером, «не цикличекой» мутацией для одного города и вторым критерием сходимости

13

Генетический алгоритм с двухточечным кроссовером, «цикличекой» мутацией для двух городов и вторым критерием сходимости

14

Генетический алгоритм с двухточечным кроссовером, «не цикличекой» мутацией для двух городов и вторым критерием сходимости

15

Генетический алгоритм с двухточечным кроссовером, «смешенной» мутацией для двух городов и вторым критерием сходимости

16

Генетический алгоритм с кроссовером «по частям», «цикличекой» мутацией для одного города и вторым критерием сходимости

17

Генетический алгоритм с кроссовером «по частям», «не цикличекой» мутацией для одного города и вторым критерием сходимости

18

Генетический алгоритм с кроссовером «по частям», «цикличекой» мутацией для двух городов и вторым критерием сходимости

19

Генетический алгоритм с кроссовером «по частям», «не цикличекой» мутацией для двух городов и вторым критерием сходимости

20

Генетический алгоритм с кроссовером «по частям», «смешенной» мутацией для двух городов и вторым критерием сходимости

21

Генетический алгоритм с кроссовером «по частям», без мутаций и вторым критерием сходимости

4.2 Тест на задачах малой размерности

Данный тест представляют собой «несимметричные незамкнутые задачи коммивояжера» при количестве городов не больше 10, даются программе на вход в виде матрицы затрат. Использовался этот тест для уверенности в правильности работы точных алгоритмов перебора и динамического программирования, а также для исследования работы генетического алгоритма на задачах малой размерности.

Тест включает в себя по 100 задач каждой из размерностей 5, 6, 7, 9, 10, матрица ограничений содержит значения от 0 до 1000.

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

Таблица 4.2

№ алгоритма

5 городов

6 городов

7 городов

9 городов

10 городов

1

47940, 0

297555, 0

1999074, 0

125613126, 0

1159747715, 0

2

41012,0

125366, 0

293429, 0

2613265, 0

5285576, 0

3

245434, 0.310

329121, 0.535

352885, 0.644

548858, 1.352

597877, 1.312

4

383212, 0.210

540357, 0.423

608930, 0.483

871349, 1.013

1119372, 1.657

5

524690, 0.210

885583, 0.280

788040, 0.500

1712369, 0.727

1977881, 1.044

6

535421, 0.173

768059, 0.249

724162, 0.362

1544689, 0.682

1864501, 0.726

7

446293, 0.144

780759, 0.285

795200, 0.375

1298272, 0.859

1663050, 0.900

8

457493, 0.162

864851, 0.269

1119195, 0.359

2834889, 0.455

3380160, 0.621

9

615445, 0.195

885784, 0.263

1072128, 0.397

1871712, 0.648

2276078, 0.661

10

600075, 0.172

1002164, 0.253

989799, 0.333

2426724, 0.578

2869449, 0.641

11

463795, 0.155

727924, 0.267

737878, 0.413

1439068, 0.625

1688384, 0.850

12

384893, 0.169

718982, 0.315

668836, 0.503

1107253, 0.902

1824984, 0.970

13

731393, 0.104

904013, 0.265

1131401, 0.344

2778641, 0.561

3445356, 0.713

14

664261, 0.174

1068748, 0.277

794839, 0.446

1806951, 0.744

2347263, 0.887

15

890573, 0.172

969951, 0.232

1098898, 0.384

2056957, 0.619

2616159, 0.843

16

477237, 0.212

633770, 0.384

622857, 0.483

1244565, 0.745

1482849, 0.917

17

447806, 0.290

663264, 0.357

584099, 0.656

1068022, 0.936

1518663, 1.059

18

676178, 0.161

737040, 0.299

811023, 0.498

1810618, 0.726

1687509, 1.111

19

666856, 0.173

690027, 0.379

813683, 0.470

1754136, 0.740

2042246, 0.970

20

745182, 0.171

666424, 0.333

785375, 0.486

1786601, 0.834

1926489, 1.017

21

315644, 0.444

346189, 0.704

401958, 0.972

676170, 1.491

966292, 1.759

По результатам данного теста хорошо видна нецелесообразность использования алгоритма полного перебора для решения «задачи коммивояжера». Также можно заметить нецелесообразность использования генетического алгоритма без мутаций (21), потому как и время его работы и ошибка по сравнению с точным результатом растет быстрее, чем, например, у 3-его алгоритма (с одноточечным кроссовером и «циклической» мутацией для одного города и одной итерации).

Кроме того, из проведенных исследований следует, что для решения «задач коммивояжера» малой размерности лучше подходит алгоритм динамического программирования (и по времени и по точности).

4.3 Тест на задачах средней размерности

Данный тест представляют собой «несимметричные незамкнутые задачи коммивояжера» при количестве городов порядка 15-19, даются программе на вход в виде матрицы затрат. Использовался этот тест для исследования времени и точности работы генетического алгоритма на задачах средней размерности.

Тест включает в себя по 100 задач каждой из размерностей 15, 17, 19, матрица ограничений содержит значения от 0 до 1000.

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

Таблица 4.3

№ алгоритма

15 городов

17 городов

19 городов

2

828575405, 0

4246515380, 0

21546365063, 0

3

1295253, 3.423

1685045, 4.166

2079217, 4.806

4

2228001, 2.970

2668267, 3.555

3517825, 4.137

5

3880801, 2.076

5693568, 2.548

6252386, 3.284

6

5179881, 1.574

8415665, 2.026

8612602, 2.550

7

4958129, 1.899

6020746, 2.289

8591202, 2.737

8

10007204, 1.464

12485682, 2.057

16063651, 2.507

9

7316538, 1.650

7442943, 2.435

11470503, 2.493

10

9035033, 1.425

12441862, 1.929

13588687, 2.536

11

6000724, 1.628

8024809, 1.819

11880129, 2.342

12

5195162, 1.843

6084621, 2.455

10374952, 2.439

13

11586620, 1.615

14710175, 1.874

18723471, 2.453

14

7425435, 1.602

8180333, 2.392

11759932, 2.856

15

10333317, 1.623

12357801, 2.122

16337563, 2.271

16

4556540, 1.943

5590378, 2.429

8760769, 2.496

17

4203885, 2.059

6003484, 2.466

7748776, 3.044

18

5953596, 2.005

7104790, 2.363

11222184, 2.694

19

5925075, 1.924

7754152, 2.508

8975546, 3.158

20

5961760, 1.911

8121099, 2.246

10559025, 2.718

По результатам данного теста хорошо видна нецелесообразность использования динамического программирования для решения «задачи коммивояжера» ввиду больших временных затрат. Трудоемкость работы генетического алгоритма на основе исследований составляет o(N^4), что позволяет надеяться на достаточно быстрое решение задачи высокой размерности. Плохую тенденцию представляет ухудшение точности: средняя разность между полученным и оптимальным значениями растет быстрее, чем линейно относительно N*1000 (1000 - верхняя граница данных). Для решения этой проблемы можно применять алгоритм несколько раз и брать наилучший результат, это может привести к лучшему результату в связи со случайными генерациями мутаций.

4.4 Тест на задачах высокой размерности

Данный тест представляют собой «несимметричные незамкнутые задачи коммивояжера» при количестве городов до 100, даются программе на вход в виде матрицы затрат. Использовался этот тест для исследования времени работы генетического алгоритма на задачах высокой размерности. Оценить отклонение полученного решения от точного на данных задачах затруднительно в связи с длительным временем, необходимым для получения точного решения

Тест включает в себя по 100 задач каждой из размерностей 40, 60, 80, 100 матрица ограничений содержит значения от 0 до 1000.

Результаты тестов (в левом столбце указаны номера алгоритмов, в остальных - результаты для соответствующей размерности входных данных; в ячейке указано время работы в тактах процессора):

Таблица 4.4

№ алгоритма

40 городов

60 городов

80 городов

100 городов

3

13869657

38421970

55298795

75944467

4

26485951

58478116

65362540

89778728

5

47573750

116052555

158248183

203085649

6

91932504

852802560

406025090

599689705

7

89224844

689499528

209191128

502414416

8

145264977

235647929

510230755

849701034

9

109529934

134146799

390671209

734172264

10

128015246

224339695

470048851

829701318

11

100875795

114735950

542020450

715425804

12

91671430

118870453

384195168

433715360

13

120963588

223183049

484323760

914393247

14

104692106

200380730

385446547

870083172

15

160120583

191328739

371821962

717835433

16

73306764

154243932

400560900

879946371

17

72630789

215827892

605618957

744229511

18

87109040

169618046

336987348

687957487

19

99422589

281745930

514861598

778038679

20

90700406

170255103

352518951

787416156

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

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

· В среднем, в алгоритме со сходимостью по второму критерию проходит не более log(N)*5 итераций. Это подтверждает общую оценку трудоемкости алгоритма o(N^4).

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

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

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

4.5 Тест для исследования «алгоритма повторных применений» генетического алгоритма

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

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

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

Тест включает в себя по 5 задач каждой из размерностей 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 матрица ограничений содержит значения от 0 до 1000.

Результаты тестов (в левом столбце указаны алгоритмы (в виде номеров входящих в них модификаций, отобраны на основе точности, показанной предыдущими тестами), в остальных - результаты для соответствующей размерности входных данных; в ячейке указана точность результата в виде частного разности между полученными и точными решениями и 1000 - верхнего ограничения данных):

Таблица 4.5

Алгоритм

11

12

13

14

15

16

17

18

19

20

21

6, 11, 16

0.460

0.351

0.648

0.707

0.837

0.654

1.339

1.344

1.637

1.525

2.314

7, 12, 17

0.402

0.606

0.811

0.896

0.855

1.248

1.420

1.537

1.660

1.524

1.934

8, 13, 18

0.335

0.488

0.699

0.710

0.869

0.938

1.191

1.269

1.285

1.085

1.851

9, 14, 19

0.641

0.767

0.605

0.665

1.012

1.107

1.044

1.400

1.565

1.422

1.656

10, 15, 20

0.410

0.584

0.689

0.722

0.932

1.149

1.035

0.921

1.140

1.230

1.475

6, 9, 12, 15, 18

0.290

0.566

0.534

0.548

0.584

0.632

1.259

1.007

1.154

1.399

1.502

7, 10, 13, 16, 19

0.427

0.335

0.429

0.432

0.545

1.018

0.942

1.032

1.006

1.069

1.423

8, 11, 14, 17, 20

0.384

0.505

0.479

0.470

0.532

0.878

1.013

1.002

1.024

1.685

1.270

6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

0.150

0.271

0.322

0.341

0.525

0.578

0.796

0.808

1.052

0.945

0.972

10, 11, 13, 15

0.234

0.394

0.504

0.641

0.868

0.698

0.813

1.094

1.473

1.109

1.274

9, 11, 12, 13, 15, 16

0.229

0.390

0.496

0.606

0.704

0.690

0.993

0.962

1.282

0.921

1.438

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

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

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

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

· В целом, результаты позволяют ожидать точности примерно 20% от N для наилучших модификаций за достаточно быстрое время. Благодаря этому, появляется возможность оценить значение целевой функции в оптимальной точке. Теоретически, дальше можно применять различные модификации генетического алгоритма до тех пор, пока это значение не будет достигнуто, но оценить количество требуемых применений и модификации, которые следует применять затруднительно.

генетический алгоритм коммивояжер программирование

Выводы

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

Литература

1. Д.И. Батищев, С.А. Исаев. Оптимизация многоэкстремальных функций с помощью генетических алгоритмов (1997)

2. Д.И. Батищев, С.А. Исаев, Е.К. Ремер. Эволюционно-генетический подход к решению задач невыпуклой оптимизации (1998)

3. Р. Акоф, М. Сасиени, Основы исследования операций, Издательство «Мир», (1971).

4. Задачи оптимизации и нечеткие переменные (1980)

5. А.И. Орлов. Теория принятия решений (2004)

6. R. Allenson. Genetic Algorithms with Gender for Multivariate Optimisation. (1992).

7. Blickle, Thiele "A Comparison of Selection Schemes used in Genetic Algorithms" (1993)

8. Darrel Whitley "A Genetic Algorithm Tutorial" (1993).

Размещено на Allbest.ru


Подобные документы

  • Этапы работы генетического алгоритма, область его применения. Структура данных, генерация первоначальной популяции. Алгоритм кроссинговера - поиск локальных оптимумов. Селекция особей в популяции. Техническое описание программы и руководство пользователя.

    реферат [1014,2 K], добавлен 14.01.2016

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

    курсовая работа [603,3 K], добавлен 21.10.2012

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

    курсовая работа [195,5 K], добавлен 08.11.2009

  • Оптимизация решения задачи с помощью алгоритма отжига. Анализ теории оптимизации как целевой функции. Метод градиентного спуска. Переменные и описание алгоритма отжига. Представление задачи коммивояжера через граф. Сведение задачи к переменным и решение.

    курсовая работа [784,0 K], добавлен 21.05.2015

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

    дипломная работа [1,7 M], добавлен 07.02.2013

  • Математическая постановка задачи. Обоснование выбора средств разработки. Входные и выходные данные работы программы. Решение задачи теста для написания и отладки программы. Описание программных модулей. Разработка алгоритма, анализ полученных результатов.

    курсовая работа [2,2 M], добавлен 13.12.2015

  • Описание принципа работы генетического алгоритма, проверка его работы на функции согласно варианту на основе готовой программы. Основные параметры генетического алгоритма, его структура и содержание. Способы реализации алгоритма и его компонентов.

    лабораторная работа [20,2 K], добавлен 03.12.2014

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

    дипломная работа [979,1 K], добавлен 30.05.2015

  • Задачи, решаемые методом динамического программирования. Основные этапы нахождения деревянного алгоритма решения задачи. Выполнение алгоритма Прима. Построение Эйлерового цикла. Решение задач средствами Excel. Алгоритм основной программы - Derevo.

    курсовая работа [586,3 K], добавлен 04.04.2015

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

    курсовая работа [714,1 K], добавлен 31.03.2015

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.