【问题标题】:What is an efficient way of traversing a graph with bfs using map reduce?使用 map reduce 遍历带有 bfs 的图的有效方法是什么?
【发布时间】:2018-12-23 02:26:11
【问题描述】:

这是一个招聘人员问我的面试问题,问题基本上是计算所有节点到每个节点的最短路径,我的解决方案如下

启动所有可能的边(没有反向 A - B 与 B-A 相同)

每个节点将在下面表示 (src, cost, current_list, dest) ,src 和 dest 基本上是我们之前启动的所有可能的边

地图:

for each edge you traverse, you duplicate your tuple and add the current   
traversed node to the cost and current list. 
if the node is the destination you annotate finish, if the the node is 
in the current list, you annotate delete

减少:

Don't really need to do anything besides outputting finish and deleting 
delete and let the other node go through the next round of map
And by outputting I mean for each src, dest pair only output the least cost

招聘人员说这效率不高,我可以看到这效率不高,因为您正在组合遍历,但我能想到的唯一选择是如果您有 n 个节点,然后生成 n 个服务器并为每个节点执行 dijkstra招聘人员说的也是错误的。有人可以帮我解决这个问题吗?

编辑:

例如。三角图

边是 A-B、B-C、C-A,路径成本为 1

算法

  1. 首先我们启动所有可能的源目标对,记住边缘的反转不是唯一的 A-B、A-C、B-C(省略B-A、C-A、B-C)

对于每个源目标对,我们有以下元组

(src=A, cost=None, current_list=A, dest=B, annotate=continue)
(src=A, cost=None, current_list=A, dest=C, annotate=continue)
(src=B, cost=None, current_list=B, dest=C, annotate=continue)
  1. 现在我们开始 map reduce 算法

    for each tuple in the tuple list we initiate:
    
        for each neighbor of the node at the end of current_list
            if the next neighbor is already in the current_list
                set annotate = delete
            elif the next neighbor is the dest
                set annotate = finish
                add path cost to cost
            else
                duplicate the current node
                add neighbor to current_list
                add path cost to cost
            delete the current tuple
    

在我们的例子中

(src=A, cost=None, current_list=A, dest=B, annotate=continue)
 =>
(src=A, cost=1, current_list=AB, dest=B, annotate=finish)
(src=A, cost=1, current_list=AC, dest=B, annotate=continue)

(src=A, cost=None, current_list=A, dest=C, annotate=continue)
=>
(src=A, cost=1, current_list=AC, dest=C, annotate=finish)
(src=A, cost=1, current_list=AB, dest=C, annotate=continue)

(src=B, cost=None, current_list=B, dest=C, annotate=continue)
=>
(src=B, cost=1, current_list=BC, dest=C, annotate=finish)
(src=B, cost=1, current_list=BA, dest=C, annotate=continue)
  1. 减少

    注意:我们减少了 src、dest 对,并将其用作我们的 key 对于元组列表中的每个元组

    if annotate == finish
        keep trace of min cost and delete tuple for each src dest pair that is not the current min
        then pass the current min as result
    elif annotate == delete
        delete the tuple
    
    else
        pass down to the next round of map
    
  2. 地图

因为我们还有一些元组有 annotate = continue

(src=B, cost=1, current_list=BA, dest=C, annotate=continue)  
=>
(src=B, cost=2, current_list=BAC, dest=C, annotate=finish)  
(src=B, cost=2, current_list=BAB, dest=C, annotate=delete)  


(src=A, cost=1, current_list=AC, dest=B, annotate=continue)
=>
(src=A, cost=2, current_list=ACB, dest=B, annotate=finish)
(src=A, cost=2, current_list=ACA, dest=B, annotate=delete)

(src=A, cost=1, current_list=AB, dest=C, annotate=continue)
=>
(src=A, cost=2, current_list=ABC, dest=C, annotate=finish)
(src=A, cost=2, current_list=ABA, dest=C, annotate=delete)
  1. 减少

我们没有 continue tuples,现在我们只使用 reduce 来找到每个 src dest 对的最小值

