![]() |
Модераторы: volvo877, Snowy, MetalFan |
![]() ![]() ![]() |
|
Fedor |
|
||||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
Вот тут думаю изложить основы реализации теории графов на Паскале. Просьба не судить слишком строго - первый опыт. А если критиковать, то с подсказками.
При написании буду в основном пользоваться: Кормен, Лейзерсон, Ривест - Алгоритмы. Построение и анализ 1. Представление графов Есть два основных способа представить граф: в виде списков смежных вершин и матрицы смежности. Первый предпочтительнее для разреженных графов, в которых количество ребер E намного больше V^2. Представление графа в виде списков смежных вершин использует массив из V списков - по одному списку на вершину. Для каждой вершины список содержит в произвольном порядке все смежные с ней вершины. Вот как эта структура данных будет выглядеть в Паскале
При использовании матрицы смежности мы нумеруем вершины графа числами 1,2,...,V и рассматриваем матрицу A, в которой a[i,j]=1 если есть ребро между вершинами i и j и a[i,j]=0в противном случае. В случае взвешенного графа, вместо единицы в a[i,j] удобно хранить вес ребра. Минус этого метода заключается в том, что матрица занимает V^2 памяти независимо от количества ребер в графе. В Паскале матрица будет выглядеть так:
-------------------- Мы - Днепряне. Мы всех сильней. |
||||
|
|||||
Fedor |
|
||||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
2. Поиск в ширину
Поиск в ширину - один из базовых алгоритмов теории графов. Он является основой для многих других алгоритмов. Пусть задан граф и виксированная начальная вершина s. Алгоритм поиска в ширину перечисляет все достижимые из s (если идти по ребрам) вершины в порядке возрастания расстояния от s. В процессе поиска из графа выделяется часть, которая называется деревом поиска в ширину с корнем в s. Она содержит все достижимые из s вершины (и только их). Для наглядности будем считать, что в процессе работы алгоритма вершины графа могут быть белыми, серыми и черными. Сначала они все белые, а в ходе работы алгоритма белая вершина может стать серую, серая - черной, но не наоборот. Встретив новую вершину, алгоритм поиска красит ее, так что серые и черные вершины - уже обнаруженные вершины. Сначала дерево поиска в ширину состоит только из начальной вершины s. Как только алгоритм находит новую вершину v, смежную с ранее найденной вершиной u, вершина v вместе с ребром (u,v) добавляется к дереву поиска и становится потомком u. Каждая вершина обнаруживается только один раз, так что двух родителей у вершины быть не может. Оценивая сложность работы этого алгоритма замечаем, что вершины только темнеют, то есть важдая вершина обрабатывается только один раз, т.е. вычислительная сложность алгоритма O(V+E). Алгоритм:
Реализация на Паскале с использованием матрицы смежности
-------------------- Мы - Днепряне. Мы всех сильней. |
||||
|
|||||
Fedor |
|
||||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
3. Поиск в глубину
Поиск в глубину - еще один способ обхода графа. Он используется в таких алгоритмах, как алгоритм топологической сортировки, алгоритм поиска сильно связанных компонент. Стратегия поиска в глубину такова: идти вглубь пока это возможно (есть непройденные исходящие ребра), возвращатся и искать другой путь когда таких ребер не существует. Если после этого остаются неиспользованные вершины - можно выбрать одну из них и повторять процесс пока остаются необнаруженные вершины. Найдя впервые вершину v, смежную с u, ми отмечаем это событие, записывая в поле p[v] значение u. Таким образом образуется дерево или несколько деревьев. Как и поиск в ширину, алгоритм поиска в глубину использует цвета вершин. Сначала все вершины - белые. При обнаружении новой вершины, она красится в серый цвет. Когда вершина полностью обработана, она перекрашивается в черный цвет. Кроме этого, поиск в глубину ставит на вершинах метки времени. Каждая вершина имеет две метки: d[v] показывает, когда вершина была обнаружена, f[v] - когда обработана. Эти метки впоследствии используются во многих алгоритмах на графах. Во время выполнения этого алгоритма, обработка каждой вершины происходит ровно один раз. Общее же количество выполнения строк 3 - 6 процедуры DFS-Visit составляет O(E). В итоге сложность данного алгоритма составляет O(V+E), где V - количество вершин в графе, а E - количество ребер. Алгоритм:
Реализация на Паскале с помощью матрицы смежности:
-------------------- Мы - Днепряне. Мы всех сильней. |
||||
|
|||||
Fedor |
|
||||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
4. Топологическая сортировка
Пусть имеется ориентированный граф без циклов. Задача о топологической сортировке этого графа состоит в следующем: надо указать такой линейный порядок на его вершинах, что любое ребро ведет от меньшей вершины к большей (в смысле этого порядка). Очевидно, что если в графе есть циклы, то такого порядка не существует. Можно сформулировать задачу о топологической сортировке и так: расположить вершины графа на горизональной прямой так, что чтобы все ребра шли слева направо. Во пример ситуации, когда возникает побобная задача: во время обучения программированию, какие-то темы нужно учить раньше других. Например, циклы нужно обязательно знать до изучения реализации теории графов. В некоторых случаях порядок не имеет значения (например, алгоритмы вычислительной геометрии можно учить как перед изучением теории графов, так и после). Мы составляем граф по следующем принципу: существует ребро (u,v) если тема u должна быть изучена перед прохождением темы v. Тем самым, топологическая сортировка описывает возможный порядок изучения тем. Алгоритм топологической сортировки предельно прост. Он основан на описанном выше алгоритме поиска в глубину.
И реализация топологической сортировки на Паскале. Граф задан матрицей смежности.
-------------------- Мы - Днепряне. Мы всех сильней. |
||||
|
|||||
Fedor |
|
||||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
Алгоритм Дейкстры
Представим себе карту автомобильных дорог Украины. Как найти кратчайший путь например из Днепропетровска в Ужгород? Можно, конечно, перебрать все возможные пути и выбрать минимальный из них. Но тогда получаются миллионы заведомо неверныз вариантов. Вот например зачем мне ехать из Днепра в Ужгород через Донецк, наматывая около шестисот лишних километров? Далее будет рассмотрен эффективный способ решения этой задачи. Алгоритм Дейкстры решает задачу о кратчайших путях из одной вершины для взвешенного ориентированного графа G = (V,E) с исходной вершиной s, в котором веса всех ребер неотрицательны. В процессе работы алгоритма поддерживается множетсво S, состоящее из вершин v, для которых расстояние от s до v уже известно. На очередном шаге алгоритм выбирает вершину u, не принадлежащую V и имеющую минимальное d[u] (где d[u] это расстояние от s до u). Далее производится т.н. релаксация всех ребер, выходящих из u. Это означает, что для каждого ребра ui если d[u ]+w(u,i)<d[i ] то d[i ]=d[u ]+w(u,i), где w(u,i) - это вес ребра ui. Все вершины, не принадлежащие S, хранятся в очереди Q Алгоритм Дейкстры:
Реализация на Паскале
-------------------- Мы - Днепряне. Мы всех сильней. |
||||
|
|||||
Hidrag |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 877 Регистрация: 9.4.2005 Где: JDK Репутация: нет Всего: 25 |
Спасибо за информацию и перевод общего описания в родной Паскаль
![]() Дали вот задание: написать процедуру, реализующую метод, отличающийся от поиска в ширину только тем, что вновь достигнутая вершина помещается не в очередь, а в стек ... с какой стороны подходить к нему? ...что будет на входе, и что должно оказаться на выходе ...не понятно... -------------------- ![]() |
|||
|
||||
maxim1000 |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: нет Всего: 110 |
так это и есть отличие поиска вглубину от поиска в ширину ![]() очередь соответствует поиску в ширину, а стек - в глубину отличие стека от очереди... гы... вспомнил интересную иллюстрацию: автомат Калашникова преобразует стек в очередь: магазин у автомата построен так: последний вставленный патрон находится сверху, он и будет взят первым ствол построен принципиально иначе: та пуля, которая попала в его начало первой, первой и вылетит с другого конца ![]() -------------------- qqq |
|||
|
||||
Hidrag |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 877 Регистрация: 9.4.2005 Где: JDK Репутация: нет Всего: 25 |
Буду мучиться ))) тока суть все равно не пойму... поиск в ширину или глубину... поиск чего? что там ищется? что будет найдено?
![]() -------------------- ![]() |
|||
|
||||
maxim1000 |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: нет Всего: 110 |
вообще мне кажется, что лучше употреблять слово "обход" вместо "поиск" на абстрактном уровне: есть дерево (вообще-то можно и любой граф, но для простоты рассмотрения пусть будет дерево) только задано оно не массивом узлов и ребер, а по-другому: для каждого узла есть функция, которая "делает" его потомков (поручик, молчать!!! ![]() теперь перед нами стоит задача - найти узел, удовлетворяющий какому-нибудь критерию (к этой задаче сводится просто огромное количество самых разных проблем) был бы это массив, мы бы просто прошлись по нему и нашли какой-нибудь узел но способ задания другой (часто бывает, что такой массив бы просто не поместился в памяти), поэтому приходится начинать с первой вершины (конечно же, проверить, не она ли искомая), получить ее потомков, их потомков, и т.д. можно сначала посмотреть на первую вершину, потом на ее прямых потомков, потом на их потомков (это - поиск в ширину) можно по-другому: посмотреть на первую вершину, на первого ее потомка, на первого потомка его потомка, ..., добраться до конца, вернуться на уровень выше, посмотреть на второго и т.д. (это - поиск в глубину) пример: предположим, нам надо найти строку только нам не сказали, что это за строка, а дали функцию, которая возвращает true или false для данной строки (например, взламываем программу полным перебором - метод жуткий, но ситуацию иллюстрирует хорошо и еще нам известно, что искомая строка не длиннее 10 символов (это - отдельный момент, к нему вернемся позже) надо перебрать все строки есть два способа: 1. перебираем все строки длиной 1, потом длиной 2, ... , длиной 10 (в ширину) 2. перебираем все строки с первой буквой A, с первой буквой B, ... с первой буквой Z (в глубину) если ограничение на длину строки не 10, а 3 (для наглядности), получим следующую последовательность: 1. в ширину: A,B,C,AA,AB,AC,BA,BB,BC,CA,CB,CC,AAA,AAB,AAC,ABA,ABB,ABC,не, мен даже при 3-х символах уже надоело ![]() 2. в глубину: A,AA,AAA,AAB,AAC,AB,ABA,ABB,ABC,AC,ACA,ACB,ACC,B,BA,BAA,... вот и все объяснение ![]() P.S. ну и обещанный возврат к ограничению на длину строки в общем случае это - ограничение на глубину дерева (маскимальную длину ветки), так вот оно не всегда есть ![]() например, в Прологе, когда производится попытка доказательства новых утверждений на основе старых получается именно вот такой проход по дереву (в каждый момент мы можем вспомнить одну из нескольких базовых посылок, а значит, получается ветвление). Но есть одна проблема: доказательство в принципе может быть любой длины, т.е. ограничения на глубину дерева нет в таких ситуациях поиск в глубину принципиально неприменим, поэтому там, насколько я припоминаю, используется поиск в ширину... -------------------- qqq |
|||
|
||||
Hidrag |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 877 Регистрация: 9.4.2005 Где: JDK Репутация: нет Всего: 25 |
maxim1000
Круто! ))) если бы нам так это преподавали возможно это бы и не было таким сложным, а то дали сухую голую теорию, задания, и делайте что хотите... Уже по дискретной математике зачет с экзаменом сдал по теории графов, а все равно врубиться не могу никак... написать процедуру, реализующую метод, отличающийся от поиска в ширину только тем, что вновь достигнутая вершина помещается не в очередь, а в стек ... уже скоро во сне сниться будет ((( -------------------- ![]() |
|||
|
||||
Fedor |
|
|||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
Модератор: давайте обсуждать алгоритмы в других темах. А здесь - только их реализация на Паскале!
-------------------- Мы - Днепряне. Мы всех сильней. |
|||
|
||||
Banderas |
|
|||
Unregistered |
Ребята, помогите с реализацией на Паскале какой-то конкретной задачи, с входными и выходными данными. Я не особо разбирабсь в програмировании, поэтому не понял, програма приведенная више, так ничего и не выполняет? Она то запускается, но ничего же не считает. Работает с голыми переменными?
|
|||
|
||||
Fedor |
|
||||
![]() Днепрянин ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2090 Регистрация: 8.2.2003 Где: Великий Репутация: нет Всего: 32 |
Banderas! Еще раз прошу создавать отдельные топики для обсуждения алгоритмов
Она считывает информацию с текстового файла, решает задачу и выводит определенную информацию на екран. Ввод-вывод показаны как пример и их легко переписать на нужные тебе. Например в приведенном мною алгоритме DFS ввод такой:
Сначала из первой строки файла считываем количество вершин графа. Затем до конца файла в строках по два числа i и j что означает что есть ребро из i и j. Эта информация заносится в матрицу. Это сообщение отредактировал(а) Fedor - 25.5.2005, 07:10 -------------------- Мы - Днепряне. Мы всех сильней. |
||||
|
|||||
talani |
|
|||
![]() Новичок Профиль Группа: Участник Сообщений: 3 Регистрация: 5.5.2008 Репутация: нет Всего: нет |
Спасибо огромное за столь полезную информацию, особенно порадовал Алгоритм Дейкстры
![]() А ещё, не могли бы вы рассказать мне, глупышке, как задавать исходные данные для графа? |
|||
|
||||
Dobermann |
|
||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 992 Регистрация: 7.1.2008 Репутация: нет Всего: 0 |
Если представить граф в виде матрицы смежности то:
Ну а в виде списков смежных вершин просто считываешь указатель на запись....
--- что-то вроде этого....только с косяками =) (с таким врятли столкнешься, ....... тут понятно, что разумней будет воспользоваться деревом ) Это сообщение отредактировал(а) Dobermann - 5.5.2008, 22:50 |
||||
|
|||||
![]() ![]() ![]() |
Правила форума "Delphi" | |
|
Запрещается! 1. Обсуждать и делится взломанными компонентами или программным обеспечением 2. Публиковать ссылки на варез 3. Оффтопить
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, THandle, Rrader, volvo877. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Object Pascal: кроссплатформенные технологии | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |