Поиск в ширину: различия между версиями

Материал из Поле цифровой дидактики
м (1 версия импортирована)
Строка 1: Строка 1:
{{другие значения|BFS}}
'''Поиск в ширину''' ('''BFS''') — один из методов обхода [[Граф (математика)|графа]]. Пусть задан граф <math>G = (V, E)</math> и выделена исходная вершина <math>s</math>. Алгоритм поиска в ширину систематически обходит все ребра <math>G</math> для «открытия» всех вершин, достижимых из <math>s</math>, вычисляя при этом расстояние (минимальное количество рёбер) от <math>s</math> до каждой достижимой из <math>s</math> вершины.  
[[Файл:Breadth-First-Search-Algorithm.gif|thumb|Поиск в ширину]]
 
'''Поиск в ширину''' ({{lang-en|breadth-first search}}, '''BFS''') — один из методов обхода [[Граф (математика)|графа]]. Пусть задан граф <math>G = (V, E)</math> и выделена исходная вершина <math>s</math>. Алгоритм поиска в ширину систематически обходит все ребра <math>G</math> для «открытия» всех вершин, достижимых из <math>s</math>, вычисляя при этом расстояние (минимальное количество рёбер) от <math>s</math> до каждой достижимой из <math>s</math> вершины. Алгоритм работает как для [[Ориентированный граф|ориентированных]], так и для неориентированных графов.<ref>{{Книга|автор=Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн|заглавие=Алгоритмы: построение и анализ|ответственный=|издание=3-е изд|место=|издательство=Издательский дом "Вильямс"|год=2013|страницы=630|страниц=1328|isbn=978-5-8459-1794-2 (рус.)|isbn2=978-0-2620-3384-8 (англ.)}}</ref>
 
Поиск в ширину имеет такое название потому, что в процессе обхода мы идём вширь, то есть перед тем как приступить к поиску вершин на расстоянии <math>k + 1</math>, выполняется обход вершин на расстоянии <math>k</math>.
Поиск в ширину имеет такое название потому, что в процессе обхода мы идём вширь, то есть перед тем как приступить к поиску вершин на расстоянии <math>k + 1</math>, выполняется обход вершин на расстоянии <math>k</math>.


Строка 9: Строка 5:


== Работа алгоритма ==
== Работа алгоритма ==
[[Файл:Animated BFS.gif|thumb|Белый — вершина, которая ещё не обнаружена. Серый — вершина, уже обнаруженная и добавленная в очередь. Чёрный — вершина, извлечённая из очереди<ref name="nstu" />.]]
Поиск в ширину работает путём последовательного просмотра отдельных ''уровней'' [[Граф (математика)|графа]], начиная с узла-источника <math>u</math>.
Поиск в ширину работает путём последовательного просмотра отдельных ''уровней'' [[Граф (математика)|графа]], начиная с узла-источника <math>u</math>.


Строка 105: Строка 100:
[[Поиск по критерию стоимости]] является обобщением поиска в ширину и оптимален на взвешенном графе с неотрицательными весами рёбер. Алгоритм посещает узлы графа в порядке возрастания стоимости пути из начального узла и обычно использует [[Очередь с приоритетом (программирование)|очередь с приоритетами]].
[[Поиск по критерию стоимости]] является обобщением поиска в ширину и оптимален на взвешенном графе с неотрицательными весами рёбер. Алгоритм посещает узлы графа в порядке возрастания стоимости пути из начального узла и обычно использует [[Очередь с приоритетом (программирование)|очередь с приоритетами]].


