【问题标题】:Finding the longest path through a DFS Forest in Haskell在 Haskell 中找到通过 DFS 森林的最长路径
【发布时间】:2016-11-17 05:22:32
【问题描述】:

我有一个图表,我正在尝试找到最长的路径,但我不确定如何去做。我正在使用来自Data.Graph 的标准GraphEdge 类型,并且该图是使用buildG 函数生成的。我最初的想法是使用dfs 执行深度优先搜索,但这产生了一个我不确定如何操作的Forest 对象。

如果它有帮助(很抱歉我不能很好地打印这个,showForest 显然只打印字符串),这是我图表上dfs 命令的输出:

[Node {rootLabel = 87, subForest = [Node {rootLabel = 82, subForest = [Node {rootLabel = 70, subForest = []},Node {rootLabel = 83, subForest = [Node {rootLabel = 66, subForest = [Node {rootLabel = 72, subForest = [Node {rootLabel = 88, subForest = []}]}]},Node {rootLabel = 79, subForest = [Node {rootLabel = 69, subForest = [Node {rootLabel = 85, subForest = []},Node {rootLabel = 84, subForest = [Node {rootLabel = 86, subForest = []},Node {rootLabel = 73, subForest = [Node {rootLabel = 81, subForest = []}]}]}]}]}]},Node {rootLabel = 89, subForest = []}]}]}]

我发现了一些不同的函数来查找通过树的最长路径,例如在这个 answer 中,但它们仅适用于 Trees,而不适用于 Forests,我不确定它是否甚至可以在两者之间转换。

谢谢!

【问题讨论】:

  • 你能详细说明一下最长路径图吗?你的意思是所有顶点对中距离最长的2个顶点
  • @Shersh 之类的;从根开始,我需要距离根最远的顶点,以及到达它所需的节点路线。
  • "很抱歉我不能很好地打印这个,showForest 显然只打印字符串" -- 如果你的意思是drawForest,你可以使用drawForest . fmap (fmap show)
  • “但它们只适用于树木,而不适用于森林,我不确定是否可以在两者之间进行转换。” -- Forest 只是Trees 的列表,所以如果你有一个与Trees 一起使用的函数,你至少可以简单地将fmap 放在Forest 之上。

标签: haskell graph tree


【解决方案1】:

正如 Shersh 在他们的回答中解释的那样,深度优先搜索不足以解决您的问题(如果是,您可以使用您链接到的答案中的 generalFold,例如,重建每个路径中的最长路径森林之树)。一种替代方法是从 Data.Graph 切换到 fgl,它提供了各种各样的图算法,包括广度优先搜索。请注意,FGL 的The Haddock documentation 相当简洁,因此您还需要通过package homepage 查阅有用的用户指南。在下面的演示中,我将使用bft 函数来获取图的广度优先生成树,这应该足以让您入门:

GHCi> import Data.Graph.Inductive.Graph
GHCi> import Data.Graph.Inductive.PatriciaTree 
GHCi> import Data.Graph.Inductive.Query.BFS
GHCi> :{
GHCi| test :: UGr
GHCi| test = mkUGraph [1..7] [(1,3),(1,2),(2,4),(3,5),(2,6),(5,2),(5,7)]
GHCi| :}
GHCi> prettyPrint test
1:()->[((),2),((),3)]
2:()->[((),4),((),6)]
3:()->[((),5)]
4:()->[]
5:()->[((),2),((),7)]
6:()->[]
7:()->[]
GHCi> bft 1 test
[[1],[2,1],[3,1],[4,2,1],[6,2,1],[5,3,1],[7,5,3,1]]

生成树本质上是从一个节点到所有可达节点的路径列表。例如,如果您只想找到最大长度的路径之一而不关心平局,那么您只需要:

GHCi> import Data.Ord
GHCi> import Data.List
GHCi> maximumBy (comparing length) (bft 1 test)
[7,5,3,1]

如果您确实关心平局,则需要对groupBy 之类的东西进行一些杂耍,但这不会从根本上变得更加困难。

