在给定目的地的(DAG)有向无环图中寻找路径

时间:2016-04-12 22:58:03

标签: f# depth-first-search directed-acyclic-graphs

我们说我有这个阵列:

let reportStructure = [|(2, 1); (3, 2); (4, 2); (5, 3); (6, 4); (7, 3)|]

元组中的第一个int向第二个int报告。

我可以用

轻松映射
let orgMap = Map.ofArray reporting

从那里,我可以很容易地得到所有使用

报告到2的整数列表
orgMap 
|> Map.filter (fun _ key -> key = 2)

返回

map [(3, 2); (4, 2)]

然而,我真正希望看到的是整个结构,从2开始。例如,我想找到一种可以为我提供样本输出的方法

map [(3, 2); (4, 2); (5, 3); (6, 4); (7, 3)]

如果我正在找人2或

map [(5, 3); (7, 3)]

如果我对第3个人感兴趣。

我可以这样做吗?如果是这样,怎么样?除了map之外还有其他结构可以更好地实现这一目标吗?

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

我假设你想得到一对带有"数字"的一对整数的列表。直接或间接向某些" root"报告。 这是一个简单但效率低下的解决方案:

let reportStructure = [|(2, 1); (3, 2); (4, 2); (5, 3); (6, 4); (7, 3)|]

let reportStructureSet = 
    reportStructure |> Set.ofArray

let reportingDirectlyTo root raportsToSet = 
    raportsToSet 
    |> Set.filter(fun (_, key) -> key = root) 

let addNextGeneration previousIteration raportsToSet = 
    let numbersLowerInHierarchy = previousIteration |> Set.map fst
    raportsToSet |> Set.filter(
        // select only those elements from raportsToSet...
        fun (num, supervisor) -> 
            // ...which either are already in previousIteration 
            (Set.contains (num, supervisor) previousIteration) || 
            // ...or are "below" someone from previousIteration
            (Set.contains supervisor numbersLowerInHierarchy))

let reportingDirectlyOrIndirectlyTo root raportsToSet = 
    // applies addNextGeneration until is "stabilizes" on some value
    let rec fixPointHelper previousIteration = 
        let nextIteration = addNextGeneration previousIteration raportsToSet
        if nextIteration = previousIteration
            then nextIteration
            else fixPointHelper nextIteration

    // set of numbers directly reporting to root
    let reportsDirectly = reportingDirectlyTo root raportsToSet
    // start "iteration" using numbers directly reporting to root
    fixPointHelper reportsDirectly

let reportingDirectlyOrIndirectlyToList root raportsToSet =
    reportingDirectlyOrIndirectlyTo root raportsToSet
    |> Set.toList

如果您想实施有效的解决方案,则应以下列方式将reportStructureSet解释为图表:

  • int是顶点
  • 一对int s是有向边

然后只需检查哪些边缘可以从" root"使用DFS

答案 1 :(得分:1)

由于OCaml接近F#并尝试在F#中找到拓扑排序,因此我没有找到任何有用的东西,而是查找了OCaml代码。

我发现An Introduction to Objective Caml使用深度优先搜索解决了您的问题并将其用作此答案的基础。另外,因为您是F#的新手,您可以查看该文档并查看代码的派生方式。奇怪的是,在发布这篇文章之后,我看了一下文档的其余部分,他在文档中有一个更高级的DFS版本。

您的输入是一个数组[| |],但您的答案是一个列表[],因此我将大部分工作作为列表。

