Алгоритм Прима описывает процесс нахождения минимального остовного дерева в графах. Рассматриваются его принципы и применение.
Алгоритм Прима предназначен для поиска минимального остовного дерева во взвешенном связном неориентированном графе. Его суть заключается в поэтапном расширении поддерева, начиная с произвольной вершины и добавляя на каждом шаге ребро минимального веса, которое соединяет уже построенное дерево с новой вершиной. Исторически алгоритм впервые был открыт Войцехом Ярником в 1930 году, затем независимо переоткрыт Робертом Примом в 1957 и Эдсгером Дейкстрой в 1959 году, что отражает его фундаментальность и универсальность применения в теории графов и практических задачах оптимизации[1].
Для реализации алгоритма вводятся ключевые структуры:
* \(d[i]\), расстояние от вершины \(i\) до построенного дерева; это минимальный вес ребра, которым можно добавить вершину \(i\).
* \(p[i]\), предок вершины \(i\), то есть вершина из дерева, через которую достигается минимальное ребро.
* \(w(i,j)\), вес ребра между вершинами \(i\) и \(j\).
* Приоритетная очередь \(Q\), хранящая вершины вне текущего дерева с ключами равными значениям \(d[i]\).
* Множество ребер минимального остовного дерева \(T\).
Изначально все расстояния устанавливаются в бесконечность (\(d[i]\gets \infty\)), кроме стартовой вершины (обозначенной индексом 1), у которой значение равно нулю (\(d[1]\gets 0\)). Предки всех вершин изначально равны nil. Множество ребер MST пусто (\(T \gets \emptyset\)).
Алгоритм циклически извлекает из очереди вершину с минимальным значением ключа (\(v \gets Extract.Min(Q)\)) и добавляет к остову ребро \((p[v], v)\), если оно определено. Затем происходит релаксация смежных вершин: для каждой соседней вершины \(u \in Q\), если вес ребра \((v,u)\), равный \(w(v,u)\), меньше текущего значения расстояния \(d[u]\), обновляется это значение и предок:
\[ d[u] \gets w(v,u), \quad p[u] \gets v. \]
Этот процесс продолжается до тех пор, пока очередь не опустеет. В итоге множество ребер \(T\) будет содержать минимальное остовное дерево графа.
Производительность алгоритма существенно зависит от способа хранения графа и организации приоритетной очереди.
При использовании простого массива для реализации очереди операция извлечения минимума занимает время порядка \(O(n),\) где число вершин равно \(n\). Обновление ключа происходит за константное время \(O(1).\).
Это даёт общую сложность порядка \(O(n^2).\).
Если же очередь реализована через бинарную кучу (пирамиду), то операции извлечения минимума и обновления ключа имеют сложность порядка \(O(\log n).\).
Тогда суммарная асимптотика будет порядка \(O(m \log n),\) где \(m\), количество рёбер графа. При использовании фибоначчиевых пирамид операция извлечения минимума выполняется за \(O(\log n),\) а обновление ключа — за \(O(1).\).
Такой подход позволяет оптимизировать работу на разреженных графах[1].
Алгоритм базируется на лемме о безопасном ребре: на каждом шаге выбирается такое ребро минимальной стоимости, которое соединяет уже построенное дерево с новой вершиной, не образуя циклов. Постепенно добавляя эти безопасные ребра, алгоритм гарантированно строит минимальное остовное дерево.
Выбор начальной вершины не влияет на итоговый вес MST, однако может изменить структуру самого дерева. Алгоритм завершится после добавления ровно \( |V| - 1 \) рёбер, где \( |V| \), число вершин исходного графа[4].
plaintext
T ← ∅
для каждой вершины i ∈ V:
d[i] ← ∞
p[i] ← nil
d[1] ← 0
Q ← V
пока Q не пуста:
v ← Extract.Min(Q)
если p[v] ≠ nil:
T ← T ∪ {(p[v], v)}
для каждой смежной с v вершины u ∈ Q:
если w(v,u) < d[u]:
d[u] ← w(v,u)
p[u] ← v
Каждый цикл добавляет одну новую вершину к дереву посредством ребра минимального веса от существующего поддерева.
Алгоритм применим только к связным неориентированным графам с заданными весами рёбер. Если граф несвязен, MST построить нельзя, задача теряет смысл.
В реальных приложениях нередко приходится работать с большими разреженными графами. В таких случаях предпочтительнее использовать структуры данных с эффективным временем работы операций вставки и извлечения минимума (например, биномиальные или фибоначчиевы кучи).
При работе с плотными графами более простой массив может оказаться предпочтительнее из-за меньших накладных расходов на управление кучей.
Важно учитывать также требования по памяти и особенности конкретной среды выполнения: например, реализация через сбалансированные бинарные деревья поиска позволяет упростить операцию уменьшения ключа (\(\mathrm{decreaseKey}\)), хотя может иметь большую константу времени работы.
Алгоритм Прима сравним по механике с алгоритмом Дейкстры: оба используют жадный подход с поддержанием множества уже обработанных вершин и выбором следующей оптимальной вершины по определённому критерию.
В отличие от алгоритма Крускала или Борувки, которые работают путём сортировки рёбер или объединения компонент соответственно, Прим строит MST пошагово от одной стартовой точки.
Это часто делает алгоритм проще для реализации на динамически меняющихся графах или когда необходимо быстро находить MST с учётом новых условий.
Алгоритм Прима обеспечивает надёжное нахождение MST со сложностью от квадратичной до почти линейно логарифмической в зависимости от используемых структур данных:
* Простая очередь на массиве, время порядка \(O(n^2).\)
* Бинарная куча, время порядка \(O(m \log n).\)
* Фибоначчиева куча, улучшенная эффективность за счёт быстрого уменьшения ключей,
что особенно важно при большом количестве рёбер.
Благодаря этим свойствам он широко применяется как теоретический инструмент и в инженерных задачах оптимизации сетей связи, электроэнергетики и транспортных систем[4][5].
[1] https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8...
[2] http://e-maxx.ru/algo/mst_prim
[3] https://ru.algorithmica.org/cs/spanning-trees/prim/
[4] https://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%B...
[5] https://foxford.ru/wiki/informatika/postroenie-minimalnogo-ostovno...
Создаётся краткое изложение…