gpt4 book ai didi

erlang - Erlang 中的 Dijkstra 算法使用什么数据结构?

转载 作者:行者123 更新时间:2023-12-04 16:01:22 27 4
gpt4 key购买 nike

免责声明:作者是Erlang的新手。

想象一下,我们有一个由 1M 个节点组成的图,每个节点有 0-4 个邻居(边从每个节点发出到这些邻居,因此该图是有向和连通的)。

这是我选择的数据结构:

为了存储图形,我使用基于 ETS 表的有向图。这允许快速(O(1))访问节点的邻居。

对于未访问的节点列表,我使用gb_sets:take_smallest(节点已经排序,获取后同时删除)。

对于前辈列表,我使用 dict 结构,它允许按以下方式存储前辈:{Node1,Node1_predecessor},{Node2,Node2_predecessor}。

对于访问过的节点列表,我使用了一个简单的列表。

问题:

  • 当我尝试更新有向图结构和 Unvisited_nodes 结构中节点的权重时,代码变得非常难以阅读和维护。将一个“对象”与需要在两个数据结构中同时更新的“字段”保持在一起似乎不是正确的方法。这样做的正确方法是什么?
  • 同样的问题是关于前辈列表。我应该在哪里存储节点“对象”的前身“字段”?也许在图(有向图结构)中?
  • 也许我应该根据进程和消息而不是对象(节点和边)及其字段(权重)重新考虑整个 Dijkstra 算法?

  • 更新:

    这是基于 Antonakos 建议的代码:
    dijkstra(Graph,Start_node_name) ->

    io:format("dijkstra/2: start~n"),

    Paths = dict:new(),
    io:format("dijkstra/2: initialized empty Paths~n"),

    Unvisited = gb_sets:new(),
    io:format("dijkstra/2: initialized empty Unvisited nodes priority queue~n"),

    Unvisited_nodes = gb_sets:insert({0,Start_node_name,root},Unvisited),
    io:format("dijkstra/2: Added start node ~w with the weight 0 to the Unvisited nodes: ~w~n", [Start_node_name, Unvisited_nodes]),

    Paths_updated = loop_through_nodes(Graph,Paths,Unvisited_nodes),
    io:format("dijkstra/2: Finished searching for shortest paths: ~w~n", [Paths_updated]).




    loop_through_nodes(Graph,Paths,Unvisited_nodes) ->
    %% We need this condition to stop looping through the Unvisited nodes if it is empty
    case gb_sets:is_empty(Unvisited_nodes) of
    false ->
    {{Current_weight,Current_name,Previous_node}, Unvisited_nodes_updated} = gb_sets:take_smallest(Unvisited_nodes),
    case dict:is_key(Current_name,Paths) of
    false ->
    io:format("loop_through_nodes: Found a new smallest unvisited node ~w~n",[Current_name]),

    Paths_updated = dict:store(Current_name,{Previous_node,Current_weight},Paths),
    io:format("loop_through_nodes: Updated Paths: ~w~n",[Paths_updated]),

    Out_edges = digraph:out_edges(Graph,Current_name),
    io:format("loop_through_nodes: Ready to iterate through the out edges of node ~w: ~w~n",[Current_name,Out_edges]),

    Unvisited_nodes_updated_2 = loop_through_edges(Graph,Out_edges,Paths_updated,Unvisited_nodes_updated,Current_weight),
    io:format("loop_through_nodes: Looped through out edges of the node ~w and updated Unvisited nodes: ~w~n",[Current_name,Unvisited_nodes_updated_2]),

    loop_through_nodes(Graph,Paths_updated,Unvisited_nodes_updated_2);
    true ->
    loop_through_nodes(Graph,Paths,Unvisited_nodes_updated)
    end;
    true ->
    Paths
    end.

    loop_through_edges(Graph,[],Paths,Unvisited_nodes,Current_weight) ->
    io:format("loop_through_edges: No more out edges ~n"),
    Unvisited_nodes;

    loop_through_edges(Graph,Edges,Paths,Unvisited_nodes,Current_weight) ->
    io:format("loop_through_edges: Start ~n"),
    [Current_edge|Rest_edges] = Edges,
    {Current_edge,Current_node,Neighbour_node,Edge_weight} = digraph:edge(Graph,Current_edge),
    case dict:is_key(Neighbour_node,Paths) of
    false ->
    io:format("loop_through_edges: Inserting new neighbour node ~w into Unvisited nodes~n",[Current_node]),
    Unvisited_nodes_updated = gb_sets:insert({Current_weight+Edge_weight,Neighbour_node,Current_node},Unvisited_nodes),
    io:format("loop_through_edges: The unvisited nodes are: ~w~n",[Unvisited_nodes_updated]),
    loop_through_edges(Graph,Rest_edges,Paths,Unvisited_nodes_updated,Current_weight);
    true ->
    loop_through_edges(Graph,Rest_edges,Paths,Unvisited_nodes,Current_weight)
    end.

    最佳答案

    您选择的数据结构看起来不错,因此主要是在其中插入什么以及如何使它们保持最新的问题。我建议如下(我稍微更改了名称):

  • Result : A dict映射 Node{Cost, Prev} ,其中 Cost是到 Node 的路径的总成本和 Prev是它在路径上的前身。
  • Open : A gb_sets {Cost, Node, Prev}的结构.
  • 具有以下形式的边的图 {EdgeCost, NextNode} .

  • 搜索结果用 Result表示并且图表根本没有更新。没有多处理或消息传递。

    算法如下:
  • 插入 {0, StartNode, Nil}Open ,其中 Nil是标志着道路尽头的东西。
  • {{Cost, Node, Prev}, Open1} = gb_sets:take_smallest(Open) .如 Node已经在 Result然后什么都不做;否则添加 {Cost, Node, Prev}Result ,对于每条边 {EdgeCost, NextNode}Node添加 {Cost + EdgeCost, NextNode, Node}Open1 ,但前提是 NextNode不在 Result .继续 Open1直到集合为空。

  • Dijkstra 算法要求 decrease_key() Open上的操作放。由于 gb_sets 不容易支持这一点我们已经使用了为 NextNode 插入元组的解决方法即使 NextNode可能存在于 Open已经。这就是我们检查节点是否从 Open 中提取的原因。已经在 Result .

    使用优先级队列的扩展讨论

    有几种方法可以通过 Dijkstra 算法使用优先队列。

    standard version of Wikipedia一个节点 v只插入一次,但位置是 v当成本和前身 v 更新时被改变。
    alt := dist[u] + dist_between(u, v)
    if alt < dist[v]:
    dist[v] := alt
    previous[v] := u
    decrease-key v in Q

    实现通常通过替换 decrease-key v in Q 来简化与 add v to Q .这意味着 v可以多次添加,因此算法必须检查 u从队列中提取的尚未添加到结果中。

    在我的版本中,我用 add v to Q 替换了上面的整个块.因此,队列将包含更多条目,但由于它们总是按顺序提取,因此不会影响算法的正确性。如果您不想要这些额外的条目,您可以使用字典来跟踪每个节点的最低成本。

    关于erlang - Erlang 中的 Dijkstra 算法使用什么数据结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6725268/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com