2014-02-16 18 views
1

我想爲Haskell實現一個最長路徑算法。我只使用了Haskell大約兩週的時間,並且之前沒有在功能語言中做過任何事情。如果您僅限於不可變數據和遞歸,嘗試在函數式語言中實現算法時,我確實迷失了方向。在Haskell中實現最長路徑算法

我一直在努力實現這個算法:http://www.geeksforgeeks.org/find-longest-path-directed-acyclic-graph/

My圖表構造是這樣的:

data = Graph w = Graph {vertices :: [(Char, w)], 
         edges :: [(Char, Char, w)]} deriving Show 

所以我有兩個頂點和邊權重,權重可以是任何數據類型。因此,在計算最長路徑時,我還需要執行兩個函數,即fg。然後,從頂點ab的最長路徑將是路徑中所有權重的f(w)g(w)之和。

我已經嘗試實現這個,但我總是發現自己試圖代碼的「勢在必行」的方式,它變得非常醜陋真快......

請點我在正確的方向。

weight_of_longest_path :: (Ord w) => Graph w -> Char -> Char 
              -> (w -> w) -> (w -> w) -> w 
weight_of_longest_path (Graph v w) startVert endVert f g = 
    let 
    topSort = dropWhile (/= startVert) $ topological_ordering (Graph v w) 
    distList = zip topSort $ 
       (snd $ head $ filter (\(a,b) -> a == startVert) v) 
       : (repeat (-999999999)) 
    finalList = getFinalList (Graph v w) topSort distList f g 
    in 
    snd $ head $ filter (\(a,b) -> b == endVert) finalList 

getFinalList :: (Ord w) => Graph w -> [Char] -> [(Char, w)] 
            -> (w -> w) -> (w -> w) -> [(Char, w)] 
getFinalList _ [] finalList _ _ = finalList 
getFinalList (Graph v w) (firstVert:rest) distList f g = 
    let 
    neighbours = secondNodes $ filter (\(a,b,w) -> a == firstVert) w 
    finalList = updateList firstVert neighbours distList (Graph v w) f g 
    in 
    getFinalList (Graph v w) rest finalList f g 

updateList :: (Ord w) => Char -> [Char] -> [(Char, w)] -> Graph w 
           -> (w -> w) -> (w -> w) -> [(Char, w)] 
updateList _ [] updatedList _ _ _ = updatedList 
updateList firstVert (neighbour:rest) distList (Graph vertices weights) f g = 
    let 
    edgeWeight = selectThird $ head 
      $ filter (\(a,b,w) -> a == firstVert && b == neighbour) weights 
    verticeWeight = snd $ head 
      $ filter (\(a,b) -> a == neighbour) vertices 
    newDist = calcDist firstVert neighbour verticeWeight edgeWeight 
         distList f g 
    updatedList = replace distList neighbour newDist 
    in 
    updateList firstVert rest updatedList (Graph vertices weights) f g 


calcDist :: (Ord w) => Char -> Char -> w -> w -> [(Char, w)] 
          -> (w -> w) -> (w -> w) -> w 
calcDist firstVert neighbour verticeWeight edgeWeight distList f g = 
    if (compareTo f g 
     (snd $ head $ filter (\(a,b) -> a == neighbour) distList) 
     (snd $ head $ filter (\(a,b) -> a == firstVert) distList) 
     edgeWeight verticeWeight) == True 
    then 
    (f (snd $ head $ filter (\(a,b) -> a == firstVert) distList)) 
    + (g edgeWeight) + (f verticeWeight) 
    else 
    (f (snd $ head $ filter (\(a,b) -> a == neighbour) distList)) 

replace :: [(Char, w)] -> Char -> w -> [(Char, w)] 
replace distList vertice value = 
    map (\[email protected](f, _) -> if f == vertice then (vertice, value) else p) 
     distList 

正如你可以看到它是這樣一個簡單的算法很多亂七八糟的代碼,我敢肯定,它在一個更清潔的方式是可行的。

+0

好吧,如果你使用'Data.Map'而不是你的頂點和邊的列表,那麼這段代碼可以被簡化(並且變得更有效率),這就是你的意思。 – Cubic

+0

記事本+ ++爲您提到的geeks網站上的C++實現(不包括'main()')計算2484個字符,併爲您的1697個字符計算:) –

+0

是的,但感覺就像我沒有使用函數式編程的力量,將distList從函數發送到函數並嘗試更新它...而某些函數只是以某種方式感覺像迭代器,這有點糟糕。 – hboy

回答

2

這是一種採用更「功能性」思維方式的方法。它圍繞着兩個功能:

longestPath :: Graph -> Node -> Node -> [Edge] 
pathCost :: Graph -> [Edges] -> Int 

longestPath返回路徑的最長路徑的邊緣的列表。 pathCost返回路徑的成本。

longestPath的定義是這樣的:(從Data.OrdmaximumBy來自Data.Listcomparing

longestPath g start end 
    | start == end = [] 
    | otherwise = 
    maximumBy (comparing (pathCost g)) 
       [ e : path | e <- edges of the node start 
          let start' = the other vertex of e, 
          let g' = graph g with node start deleted, 
          let path = longestPath g' start' end ] 

注:邊緣將以相反的順序生成。

有大量的實現細節需要弄清楚,特別是,如果沒有從startnode(當開始刪除時可能會發生這種情況,您將不得不稍微修改以處理這種情況節點),但這是我開始的方法。

+0

非常感謝,看起來像我可以使用的東西。如果我們將(pathCost g)作爲第一個參數傳遞給第一個參數,那麼這是什麼意思? maximumBy? – hboy

+0

'Ordering'只是一個具有三個值的數據類型:'LT','EQ'和'GT'。函數'a - > a - > Ordering'需要兩個a並返回其中的一個值 - 即它是一個比較函數。 'maximumBy'結合'比較'選擇列表中的「最大」元素,其中「最大」由傳遞給「比較」的函數確定。 – ErikR

+0

我明白了,再次感謝! – hboy