Найбільш економічний спосіб підключення всього
Враховуючи набір вузлів та зважені ребра між деякими з них, мінімальне покриваюче дерево (МПД) — це підмножина ребер, яка з’єднує кожен вузол в єдине дерево — без циклів, без відключених частин — за найменшою можливою сумарною вагою ребер. Воно відповідає на питання, яке постійно виникає в інженерії: як підключити електромережу, прокласти труби, маршрутизувати кабелі або з’єднати набір міст дорогами, використовуючи найменший можливий обсяг матеріалу, при цьому гарантуючи, що кожна точка доступна з будь-якої іншої? Покриваюче дерево для n вузлів завжди має рівно n-1 ребер — будь-яка менша кількість призводить до відключення, будь-яка більша — до надмірної циклографії — тому проблема МПД насправді полягає в тому, які саме n-1 ребер зберегти з усіх доступних.
Алгоритм Кршко́ла: сортувати, потім жадібно додавати ребра
Алгоритм Кршко́ла (1956) дивуючий своєю простотою: відсортувати всі ребра графа за вагою, а потім проходити по них від найдешевшого до найдорожчого, додаючи кожне ребро до зростаючого лісу, якщо це не призводить до утворення циклу. Цей єдиний глобальний принцип – завжди брати найдешевший ребро, яке не закриває петлю – достатньо для гарантування оптимального рішення, що вражає з огляду на мінімальну кількість перевірок вперед, які використовує алгоритм.
відсортувати всі ребра за зростанням ваги для кожного ребра (u, v) в цьому порядку: якщо find(u) != find(v): # u і v знаходяться в різних компонентах union(u, v) # об'єднати їх додати ребро (u, v) до MST # зупинитись, коли додано n-1 ребер
Операції find/union є точно Union-Find (розділений набір): find(x) повідомляє, до якого компонента належить x, а union(u, v) об'єднує два компоненти в один. За допомогою стандартних оптимізацій – об’єднання за рангом/розміром та стиснення шляхів під час find – кожна операція виконується за приблизно постійний час (технічно O(alpha(n)), де alpha є інверсною функцією Акермана, яка фактично дорівнює 4 або 5 для будь-якого розміру графа, який може коли-небудь існувати на практиці). Домінуючою вартістю алгоритму Кршко́ла є початкове сортування, O(E log E).
sort all edges by weight ascending
for each edge (u, v) in that order:
if find(u) != find(v): # u and v are in different components
union(u, v) # merge them
add edge (u, v) to the MST
# stop once n-1 edges have been added
Алгоритм Пріма: вирощуйте одну дереватину з насіння
Алгоритм Пріма (1957, хоча основна ідея датується Ярніком у 1930 році) використовує інший шлях до одного й того ж оптимального рішення: починайтеся з будь-якого окремого вузла та повторно розширюйте потокове дерево шляхом додавання найдешевшого ребра, яке з’єднує вузол, який вже є в дереві, з вузлом, який ще не в ньому. Де Красбур думає глобально про кожен край у всьому графі одночасно, Пріма росте один із зв’язних об’єктів назовні, по одне ребро за раз, і ніколи не потрібно турбуватися про випадкове створення циклу — за конструкцією, кожне додаване ребро з’єднує новий вузол, тому цикл структурно неможливий.
почніть із будь-якого окремого вузла в дереві підтримуйте чергу пріоритетів (вага, ребро) для кожного краю, що виходить від потокового дерева доки дерево має менше ніж n вузлів: витягніть найдешевшу (вага, ребро), чиє далеке кінцеве значення ще не в дереві додайте це ребро та його далеке кінцеве значення до дерева завантажте всі краї, що виходять від новододаного вузла З бінарною кулею як чергою пріоритетів алгоритм Пріма працює за часом O(E log V); з Фібонічовою кулею це покращується до O(E + V log V), що має значення для дуже щільних графів. На практиці Пріма схиляється до кращого вибору для щільних графів (багато ребер відносно вузлів), оскільки їй ніколи не потрібно сортувати краї, які вона ніколи не торкатиметься, тоді як Красбур схильний до переваги для розріджених графів, оскільки сортування короткого списку ребер дешево і мінімальне обслуговування Union-Find.
start with any single node in the tree
maintain a priority queue of (weight, edge) for every edge leaving the current tree
while the tree has fewer than n nodes:
pop the cheapest (weight, edge) whose far endpoint is not yet in the tree
add that edge and its far endpoint to the tree
push all edges leaving the newly added node
Чому обидва гарантовані оптимальними: властивість розрізу
Обидва алгоритми базуються на одній основній теоремі – властивості розрізу: для будь-якого способу розділення вузлів графа на дві непусті групи, найдешевеньший перехід між цими групами через край гарантовано належить до мінімального сплету (за умови відмінних ваг ребер, для простоти). Kruskal використовує це опосередковано, завжди приймаючи глобально найдешевший безпечний край; Prim використовує це явно на кожному кроці, вибираючи найдешевшу межу між групами "вузли вже у дереві" та "вузли ще не у дереві". Оскільки кожен край, який додає будь-який з цих алгоритмів, задовольняє властивості розрізу, індуктивний аргумент показує, що остаточний набір країв повинен бути допустимим мінімальним сплетеним графіком – жадібний вибір, зроблений правильно, ланцюжиться в глобально оптимальну структуру, що є незвичайною; більшість задач оптимізації графів не дозволяють чисто жадібне рішення.
Мінімальні Спангуючі Дерева: Де MST відображаються за межами схем проводки
Мінімальні сплетіння дерев також є основою однозв’язного ієрархічного кластеризації: виконання алгоритму та зупинка, не додаючи k-1 найдорожчих ребер, природним чином розділяє граф на k кластерів, оскільки видалення цих найбільш дорогих з’єднувальних ребер – це те, як ви розділяєте найслабші зв’язки. MST використовуються для наближення рішень задачі про постачання вантажів (MST дає доведену нижню межу оптимального маршруту, а подвоєння його ребер дає прогумований маршрут у межах фактора 2 від оптимального для метричних прикладів), і вони з’являються в дизайні схем, аналізі надійності мереж та сегментації зображень, де піксельні графіки подібності розрізають вздовж їх найдорожчих ребер MST, щоб розділити області.
Часті запитання
Чи завжди алгоритми Краскала та Пріма створюють один і той самий граф?
Вони завжди генерують мінімальне обв’язне дерево з однаковим загальним вагою, але якщо граф має ребра з рівними вагами, вони можуть вибрати різні конкретні ребра та, відповідно, створити різні (але однаково оптимальні) дерева. З усіма різними вагами ребер, мінімальне обв’язне дерево є унікальним, і обидва алгоритми збігаються до точно одного й того ж набору ребер.
Чому мінімальне обв’язне дерево не може містити циклу?
Обв’язне дерево за визначенням має рівно n-1 ребра, які з’єднують n вузлів без надмірності; будь-який цикл означає, що принаймні одне ребро в цьому циклі можна було б видалити, не порушуючи зв'язність графа, що призвело б до дешевших і пов’язаних підграфів і суперечило б мінімальності. Обидва алгоритми явно уникають додавання ребра, яке закриває цикл.
Чи слід мені використовувати алгоритм Краскала чи Пріма для заданого графа?
Алгоритм Пріма зазвичай швидший на щільних графах (багато ребер відносно вузлів), оскільки він ніколи не торкається ребер поза межами кордону зростаючого дерева, тоді як алгоритм Краскала зазвичай швидший на розріджених графах, оскільки сортування короткого списку ребер і виконання операцій Union-Find майже постійного часу є дешевим. Обидва мають однакову гарантію оптимальної ваги, тому вибір дійсно залежить від продуктивності вашого конкретного графа щодо щільності.
Спробуйте наживо
Усе, що вище, працює прямо у вашому браузері — відкрийте Minimum Spanning Tree і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.
▶ Відкрити симуляцію Minimum Spanning Tree