Как я могу написать алгоритм MST (Prim или Kruskal) на Haskell?

Я могу написать алгоритмы как Prim, так и Kruskal, чтобы найти минимальное остовное дерево в C ++ или Java, но я хочу знать, как реализовать их в Haskell с помощью O (mlogm) или O (mlogn) (чисто функциональные программы лучше). Большое спасибо.


person Lin Jin    schedule 27.11.2010    source источник
comment
Можете объяснить, в чем проблема с реализацией? Вам будет легче помочь, если мы будем знать, в чем ваша проблема.   -  person fuz    schedule 27.11.2010
comment
Проблема в основном в массиве.   -  person Lin Jin    schedule 27.11.2010
comment
Прим нужен массив, чтобы записать, был ли выбран узел, а Крускалу нужен Union Find Set. Модификация массива стоит дорого.   -  person Lin Jin    schedule 27.11.2010
comment
@Jin Lin: Вы можете использовать дерево вместо массива (например, Data.Set или что-то еще) или какой-то изменяемый массив. Но предпочитаю дерево.   -  person fuz    schedule 27.11.2010
comment
@FUZxxl: Ну, сложность дерева - O (log (n)), а изменяемый массив не является чисто функциональным. Есть ли другие решения? Может быть, что-то вроде чисто функциональной реализации очереди.   -  person Lin Jin    schedule 27.11.2010
comment
Вы можете обернуть изменяемый массив в монаду состояний. Это очень функционально.   -  person fuz    schedule 27.11.2010
comment
Я думаю, что дерево может быть лучше, оно не повлияет на сложность, когда я использую его в качестве массива флагов в примерах. Спасибо. :)   -  person Lin Jin    schedule 27.11.2010
comment
@FUZxxl, как можно обернуть изменяемый массив в монаду состояния? Я знаю STArray в монаде ST и IOArray в монаде IO   -  person is7s    schedule 27.06.2011
comment
@ is7s Именю ST с состоянием. Извините за неточность.   -  person fuz    schedule 27.06.2011


Ответы (3)


Как предлагает Свеннингссон, очередь поиска с приоритетом хорошо подходит как для Крускала, так и для Прима (по крайней мере, автор заявляет об этом в своем paper.) Проблема с Kruskal заключается в том, что он требует наличия O (log n) алгоритм поиска объединения. Структура данных union-find с чисто функциональным интерфейсом описана здесь, но это использует изменяемое состояние внутри, и чисто функциональная реализация может быть невозможна, и на самом деле существует несколько проблем, для которых эффективное чисто функциональное решение не известно, как обсуждается в этот вопрос SO.

Альтернативой, не являющейся чистой, является реализация алгоритма поиска объединения в монаде ST. Поиск на Hackage обнаружил, что пакет эквивалентность соответствует нашим потребностям. Ниже представлена ​​реализация Kruskal с использованием Data.Equivalence.Monad из пакета Equivalence:

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) с помощью массивов или просто отслеживание веса во входном списке, но на самом деле это не часть алгоритма.

person HaskellElephant    schedule 27.11.2010
comment
Кажется, существует эффективная постоянная реализация: www.lri.fr/~filliatr/ftp/publis/puf-wml07.ps - person Landei; 26.06.2012
comment
@Landei, похоже, я обновлю свой ответ, как только проведу небольшое исследование по этому поводу. - person HaskellElephant; 26.06.2012

Вот грубая реализация Крускала.

import Data.List(sort)
import Data.Set (Set, member, fromList, insert, union)

data Edge a = Edge a a Double deriving Show

instance (Eq a) => Eq (Edge a) where
  Edge x1 y1 z1 == Edge x2 y2 z2 = x1 == x2 && y1 == y2 && z1 == z2

instance Eq a => Ord (Edge a) where
  (Edge _ _ x) `compare` (Edge _ _ y) = x `compare` y

kruskal :: Ord a => [Edge a] -> [Edge a]
kruskal = fst . foldl mst ([],[]) . sort

mst :: Ord a => ([Edge a],[Set a]) -> Edge a -> ([Edge a],[Set a])
mst (es, sets) e@(Edge p q _) = step $ extract sets where
   step (rest, Nothing, Nothing) = (e : es, fromList [p,q] : rest)
   step (rest, Just ps, Nothing) = (e : es, q `insert` ps : rest)
   step (rest, Nothing, Just qs) = (e : es, p `insert` qs : rest)
   step (rest, Just ps, Just qs) | ps == qs = (es, sets) --circle
                                 | otherwise = (e : es, ps `union` qs : rest)
   extract = foldr f ([], Nothing, Nothing) where
       f s (list, setp, setq) =
            let list' = if member p s || member q s then list else s:list
                setp' = if member p s then Just s else setp
                setq' = if member q s then Just s else setq
            in (list', setp', setq') 

Первым шагом является сортировка ребер, что составляет O (n log n). Проблема состоит в том, чтобы быстрее найти наборы вершин в функции извлечения. Я не смог найти более быстрого решения для этого, может у кого-то есть идея ...

[Обновлять]

Для реализации Scala я использовал структуру данных, подобную карте, для (надеюсь) лучшей производительности, но, к сожалению, она использует изменяемые наборы, и я понятия не имею, как перевести это в Haskell. Код есть в моем блоге (извините, описание на немецком языке): http://dgronau.wordpress.com/2010/11/28/nochmal-kruskal/

person Landei    schedule 27.11.2010

Я думаю, что очередь поиска с приоритетом - это то, что вы ищете. Его можно оптимально реализовать на функциональном языке, как продемонстрировал Ральф Хинце в статье. Похоже, что эту бумагу можно получить только в библиотеке acm за определенную плату.

person svenningsson    schedule 27.11.2010
comment
Статья доступна здесь: (portal.acm .org / - person HaskellElephant; 27.11.2010