【讨论】:

    【解决方案2】:

    我将首先解释dfs 的作用。正如您在 cmets 中提到的,您使用 buildG 函数,所以我也会在我的回答中使用它。

    dfs 有 2 个参数:图和根节点列表。然后dfs 从列表中的每个顶点编号开始运行深度优先搜索,返回列表中这些节点的可到达顶点的Tree。例如:

    1. 您有 1 个顶点的图,并且您想从 1 中查找路径。然后您将得到一个列表,其中只有一个 Tree,其中根标签为 1,子林为空。
    2. 您有 2 个顶点和从 1 到 2 的边的图,并且您希望从 1 中查找路径。然后您将获得只有一个 Tree 的列表,其中根标签为 1,子林是列表,其中 2 作为根标签.
    3. 您有 3 个顶点和边 (1,2) 和 (1,3) 的图,并且您希望从 1 中查找路径。然后您将获得只有一个 Tree 的列表,其中根标签为 1 和子林是一个有两棵树的列表,分别以 2 和 3 作为根标签。

    所有这些示例(甚至更多)都将在下一个 ghci 会话中演示:

    λ: let g = buildG (1,1) []
    λ: dfs g [1]
    [Node {rootLabel = 1, subForest = []}]
    λ: dfs g [2]
    *** Exception: Ix{Int}.index: Index (2) out of range ((1,1))
    λ: let g = buildG (1,2) [(1,2)]
    λ: dfs g [1]
    [Node {rootLabel = 1, subForest = [Node {rootLabel = 2, subForest = []}]}]
    λ: dfs g [2]
    [Node {rootLabel = 2, subForest = []}]
    λ: let g = buildG (1,3) [(1,2), (1,3)]
    λ: dfs g [1]
    [Node {rootLabel = 1, subForest = [Node {rootLabel = 3, subForest = []},Node {rootLabel = 2, subForest = []}]}]
    

    但是,不幸的是,dfs 无法帮助您完成任务:找到距给定顶点最远的顶点。在算法的工作方式中,它只是无法解决此类问题。假设你有 4 个顶点和边的图:

    1 → 2, 2 → 3, 3 → 4, 1 → 4

    从 1 开始有最长路径的顶点是 3(这条路径有 2 条边)。但是dfs 会根据您指定的边的顺序返回不同的结果。

    λ: let g = buildG (1,4) [(1,2), (1,4), (2,3), (3,4)]
    λ: dfs g [1]
    [Node {rootLabel = 1, subForest = [Node {rootLabel = 4, subForest = []},Node {rootLabel = 2, subForest = [Node {rootLabel = 3, subForest = []}]}]}]
    λ: let g = buildG (1,4) [(1,4), (1,2), (2,3), (3,4)]
    λ: dfs g [1]
    [Node {rootLabel = 1, subForest = [Node {rootLabel = 2, subForest = [Node {rootLabel = 3, subForest = [Node {rootLabel = 4, subForest = []}]}]}]}]
    

    这不是具体实现的问题。这就是dfs 的工作原理。在一般情况下,您不能使用dfs 来解决您的特定问题。此外,仅通过重新排列列表中的边缘无法解决此问题,因为您通常不知道如何重新排列它们。

    您真正需要使用的是bfs 算法——广度优先搜索。不幸的是,它没有在Data.Graph 库中实现。所以你需要从零开始实现整个算法,或者在某个地方找到实现(这里有一些讨论:Traversing a graph breadth first, marking visited nodes in Haskell)。

    【讨论】:

    • 谢谢!我做了一个天真的(和错误的)假设,因为我正在处理分支的深度,我需要深度优先搜索而不是广度优先。我现在使用了广度优先算法(duplode 的答案中的 bft)并且它正在工作。
    【解决方案3】:

    需要注意的是图中的最长路径与最长路径不一样 在生成树中。图可以有许多生成树,而这些树没有 具有相同的高度。 duplode 提供的 btf 代码正确地找到了生成树中的最长路径。

    例如,duplode answer中的图形 mkUGraph [1..7] [(1,3),(1,2),(2,4),(3,5),(2,6),(5,2),(5,7)] 有一条比建议的[1,3,5,7] 更长的路径,该路径是[1,3,5,2,6]

    一个更简单的例子 mkUGraph [1,2,3] [(1,2),(1,3),(2,3)] 使用 FGL btf 1 将产生生成树 [[1], [3,1], [2,1]],最长的路径是 [1,2,3](它本身就是一棵生成树)。

    一般来说,找到最长路径并非易事(实际上是 NP-hard),但对于 DAG,可以使用拓扑排序节点的相当简单的折叠来完成。

    https://github.com/rpeszek/graph-exercise

    【讨论】:

      猜你喜欢
      • 2016-07-31
      • 1970-01-01
      • 2022-11-11
      • 2015-06-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      相关资源
      最近更新 更多