Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Практика Санек.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
198.01 Кб
Скачать

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ

ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ

ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

«ДОНСКОЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ»

ДГТУ

Факультет Информатика и вычислительная техника

Кафедра «ПОВТ и АС»

ОТЧЕТ

по распределенной компьютерной 2 курса практике

в ДГТУ

в период с «__»_________________20__г. по «__»______________20__ г.

студента группы ВПР22 Боташева Александра Михайловича

Руководитель практики

от кафедры старший преподаватель Романенко Е.А.

Оценка

Ростов-на-Дону

2013 г.

СОДЕРЖАНИЕ

ВВЕДЕНИЕ………………………………………………………………………… 3

1 ПОСТАНОВКА ЗАДАЧИ…………………………………………………...…... 4

2 АЛГОРИТМИЧЕСКОЕ КОНСТРУИРОВАНИЕ………………………………. 5

2.1 Формальное описание алгоритма…………………………………...….….… 5

2.2 Блок-схема………………………...…………………………………...….….… 8

3 ПРОГРАММНОЕ КОНСТРУИРОВАНИЕ…………………………..………....11

4 КОНТРОЛЬНЫЙ ПРИМЕР…………………………………………………….. 12

5 РЕКОМЕНДАЦИИ ПОЛЬЗОВАТЕЛЮ……………………………………….. 14

6 РЕКОМЕНДАЦИИ ПРОГРАММИСТУ………………………………...…….. 15

7 ЗАКЛЮЧЕНИЕ………………………………………………………….……… 16

8 СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ…………………………… 17

Приложение А. Код программы…………………………...……………………... 18

ВВЕДЕНИЕ

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

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

В теории графов, паросочетание или независимое множество ребер в графе — это набор попарно несмежных ребер.

Пусть дан граф G = (V,E), паросочетание M в G это множество попарно несмежных ребер, то есть ребер, не имеющих общих вершин.

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

Произвольное максимальное паросочетание находится тривиальным жадным алгоритмом за линейное время. Для поиска наибольшего паросочетания могут использоваться: алгоритм Эдмондса, рандомизированный алгоритм на основе матрицы Татта. В случае двудольного графа: венгерский алгоритм, алгоритм Хопкрофта — Карпа (англ.), алгоритм Куна. На текущий момент не известно ни одного полиномиального алгоритма нахождения наименьшего максимального паросочетания, т. е. максимального паросочетания, содержащего наименьшее количество рёбер.

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

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

  1. Входящие данные

  2. Метод реализующий алгоритм

  3. Вывод результатов алгоритма

2 Алгоритмическое конструирование

2.1 Формальное описание алгоритма

Алгоритм заключается в нахождении максимального количества паросочетаний.

Алгоритм Куна — непосредственное применение теоремы Бержа. Его можно кратко описать так: сначала возьмём пустое паросочетание, а потом — пока в графе удаётся найти увеличивающую цепь, — будем выполнять чередование паросочетания вдоль этой цепи, и повторять процесс поиска увеличивающей цепи. Как только такую цепь найти не удалось — процесс останавливаем, — текущее паросочетание и есть максимальное.

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

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

Алгоритм просматривает все вершины   первой доли графа: u = 1..n. Если текущая вершина   уже насыщена текущим паросочетанием (т.е. уже выбрано какое-то смежное ей ребро), то эту вершину пропускаем. Иначе — алгоритм пытается насытить эту вершину, для чего запускается поиск увеличивающей цепи, начинающейся с этой вершины.

Поиск увеличивающей цепи осуществляется с помощью специального обхода в глубину или ширину (обычно в целях простоты реализации используют именно обход в глубину). Изначально обход в глубину стоит в текущей ненасыщенной вершине   первой доли. Просматриваем все рёбра из этой вершины, пусть текущее ребро — это ребро (u, to). Если вершина to ещё не насыщена паросочетанием, то, значит, мы смогли найти увеличивающую цепь: она состоит из единственного ребра (u, to); в таком случае просто включаем это ребро в паросочетание и прекращаем поиск увеличивающей цепи из вершины  . Иначе, — если to уже насыщена каким-то ребром (p, to), то попытаемся пройти вдоль этого ребра: тем самым мы попробуем найти увеличивающую цепь, проходящую через рёбра (u. to), (to, p). Для этого просто перейдём в нашем обходе в вершину p — теперь мы уже пробуем найти увеличивающую цепь из этой вершины.

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

После того, как все вершины u = 1..n будут просмотрены, текущее паросочетание будет максимальным.

Здесь n — число вершин в первой доле, к — во второй доле, g[u] — список рёбер из вершины u первой доли (т.е. список номеров вершин, в которые ведут эти рёбра из u). Вершины в обеих долях занумерованы независимо, т.е. первая доля — с номерами 1..n, вторая — с номерами 1..k.

Дальше идут два вспомогательных массива: mt и used. Первый — mt — содержит в себе информацию о текущем паросочетании. Для удобства программирования, информация эта содержится только для вершин второй доли: mt[i] — это номер вершины первой доли, связанной ребром с вершиной   второй доли (или -1, если никакого ребра паросочетания из   не выходит). Второй массив — used — обычный массив "посещённостей" вершин в обходе в глубину (он нужен, просто чтобы обход в глубину не заходил в одну вершину дважды).

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

Внутри функции просматриваются все рёбра, исходящие из вершины   первой доли, и затем проверяется: если это ребро ведёт в ненасыщенную вершину to, либо если эта вершина to насыщена, но удаётся найти увеличивающую цепь рекурсивным запуском из mt[yo], то мы говорим, что мы нашли увеличивающую цепь, и перед возвратом из функции с результатом true производим чередование в текущем ребре: перенаправляем ребро, смежное с to, в вершину  .

В основной программе сначала указывается, что текущее паросочетание — пустое (список mt заполняется числами -1). Затем перебирается вершина   первой доли, и из неё запускается обход в глубину tryKuhn, предварительно обнулив массив used.

Стоит заметить, что размер паросочетания легко получить как число вызовов tryKuhn в основной программе, вернувших результат true. Само искомое максимальное паросочетание содержится в массиве mt.