【问题讨论】:

  • 你所描述的甚至没有正确解决 MapReduce 的单源最短路径问题,如果我是你的面试官,我首先不会担心效率,而是正确性。跨度>
  • 我不确定你的意思,reduce部分只输出成本最低的源节点目标节点对,并且我遍历从A到B的所有可能路径,其中A和B是任意的
  • 所以你不想要全对最短路径,而是两个顶点之间的最小成本边?
  • 我跟踪最短路径和最小成本边,因为我有一个当前列表变量,我传递,如果成本结果是最小成本,那么当前列表必须是成本方面的最短路径,您可以将当前列表作为链表或数组来跟踪遍历顺序
  • 如果我有 n 个节点,我计算了最短路径(如 A->B->C)和每个节点到 n - 1 个其他节点的最小成本(即从顶点到顶点)。我的算法没有计算最小生成树

标签: algorithm graph mapreduce graph-algorithm breadth-first-search


【解决方案1】:

Floyd-Warshall 的内部两个循环本质上是矩阵乘法,加法替换为 min,乘法替换为加法。您可以使用 map-reduce 进行矩阵乘法,因此您可以使用 |V| 实现 Floyd Warshall地图减少。

来自 Floyd-Warshall 的维基百科页面:

1 let dist be a |V| × |V| array of minimum distances initialized to ∞ (infinity)
2 for each vertex v
3    dist[v][v] ← 0
4 for each edge (u,v)
5    dist[u][v] ← w(u,v)  // the weight of the edge (u,v)
6 for k from 1 to |V|
7    for i from 1 to |V|
8       for j from 1 to |V|
9          if dist[i][j] > dist[i][k] + dist[k][j] 
10             dist[i][j] ← dist[i][k] + dist[k][j]
11         end if

内部的两个循环(ij,第 7 到 11 行)在结构上与矩阵乘法相同,您可以调整任何“map-reduce 上的矩阵乘法”解决方案来执行此操作。

外部 (k) 循环变为 |V|地图减少。

【讨论】:

  • 好主意,我昨天也在考虑 FloydWarshall。回到效率,你认为每个顶点运行一个作业是个好主意吗?:)
  • 可悲的是,一篇声称通过三个 mapreduce 执行来做到这一点的论文在付费墙后面ieeexplore.ieee.org/xpl/…
【解决方案2】:

我想提出以下方法 - 通过 map-reduce 在图中查找最短路径。

让我们从一个小例子开始,这将导致对算法进一步实现的直觉。

想象一下,关于图的信息以 邻接列表 的形式存储(带有有效负载,表示相应节点之间的路径)。例如:

A -> [ {B, "A-B"}, {C, "A-C"}, {D, "A-D"} ]

从给定的示例中 - 我们可以“推断”关于图中以下连接的信息:

1) 直接连接

  • A -> B(路径:"A-B"
  • A -> C(路径:"A-C"
  • A -> D(路径:"A-D"

2) 通过节点A的传递连接

  • B -> C(路径:"B-A-C"

    (其中path("B -> C") == reverse(path("A -> B")) + path("A -> C")

  • C -> B(路径:"C-A-B"
  • C -> D(路径:"C-A-D"
  • D -> C(路径:"D-A-C"
  • D -> B(路径:"D-A-B"
  • B -> D(路径:"B-A-D"

换句话说:我们只是将邻接列表的一个条目“映射” - 到多对可相互访问的节点(对于所有生成的对 - 存在路径)。

每一对节点,实际上代表连接:Source -> Target

所以,现在,我们可以组合所有具有相同源节点的对:

Source -> [{Target 1, "Path-to-Target-1"}, {Target 2, "Path-to-target-2"}, ...]

实际上,在组合之后——每个源都会关联一个目标节点列表:列表可能包含重复的目标节点(重复的目标节点,只是对应不同的可能路径)。

所以,我们只需要从目标节点列表中删除重复项(只保留对应于最短路径的目标节点)。

上面的两段 - 实际上描述了 reduce 步骤。 reduce 步骤的输出 - 与 ma​​p 步骤的输入相同。

所以,最后 - 重复这些 map-reduce 步骤直到收敛。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多