== История и применения ==
Поиск в ширину был формально предложен [[Мур, Эдвард Форест|Э. Ф. Муром]] в контексте поиска пути в лабиринте<ref name="moore1959" />. Ли независимо открыл тот же алгоритм в контексте [[Алгоритм Ли|разводки проводников на печатных платах]]<ref name="lee1961" /><ref>Cormen ''et al'', Introduction to Algorithms, 3rd edition, p. 623</ref><ref name="se_origin" />.
Поиск в ширину может применяться для решения задач, связанных с [[Теория графов|теорией графов]]:
* [[Волновой алгоритм]] [[Поиск пути|поиска пути]] в лабиринте<ref name="moore1959" />
* Волновая [[трассировка печатных плат]]<ref name="lee1961" />
* Поиск [[Компонента связности графа|компонент связности]] в графе
* [[Задача о кратчайшем пути|Поиск кратчайшего пути]] между двумя узлами невзвешенного графа
* [[Поиск в пространстве состояний]]: нахождение решения задачи с наименьшим числом ходов, если каждое состояние системы можно представить вершиной графа, а переходы из одного состояния в другое — рёбрами графа<ref name="emaxx" />
* Нахождение кратчайшего цикла в ориентированном невзвешенном графе<ref name="emaxx" />
* Нахождение всех вершин и рёбер, лежащих на каком-либо кратчайшем пути между двумя вершинами <math>a</math> и <math>b</math><ref name="emaxx" />
* Поиск увеличивающего пути в [[Форда-Фалкерсона алгоритм|алгоритме Форда-Фалкерсона]] ([[алгоритм Эдмондса-Карпа]])
== См. также ==
{{wikibooks|Реализации алгоритмов/Поиск в ширину|Примеры реализации поиска в ширину}}
* [[Поиск с ограничением глубины]]
== Примечания ==
{{примечания|1|refs=
  <ref name="emaxx">MAXimal :: algo :: [http://e-maxx.ru/algo/bfs Поиск в ширину в графе и его приложения] {{Wayback|url=http://e-maxx.ru/algo/bfs |date=20130916134130 }}</ref>
  <ref name="nstu">НГТУ Структуры и алгоритмы обработки данных [http://edu.nstu.ru/courses/saod/bfs.htm Обход графа в ширину] {{Wayback|url=http://edu.nstu.ru/courses/saod/bfs.htm |date=20121230194547 }}</ref>
  <ref name="moore1959">{{source|Q28048674|ref=Moore|ref-year=1959}} <!-- The shortest path through a maze // International Symposium on the Theory of Switching — 1957 --></ref>
  <ref name="lee1961">C. Y. Lee (1961), [https://dx.doi.org/10.1109/TEC.1961.5219222 An algorithm for path connection and its applications]. ''IRE Transactions on Electronic Computers'', EC-10(3), pp. 346–365</ref>
  <ref name="se_origin">''Mathematics Stack Exchange'' [http://math.stackexchange.com/questions/283914/ Origin of the Breadth-First Search algorithm]</ref>
}}
== Литература ==
* {{книга
| автор = T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein
| заглавие = Introduction to Algorithms
| издание = 3rd edition
| издательство = The MIT Press
| год = 2009
| isbn = 978-0-262-03384-8
}}. Перевод 2-го издания: {{книга
|автор = Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн
|заглавие = '''Алгоритмы: построение и анализ'''
|оригинал = Introduction to Algorithms
|ссылка =
|издание = 2-е изд
|место =  М.
|издательство = [[Вильямс (издательство)|«Вильямс»]]
|год = 2006
|страницы = 1296
|isbn = 0-07-013151-1
}}
* {{source|Q21694522|part=Глава 5. Метод уменьшения размера задачи: Поиск в ширину}}
== Ссылки ==
{{Навигация}}
* ''Steven M. Rubin'' [http://www.rulabinsky.com/cavd/text/chap04-3.html Computer Aids for VLSI Design. Chapter 4: Synthesis Tools]
* [http://100byte.ru/100btwrks/wv/wv.html Волновой алгоритм поиска пути]
* [http://kvodo.ru/search-width.html Поиск в ширину на Pascal и C++]
{{Алгоритмы поиска на графах}}
[[Категория:Алгоритмы поиска на графах]]
[[Категория:Алгоритмы поиска]]
[[Категория:Алгоритмы поиска]]

Версия 21:21, 19 октября 2022

Поиск в ширину (BFS) — один из методов обхода графа. Пусть задан граф [math]\displaystyle{ G = (V, E) }[/math] и выделена исходная вершина [math]\displaystyle{ s }[/math]. Алгоритм поиска в ширину систематически обходит все ребра [math]\displaystyle{ G }[/math] для «открытия» всех вершин, достижимых из [math]\displaystyle{ s }[/math], вычисляя при этом расстояние (минимальное количество рёбер) от [math]\displaystyle{ s }[/math] до каждой достижимой из [math]\displaystyle{ s }[/math] вершины. Поиск в ширину имеет такое название потому, что в процессе обхода мы идём вширь, то есть перед тем как приступить к поиску вершин на расстоянии [math]\displaystyle{ k + 1 }[/math], выполняется обход вершин на расстоянии [math]\displaystyle{ k }[/math].

Поиск в ширину является одним из неинформированных алгоритмов поиска<ref name="emaxx" />.

Работа алгоритма

Поиск в ширину работает путём последовательного просмотра отдельных уровней графа, начиная с узла-источника [math]\displaystyle{ u }[/math].

Рассмотрим все рёбра [math]\displaystyle{ (u,v) }[/math], выходящие из узла [math]\displaystyle{ u }[/math]. Если очередной узел [math]\displaystyle{ v }[/math] является целевым узлом, то поиск завершается; в противном случае узел [math]\displaystyle{ v }[/math] добавляется в очередь. После того, как будут проверены все рёбра, выходящие из узла [math]\displaystyle{ u }[/math], из очереди извлекается следующий узел [math]\displaystyle{ u }[/math], и процесс повторяется.

Неформальное описание

  1. Поместить узел, с которого начинается поиск, в изначально пустую очередь.
  2. Извлечь из начала очереди узел [math]\displaystyle{ u }[/math] и пометить его как развёрнутый.
    • Если узел [math]\displaystyle{ u }[/math] является целевым узлом, то завершить поиск с результатом «успех».
    • В противном случае, в конец очереди добавляются все преемники узла [math]\displaystyle{ u }[/math], которые ещё не развёрнуты и не находятся в очереди.
  3. Если очередь пуста, то все узлы связного графа были просмотрены, следовательно, целевой узел недостижим из начального; завершить поиск с результатом «неудача».
  4. Вернуться к п. 2.

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

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

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

Рекурсивная формулировка:

BFS(start_node, goal_node) {
  return BFS'({start_node}, ∅, goal_node);
}
BFS'(fringe, visited, goal_node) {
  if(fringe == ∅) {
    // Целевой узел не найден
    return false; 
  }
  if (goal_nodefringe) {
    return true;
  }
  return BFS'({child | xfringe, child ∈ expand(x)} \ visited, visitedfringe, goal_node);
}

Итеративная формулировка:

BFS(start_node, goal_node) {
 for(all nodes i) visited[i] = false; // изначально список посещённых узлов пуст
 queue.push(start_node);              // начиная с узла-источника
 visited[start_node] = true;
 while(! queue.empty() ) {            // пока очередь не пуста
  node = queue.pop();                 // извлечь первый элемент в очереди
  if(node == goal_node) {
   return true;                       // проверить, не является ли текущий узел целевым
  }
  foreach(child in expand(node)) {    // все преемники текущего узла, ...
   if(visited[child] == false) {      // ... которые ещё не были посещены ...
    queue.push(child);                // ... добавить в конец очереди...
    visited[child] = true;            // ... и пометить как посещённые
   }
  }
 }
 return false;                        // Целевой узел недостижим
}

Реализация на Pascal:

function BFS(v : Node) : Boolean;
begin
  enqueue(v);
  while queue is not empty do
  begin
    curr := dequeue();
    if is_goal(curr) then
    begin
      BFS := true;
      exit;
    end;
    mark(curr);
    for next in successors(curr) do
      if not marked(next) then
      begin
        enqueue(next);
      end;
  end;
  BFS := false;
end;

Свойства

Обозначим число вершин и рёбер в графе как [math]\displaystyle{ \vert V \vert }[/math] и [math]\displaystyle{ \vert E \vert }[/math] соответственно.

Пространственная сложность

Так как в памяти хранятся все развёрнутые узлы, пространственная сложность алгоритма составляет [math]\displaystyle{ O(\vert V \vert + \vert E \vert) }[/math]<ref name="emaxx" />.

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

Временная сложность

Так как в худшем случае алгоритм посещает все узлы графа, при хранении графа в виде списков смежности, временная сложность алгоритма составляет [math]\displaystyle{ O(\vert V \vert + \vert E \vert) }[/math]<ref name="emaxx" /><ref name="nstu" />.

Полнота

Если у каждого узла имеется конечное число преемников, алгоритм является полным: если решение существует, алгоритм поиска в ширину его находит, независимо от того, является ли граф конечным. Однако если решения не существует, на бесконечном графе поиск не завершается.

Оптимальность

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

Поиск по критерию стоимости является обобщением поиска в ширину и оптимален на взвешенном графе с неотрицательными весами рёбер. Алгоритм посещает узлы графа в порядке возрастания стоимости пути из начального узла и обычно использует очередь с приоритетами.