我想帮助实现Haskell的最长路径算法。我只使用了Haskell大约两个星期,之前没有用函数式语言做过任何事情。当您仅限于不可变数据和递归时,我在尝试用函数式语言实现算法时真的迷失了。
我一直在尝试实施此算法:http://www.geeksforgeeks.org/find-longest-path-directed-acyclic-graph/
我的图表构造如下:
data = Graph w = Graph {vertices :: [(Char, w)],
edges :: [(Char, Char, w)]} deriving Show
所以我在顶点和边上都有权重,权重可以是任何数据类型。因此,在计算最长路径时,我还需要使用两个函数f
和g
。从顶点a
到b
的最长路径将是路径中所有权重的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 (\p@(f, _) -> if f == vertice then (vertice, value) else p)
distList
正如你所看到的,对于这样一个简单的算法来说,这是一个很乱的代码,而且我确信它可以用更干净的方式实现。
答案 0 :(得分:2)
这是一种采用更“功能”的思维方式的方法。它围绕两个功能:
longestPath :: Graph -> Node -> Node -> [Edge]
pathCost :: Graph -> [Edges] -> Int
longestPath
将路径作为最长路径的边缘列表返回。 pathCost
返回路径的费用。
longestPath
的定义是这样的:
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 ]
(maximumBy
来自Data.List
和来自comparing
的{{1}}
N.B。边缘将以相反的顺序生成。
有许多实现细节需要弄清楚,特别是,当没有从Data.Ord
到start
的路径时,您必须稍微修改它以处理这种情况(这可以在你开始删除节点时发生),但这是我开始的方法。