Поиск в ширину (BFS): различия между версиями
[непроверенная версия] | [непроверенная версия] |
Elijah (обсуждение | вклад) |
Elijah (обсуждение | вклад) |
||
Строка 93: | Строка 93: | ||
=== Информационный граф === | === Информационный граф === | ||
− | Информационный граф | + | Информационный граф классического алгоритма поиска в ширину приведен на рисунке 1. |
− | [[Файл:BFS- | + | [[Файл:Classical_BFS_ig.png|thumb|center|500px|Рисунок 1. Информационный граф алгоритма BFS.]] |
+ | |||
+ | На рисунке 1 используются следующие обозначения: | ||
+ | |||
+ | [1] - добавление вершины <math>u</math> к множеству <math>F</math>. | ||
+ | |||
+ | [2] - извлечение добавленной вершины <math>v</math> из множества <math>F</math>. | ||
+ | |||
+ | [3] - проверка расстояний до вершин, смежных с вершиной <math>v</math>. | ||
+ | |||
+ | [1] - добавление еще не посещенных вершин в множество <math>P</math>. | ||
+ | |||
+ | [4] - замена множества <math>F</math> на <math>P</math> и проверка его пустоты. В случае негустого множества - переход на следующую итерацию, иначе завершение работы алгоритма. | ||
+ | |||
+ | Данный алгоритм имеет один важный недостаток при реализации: операция [4] требует бесконфликтной возможности добавления элементов в множество P, что, на практике, всегда будет сводиться к сериализации обращений к структуре данных, моделирующих данное множество. | ||
+ | |||
+ | В результате часто используется модификация алгоритма, использующая набор независимых структур данных для каждого из параллельных процессов. Информационный граф данного подхода приведен на рисунке 2. | ||
+ | |||
+ | [[Файл:BFS_ig_parallel_queues.png|thumb|center|500px|Рисунок 2. Информационный граф алгоритма BFS (независимые структуры данных).]] | ||
+ | |||
+ | Обозначения для рисунка 2: | ||
+ | |||
+ | [1] - добавление вершины-источника в множество <math>F</math> . | ||
+ | |||
+ | [2] - разделение данных множества <math>F</math> между процессами | ||
+ | |||
+ | [3] - помещение в множества <math>F_i</math> соответствующих данных из <math>F</math> каждым процессом с номером i. | ||
+ | |||
+ | [4] - извлечение очередной вершины из множеств <math>F_i</math>, обход её соседей и добавление их в множество <math>P_i</math> в случае, если они еще не посещены | ||
+ | |||
+ | [5] - попарное слияние множеств <math>P_i</math> для различных процессов, итоговое преобразование их в множество <math>F</math>. | ||
+ | |||
+ | [6] - проверка условия выхода из цикла | ||
=== Ресурс параллелизма алгоритма === | === Ресурс параллелизма алгоритма === |
Версия 11:58, 8 августа 2017
Алгоритм DCSC поиска компонент сильной связности | |
Последовательный алгоритм | |
Последовательная сложность | O(n + m) |
Объём входных данных | O(m + n) |
Объём выходных данных | O(n) |
Параллельный алгоритм | |
Высота ярусно-параллельной формы | N/A, \max O(V) |
Ширина ярусно-параллельной формы | N/A, \max O(E) |
Основные авторы описания: И.В.Афанасьев
Содержание
- 1 Свойства и структура алгоритма
- 1.1 Общее описание алгоритма
- 1.2 Математическое описание алгоритма
- 1.3 Вычислительное ядро алгоритма
- 1.4 Макроструктура алгоритма
- 1.5 Схема реализации последовательного алгоритма
- 1.6 Последовательная сложность алгоритма
- 1.7 Информационный граф
- 1.8 Ресурс параллелизма алгоритма
- 1.9 Входные и выходные данные алгоритма
- 1.10 Свойства алгоритма
- 2 Программная реализация алгоритма
- 2.1 Особенности реализации последовательного алгоритма
- 2.2 Локальность данных и вычислений
- 2.3 Возможные способы и особенности параллельной реализации алгоритма
- 2.4 Масштабируемость алгоритма и его реализации
- 2.5 Динамические характеристики и эффективность реализации алгоритма
- 2.6 Выводы для классов архитектур
- 2.7 Существующие реализации алгоритма
- 3 Литература
1 Свойства и структура алгоритма
1.1 Общее описание алгоритма
Поиск в ширину (англ. Breadth-First Search, BFS) позволяет вычислить кратчайшие расстояния (в терминах количества рёбер) от выделенной вершины ориентированного графа до всех остальных вершин, и/или построить корневое направленное дерево, расстояния в котором совпадают с расстояниями в исходном графе. Кроме того, поиск в ширину позволяет решать задачу проверки достижимости (существуют ли пути между вершиной источником и остальными вершинами графа). Впервые алгоритм поиска в ширину описан в работах Мура[1] и Ли[2].
Алгоритм основан на обходе вершин графа "по слоям". На каждом шаге есть множество "передовых" вершин, для смежных к которым производится проверка, относятся ли они к еще не посещенным. Все еще не посещенные вершины добавляются в новое множество "передовых" вершин, обрабатываемых на следующем шаге. Изначально в множество "передовых" вершин входит только вершина-источник, от которой и начинается обход.
В последовательном случае алгоритм имеет алгоритмическую сложность O(n + m), где n - число вершин в графе, m - число ребер в графе.
1.2 Математическое описание алгоритма
Пусть задан граф G = (V, E) без весов, и с выделенной вершиной-источником u. Путем P(u,v) между вершинами u и v называется множество ребер (u, v_1), (v_1, v_2), ... (v_{n-1}, v). Длиной пути d(u,v) обозначим число ребер в данном пути между вершинами u и v. Поиск в ширину находит кратчайшие пути d(u,v) от вершины u до всех остальных вершин графа описанным далее образом.
В начале работы алгоритма расстояние до вершины-источника d(u)=0, до остальных вершин d(v) = \infty, \forall v \neq u . Так же в начале работы алгоритм инициализируются множество F = \{u\}.
Далее на каждом шаге алгоритм строится множество вершин P = {w} , таких, что для \forall v \in F \exists (v, w) \in E | d(w) = \infty , при том обновляются расстояния d(w)=d(v)+1 для \forall w \in P . Затем производится переход на следующий шаг до тех пор, пока P \neq \emptyset, при том в начале каждого шага множество F заменяется на P.
1.3 Вычислительное ядро алгоритма
Вычислительным ядром алгоритма является обход вершин, смежной к выбранной вершине v с последующем добавлением еще не посещенных вершин в множество P. Данная операция выполняться на каждом шаге для каждой вершины v \in F.
1.4 Макроструктура алгоритма
Алгоритм последовательно уточняет значения функции d(v).
Структуру можно описать следующим образом:
1. Инициализация: всем вершинам присваивается предполагаемое расстояние d(v)=\infty, кроме вершины-источника, для которой d(u)=0 .
2. Помещение вершины источника v в множество "передовых" вершин F.
3. Обход вершин множества F.
а) Инициализация множества P=\emptyset.
б) Для каждой вершины v \in F обход всех вершин w | \exists (v, w) (смежных с ней), c помещением в множество P таких вершин w | d(w)=\infty.
в) Замена множества F на P и переход на шаг 3 в случае, если множество F \neq \emptyset.
1.5 Схема реализации последовательного алгоритма
Простейшая версия алгоритма поиск в ширину может быть реализована при помощи очередей на языке C++ следующим образом. Код приведен в предположении, что граф хранится в формате сжатого списка смежности: для каждой вершины в массиве vertices_to_edges_ptrs хранятся индекс начала и индекс конца списка смежных с ней вершин из массива dst_ids.
// init distances
for(int i = 0; i < vertices_count; i++)
_result[i] = MAX_INT;
// init queue and first vertex
std::queue<int> vertex_queue;
vertex_queue.push(_source_vertex);
_result[_source_vertex] = 1;
// do bfs
while(vertex_queue.size() > 0)
{
int cur_vertex = vertex_queue.front();
vertex_queue.pop();
long long first = vertices_to_edges_ptrs[cur_vertex];
long long last = vertices_to_edges_ptrs[cur_vertex + 1];
for(long long i = first; i < last; i++)
{
int dst_id = dst_ids[i];
if(_result[dst_id] == MAX_INT)
{
_result[dst_id] = _result[src_id] + 1;
vertex_queue.push(dst_id);
}
}
}
1.6 Последовательная сложность алгоритма
Алгоритм имеет последовательную сложность O(m + n), где n и m - число вершин и ребер графа соответственно: алгоритм инициализирует начальный массив расстояний - O(n) операций, а затем обходит каждую вершину один единственный раз - O(m) операций.
1.7 Информационный граф
Информационный граф классического алгоритма поиска в ширину приведен на рисунке 1.
На рисунке 1 используются следующие обозначения:
[1] - добавление вершины u к множеству F.
[2] - извлечение добавленной вершины v из множества F.
[3] - проверка расстояний до вершин, смежных с вершиной v.
[1] - добавление еще не посещенных вершин в множество P.
[4] - замена множества F на P и проверка его пустоты. В случае негустого множества - переход на следующую итерацию, иначе завершение работы алгоритма.
Данный алгоритм имеет один важный недостаток при реализации: операция [4] требует бесконфликтной возможности добавления элементов в множество P, что, на практике, всегда будет сводиться к сериализации обращений к структуре данных, моделирующих данное множество.
В результате часто используется модификация алгоритма, использующая набор независимых структур данных для каждого из параллельных процессов. Информационный граф данного подхода приведен на рисунке 2.
Обозначения для рисунка 2:
[1] - добавление вершины-источника в множество F .
[2] - разделение данных множества F между процессами
[3] - помещение в множества F_i соответствующих данных из F каждым процессом с номером i.
[4] - извлечение очередной вершины из множеств F_i, обход её соседей и добавление их в множество P_i в случае, если они еще не посещены
[5] - попарное слияние множеств P_i для различных процессов, итоговое преобразование их в множество F.
[6] - проверка условия выхода из цикла
1.8 Ресурс параллелизма алгоритма
1.9 Входные и выходные данные алгоритма
1.10 Свойства алгоритма
2 Программная реализация алгоритма
2.1 Особенности реализации последовательного алгоритма
2.2 Локальность данных и вычислений
2.2.1 Локальность реализации алгоритма
2.2.1.1 Структура обращений в память и качественная оценка локальности
2.2.1.2 Количественная оценка локальности
2.3 Возможные способы и особенности параллельной реализации алгоритма
2.4 Масштабируемость алгоритма и его реализации
2.4.1 Масштабируемость алгоритма
2.4.2 Масштабируемость реализации алгоритма
2.5 Динамические характеристики и эффективность реализации алгоритма
2.6 Выводы для классов архитектур
2.7 Существующие реализации алгоритма
- Распределённый алгоритм поиска вширь является вычислительным ядром бенчмарка Graph500.
- C++: Boost Graph Library (функции
breadth_first_search
,breadth_first_visit
). - C++, MPI: Parallel Boost Graph Library (функция
breadth_first_search
). - Java: WebGraph (класс
ParallelBreadthFirstVisit
), многопоточная реализация. - Python: NetworkX (функция
bfs_edges
). - Python/C++: NetworKit (класс
networkit.graph.BFS
).