这是我招聘人员提出的一个面试问题,问题基本上是计算所有节点到每个节点的最短路径,我的解决方案如下
启动所有可能的边(没有反向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
算法
对于每个源目标对,我们有以下元组
(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)
现在我们启动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)
减少
注意:我们减少src,dest对,并将其用作我们的密钥 对于元组列表中的每个元组
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
地图
因为我们仍然有一些具有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)
我们没有继续元组,现在我们只使用reduce来查找每个src dest对的最小值
答案 0 :(得分:4)
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
内部两个循环(i
和j
,第7行到第11行)在结构上与矩阵乘法相同,并且您可以调整任何“map-reduce”矩阵乘法来执行此操作
外部(k
)循环变为| V |地图降低。
答案 1 :(得分:0)
我想提出以下方法 - 通过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步骤的输出 - 与 map 步骤的输入相同。
所以,最后 - 只需重复这些map-reduce步骤直到收敛。