Как предполагает Свеннингссон, приоритетная очередь поиска хорошо подходит как для Крускала, так и для Прима (по крайней мере, автор объявляет об этом в своей статье .) Проблема с Крускалом заключается в том, что она требуету вас есть O (log n) алгоритм поиска объединения .Структура соединения с поиском объединения с чисто функциональным интерфейсом описана здесь , но она использует изменяемое состояние внутри, и чисто функциональная реализация может быть невозможна, и на самом деле, есть несколько проблем, когда эффективное чисто функциональное решениенеизвестно, как обсуждалось в этом связанном вопросе SO.
Неприменимой альтернативой является реализация алгоритма поиска объединения в монаде ST.Поиск в Hackage обнаружил, что пакет эквивалентности удовлетворяет нашим потребностям.Ниже приведена реализация Kruskal с использованием Data.Equivalence.Monad из пакета эквивалентности :
import Data.Equivalence.Monad
import Data.Graph as G
import Data.List(sortBy)
import Data.Map as M
import Control.Monad(filterM)
import Data.Ord(comparing)
run = runEquivM (const ()) (const $ const ())
kruskal weight graph = run $
filterM go (sortBy (comparing weight) theEdges)
where
theEdges = G.edges graph
go (u,v) = do
eq <- equivalent u v
if eq then return False else
equate u v >> return True
. Может использоваться следующим образом:
fromL xs = fromJust . flip M.lookup (M.fromList xs)
testWeights = fromL [((1,2),1),((2,3),4),((3,4),5),((1,4),30),((1,3),4)]
testGraph = G.buildG (1,4) [(1,2),(2,3),(3,4),(1,4),(1,3)]
test = kruskal testWeights testGraph
и работаеттест дает:
[(1,2),(1,3),(3,4)]
Следует отметить, что время выполнения зависит от веса, выполняемого за время O (1), однако fromL
создает функцию веса, выполняющуюся за время O (log (n)), это можно улучшить до O (1) времени, используя массивы или просто отслеживая вес в списке ввода, но на самом деле это не часть алгоритма.