答案的顺序与您的顺序不同,但它们的格式相同。

    let reportStructure = [|(2, 1); (3, 2); (4, 2); (5, 3); (6, 4); (7, 3)|]

    //
    //  6 -> 4 -> 2
    //  5 -> 3 -> 2 -> 1 
    //  7 -> 3

    // val revStructure : tl:('a * 'b) list -> ('b * 'a) list
    let revStructure tl = List.map (fun (a,b) -> (b,a)) tl

    // val mem : item:'a -> list:'a list -> bool when 'a : equality
    let mem item list = List.exists (fun x -> x = item) list 

    // val successors : n:'a -> edges:('a * 'b) list -> 'b list when 'a : equality
    let successors n edges = 
        let matching (s,_) = s = n
        List.map snd (List.filter matching edges)

    // val dist : pred:'a -> succs:'b list -> ('a * 'b) list
    let dist pred succs = List.map (fun y -> (pred,y)) succs

    // val dfsPairs : edges:('a * 'a) list -> start:'a -> ('a * 'a) list when 'a : equality
    let dfsPairs edges start =
        let rec dfsPairsInner edges visited start result = 
            match start with 
            | [] -> List.rev (revStructure result) 
            | n::nodes -> 
                if mem n visited then 
                    dfsPairsInner edges visited nodes result
                else 
                    let predecessors = dist n (successors n edges)
                    let result =
                        match predecessors with
                        | [] -> result
                        | _ -> predecessors @ result
                    dfsPairsInner edges (n::visited) ((successors n edges) @ nodes) result
        dfsPairsInner edges [] [start] []

    let revEdges = revStructure (List.ofArray reportStructure)

    let result = dfsPairs revEdges 2
    // val result : (int * int) list = [(4, 2); (3, 2); (7, 3); (5, 3); (6, 4)]

    let result = dfsPairs revEdges 3
    // val result : (int * int) list = [(7, 3); (5, 3)]

答案 2 :(得分:0)

我喜欢f#谜题,所以我对这个捅了一下。我希望你喜欢。

let orgList = [(2, 1); (3, 2); (4, 2); (5, 3); (6, 4); (7, 3)]

let orgMap =
    orgList
    |> List.fold (fun acc item -> 
        let key = snd item
        match Map.tryFind key acc with 
        | Some(value) -> 
            let map' = Map.remove key acc
            Map.add(key) (item::value) map'
        | None -> 
            Map.add(key) (item::[]) acc
        ) Map.empty<int, (int*int) list> 

let findReports supervisor = 
    let rec findReports' acc collection = 
        match collection with 
        | head::tail -> 
            (findReports' (head::acc) tail) 
            @   match Map.tryFind (fst head) orgMap with
                | Some(value) -> (findReports' [] value)
                | None -> []
        | [] -> acc    
    findReports' [] (Map.find supervisor orgMap)    

findReports 2
|> List.map fst
|> List.distinct

返回

val it : int list = [3; 4; 5; 7; 6]

findReports 2返回

val it : (int * int) list = [(3, 2); (4, 2); (5, 3); (7, 3); (6, 4)]

我打破它以澄清。

let orgList = [ (1, 2); (1, 3); (1, 4); (2, 5); (3, 6); (4, 5); (5, 6); (5, 7) ]

我们将您的元组列表创建为((报告,老板)列表的老板功能映射)。这可称为邻接列表,用于遍历图形。

let orgMap =
    orgList
    |> List.fold (fun acc item -> 
        let key = snd item
        match Map.tryFind key acc with 

如果老板下有报告列表,请添加到该列表中。

        | Some(reports) -> 
            let map' = Map.remove key acc
            Map.add(key) (item::reports) map'

否则,添加到空列表并插入字典。

        | None -> 
            Map.add(key) (item::[]) acc

以空地图作为累加器开始。

        ) Map.empty<int, (int*int) list> 

通过项目递归查找所有报告。

let findReports supervisor = 
    let rec findReports' acc collection = 
        match collection with 

如果有项目,请将其附加到累加器。这是BFS。如果在连接运算符(@)之前和之后切换表达式,它将变为DFS。

        | head::tail -> 
            (findReports' (head::acc) tail) 

将当前列表连接到报告的递归报告列表。

            @   match Map.tryFind (fst head) orgMap with
                | Some(value) -> (findReports' [] value)
                | None -> []

如果在列表的末尾,则返回列表。

        | [] -> acc    

运行递归函数。

    findReports' [] (Map.find supervisor orgMap)    

运行该功能。

findReports 7

仅返回报告

|> List.map fst

不要两次报告报告。

|> List.distinct