【问题标题】:Why does my implementation of Dijkstra's algorithm fail under this undefined case?为什么在这种未定义的情况下我的 Dijkstra 算法的实现会失败?
【发布时间】:2021-04-21 01:28:08
【问题描述】:

我正在尝试在 Prolog(特别是 GNU Prolog)中编写 Dijkstra 算法的实现。首先,我做了一个事实connection,它描述了下图的连接顶点。

现在看来我的实现适用于我大约一半的测试用例,即这些:

s, k
g, h
e, c

但是对于这些,

h, g (this one has an empty Tentative_Connections)
c, e (this one too)
i, j (this one finds the incorrect path of [i, k, j])

失败了。

我的程序基于算法wikipedia 页面上给出的描述。这是一些粗略的伪代码:

for a current node, find its connecting nodes
remove the ones that are visited
make a new list of nodes that factors in a tentative distance
delete the current node from the list of unvisited nodes
if the destination node is not in the unvisited list, the current built-up path is final
otherwise, recur on the node that is the closest regarding the tentative distance

但无论如何,我的代码总是失败(出现上述错误)。为了详细说明,这里是 h, g 案例的失败:

Unchartered = []
Tentative = []
Destination not reached
No tentative connections!
Closest = _1232
Closest = _1232
Next_Path = [h,b,s,c,l,i,k,j]
uncaught exception: error(instantiation_error,(is)/2)
| ?- 

似乎在某一时刻,没有暂定点可供搜索。我不知道从这里做什么,因为维基百科没有详细说明这个问题。有知道这个算法的人知道我可以做些什么来修复我的代码吗?

% https://www.youtube.com/watch?v=GazC3A4OQTE
% example graph
% cl; gprolog --consult-file dijkstra.pl --entry-goal main

connection(s, c, 3).
connection(c, l, 2).
connection(l, i, 4).
connection(l, j, 4).
connection(i, j, 6).
connection(i, k, 4).
connection(j, k, 4).
connection(k, e, 5).
connection(e, g, 2).
connection(g, h, 2).
connection(h, f, 3).
connection(f, d, 5).
connection(d, a, 4).
connection(b, d, 4).
connection(b, a, 3).
connection(b, s, 2).
connection(b, h, 1).
connection(a, s, 7).

get_vertices(_, Vertex_Count, Traversed, Traversed) :-
    length(Traversed, Vertex_Count).

get_vertices([], _, Traversed, Traversed).

get_vertices([Vertex | Vertices], Vertex_Count, Traversed, Result) :-
    get_vertices(Vertex, Vertex_Count, Traversed, More_Traversed),
    get_vertices(Vertices, Vertex_Count, More_Traversed, Result).

get_vertices(Vertex, _, Traversed, Traversed) :-
    atom(Vertex), member(Vertex, Traversed).

get_vertices(Vertex, Vertex_Count, Traversed, Result) :-
    atom(Vertex),
    findall(Connected, are_connected(Vertex, Connected, _), Vertices),
    get_vertices(Vertices, Vertex_Count, [Vertex | Traversed], Result).

are_connected(A, B, S) :- connection(A, B, S) ; connection(B, A, S).

keep_unvisited([], _, []).
keep_unvisited([Connection | Connections], Unvisited, Result) :-
    keep_unvisited(Connections, Unvisited, Tail_Result),
    [Connection_Name, _] = Connection,
    (member(Connection_Name, Unvisited) ->
        Result = [Connection | Tail_Result];
    Result = Tail_Result).

w_tentative_distances([], _, []).
w_tentative_distances([Connection | Connections], Node_Val, Result) :-
    w_tentative_distances(Connections, Node_Val, Tail_Result),
    [Connection_Name, Connection_Val] = Connection,

    Tentative_Distance is Connection_Val + Node_Val,
    (Connection_Val > Tentative_Distance ->
        New_Distance = Tentative_Distance; New_Distance = Connection_Val),

    Result = [[Connection_Name, New_Distance] | Tail_Result].

closest_node_([], Closest, Closest).
closest_node_([Node | Rest], Closest, Result) :-
    [_, Node_Val] = Node,
    [_, Closest_Val] = Closest,
    (Node_Val < Closest_Val ->
        closest_node_(Rest, Node, Result)
        ;
    closest_node_(Rest, Closest, Result)).

closest_node([Node | Rest], Result) :-
    closest_node_(Rest, Node, Result).

dijkstra([Node_Name, Node_Val], Unvisited, Dest_Node, Path, Final_Path) :-
    findall([Connected, Dist], are_connected(Node_Name, Connected, Dist), Connections),
    keep_unvisited(Connections, Unvisited, Unchartered_Connections),
    w_tentative_distances(Unchartered_Connections, Node_Val, Tentative_Connections),

    % trace,
    delete(Unvisited, Node_Name, New_Unvisited),
    format('Path = ~w\nNode_Name = ~w\nNode_Val = ~w\n', [Path, Node_Name, Node_Val]),
    format('Unvisited = ~w\nNew_Unvisited = ~w\n', [Unvisited, New_Unvisited]),
    % notrace,

    format('Connections = ~w\n', [Connections]),
    format('Unchartered = ~w\n', [Unchartered_Connections]),
    format('Tentative = ~w\n', [Tentative_Connections]),

    (member(Dest_Node, Unvisited) -> % destination has not been reached
        write('Destination not reached\n'),
        (closest_node(Tentative_Connections, Closest); write('No tentative connections!\n')),
        format('Closest = ~w\n', [Closest]),
        append(Path, [Node_Name], Next_Path),
        format('Closest = ~w\nNext_Path = ~w\n', [Closest, Next_Path]),
        dijkstra(Closest, New_Unvisited, Dest_Node, Next_Path, Final_Path);
    write('The end was reached!\n'),
    Final_Path = Path).

dijkstra_wrapper(Start, End, Vertex_Count, Path) :-
    get_vertices(Start, Vertex_Count, [], Unvisited),
    dijkstra([Start, 0], Unvisited, End, [], Path).

main :-
    dijkstra_wrapper(h, g, 13, Path),
    write(Path), nl.

非工作示例具有不断增长的未访问列表(对于某些递归情况)

Path = [h,b,s,c,l,i,k]
Node_Name = j
Node_Val = 4
Unvisited = [g,e,j,a,d,f]
New_Unvisited = [g,e,a,d,f]
Connections = [[k,4],[l,4],[i,6]]
Unchartered = []
Tentative = []
Destination not reached

如果没有路可走怎么办?

【问题讨论】:

  • 让变量像w_tentative_distances([], _, []). 一样打开是糟糕的一天的秘诀。从逻辑上讲,这意味着w_tentative_distances([], apple_pie, []) 是一个真实的陈述,它以某种方式与图中的路径相关。但是我没有看到您在程序的其他地方引用苹果派...但是,您确实将其称为w_tentative_distances(Connections, Node_Val, Tail_Result),然后尝试使用Node_Val 作为数字,这绝对不是上述@987654335 @ 是 []keep_unvisited([], _, []). 的类似问题
  • 但实际上你应该先修复get_vertices/4get_vertices(h, 13, [], Unvisited) 真的应该有这么多冗余的解决方案吗?它真的应该有[b,b,g,e,k,j,i,l,c,s,a,d,f,h](注意双b)作为解决方案吗?您真的想要 one 谓词同时接受顶点和顶点列表作为其第一个参数吗?这种可能的类型混淆是导致意外失败和不终止的非常好的方法。

标签: algorithm optimization prolog graph-algorithm dijkstra


【解决方案1】:

另一个。

它使用library(assoc) 关联列表来存储与每个顶点关联的路径。这避免了必须对列表扫描进行编码。 “下一个要访问的顶点”列表仍然是一个普通列表,但是我们需要在每次访问后消除重复的顶点并保留那些距离最小的顶点,然后按顶点距离对列表进行排序,以便下一个要访问的顶点be visitited 位于首位。

(SWI-Prolog 有一个内置的关联数据结构,SWI-Prolog dict,我在这里没有使用它。library(assoc) 是否存在于 GNU Prolog 中?它应该...)


?- main.

VisitThese is currently: [0-a] 
Neighbors of vertex a : [s-7,d-4,b-3] 
Dirty visitations : [7-s,4-d,3-b], Clean visitations : [3-b,4-d,7-s]
VisitThese is currently: [3-b,4-d,7-s] 
Neighbors of vertex b : [d-4,a-3,s-2,h-1] 
Dirty visitations : [5-s,4-h,4-d,7-s], Clean visitations : [4-d,4-h,5-s]
VisitThese is currently: [4-d,4-h,5-s] 
Neighbors of vertex d : [a-4,f-5,b-4] 
Dirty visitations : [9-f,4-h,5-s], Clean visitations : [4-h,5-s,9-f]
VisitThese is currently: [4-h,5-s,9-f] 
Neighbors of vertex h : [f-3,g-2,b-1] 
Dirty visitations : [7-f,6-g,5-s,9-f], Clean visitations : [5-s,6-g,7-f]
VisitThese is currently: [5-s,6-g,7-f] 
Neighbors of vertex s : [c-3,b-2,a-7] 
Dirty visitations : [8-c,6-g,7-f], Clean visitations : [6-g,7-f,8-c]
VisitThese is currently: [6-g,7-f,8-c] 
Neighbors of vertex g : [h-2,e-2] 
Dirty visitations : [8-e,7-f,8-c], Clean visitations : [7-f,8-c,8-e]
VisitThese is currently: [7-f,8-c,8-e] 
Neighbors of vertex f : [d-5,h-3] 
Dirty visitations : [8-c,8-e], Clean visitations : [8-c,8-e]
VisitThese is currently: [8-c,8-e] 
Neighbors of vertex c : [l-2,s-3] 
Dirty visitations : [10-l,8-e], Clean visitations : [8-e,10-l]
VisitThese is currently: [8-e,10-l] 
Neighbors of vertex e : [g-2,k-5] 
Dirty visitations : [13-k,10-l], Clean visitations : [10-l,13-k]
VisitThese is currently: [10-l,13-k] 
Neighbors of vertex l : [i-4,j-4,c-2] 
Dirty visitations : [14-i,14-j,13-k], Clean visitations : [13-k,14-i,14-j]
VisitThese is currently: [13-k,14-i,14-j] 
Neighbors of vertex k : [e-5,i-4,j-4] 
Dirty visitations : [14-i,14-j], Clean visitations : [14-i,14-j]
VisitThese is currently: [14-i,14-j] 
Neighbors of vertex i : [j-6,k-4,l-4] 
Dirty visitations : [14-j], Clean visitations : [14-j]
VisitThese is currently: [14-j] 
Neighbors of vertex j : [k-4,l-4,i-6] 
Dirty visitations : [], Clean visitations : []

Vertex a is at distance 0 via [a-0]
Vertex b is at distance 3 via [a-0,b-3]
Vertex c is at distance 8 via [a-0,b-3,s-5,c-8]
Vertex d is at distance 4 via [a-0,d-4]
Vertex e is at distance 8 via [a-0,b-3,h-4,g-6,e-8]
Vertex f is at distance 7 via [a-0,b-3,h-4,f-7]
Vertex g is at distance 6 via [a-0,b-3,h-4,g-6]
Vertex h is at distance 4 via [a-0,b-3,h-4]
Vertex i is at distance 14 via [a-0,b-3,s-5,c-8,l-10,i-14]
Vertex j is at distance 14 via [a-0,b-3,s-5,c-8,l-10,j-14]
Vertex k is at distance 13 via [a-0,b-3,h-4,g-6,e-8,k-13]
Vertex l is at distance 10 via [a-0,b-3,s-5,c-8,l-10]
Vertex s is at distance 5 via [a-0,b-3,s-5]

计算公式:

% =========
% Graph definition
% =========

% ---------
% "Asymmetric connection relation"
% ---------

% "Connection relation" between vertices. Each edge is labeled with
% a "distance" (cost)
%
% connection(?VertexName1,?VertexName2,?Cost).
%
% This also indirectly defines the set of vertices which are simply
% given by their names, which are atoms. 
%
% This relation is not symmetric. We make it symmetric by defining
% a symmetric relations "on top". Improvement: This relation could
% be made "canonical" in that a unique representation would be
% enforced by demanding that VertexName1 @=< VertexName (i.e. the
% vertex names would appear sorted by the standard order of terms). 

connection(s, c, 3).
connection(c, l, 2).
connection(l, i, 4).
connection(l, j, 4).
connection(i, j, 6).
connection(i, k, 4).
connection(j, k, 4).
connection(k, e, 5).
connection(e, g, 2).
connection(g, h, 2).
connection(h, f, 3).
connection(f, d, 5).
connection(d, a, 4).
connection(b, d, 4).
connection(b, a, 3).
connection(b, s, 2).
connection(b, h, 1).
connection(a, s, 7).

% ---------
% "Symmetric connection relation"
% ---------

sym_connection(Vertex1,Vertex2,Cost) :- connection(Vertex1,Vertex2,Cost).
sym_connection(Vertex1,Vertex2,Cost) :- connection(Vertex2,Vertex1,Cost).

% =========
% Start measuring paths and their cost from vertex 'a'.
%
% Initially we only know about vertex 'a' itself
%
% - It is the only member in the list of vertices to be visited next,
%   at  distance/cost 0. This is represented by a single pair in list
%   VisitThese: it is a list containing only [0-a].
% - We have a path to 'a' with overall distance/cost 0, containing only
%   vertex 'a' found at cost 0: the path is [a-0].
%   PathContainerIn maps 'a', the destination vertex, to that path
%   [[a-0]]. 
%   The container is implemented by an "association list" (a "map") 
%   from library(assoc); other abstract data types are possible, in
%   particular SWI-Prolog's "dict" if this were running in SWI-Prolog.
% =========

main :-
   list_to_assoc([a-[a-0]],PathContainerIn),
   VisitThese=[0-a], 
   % Do it!
   dijsktra(VisitThese,PathContainerIn,PathContainerOut),
   % We are done! we just need to print out...
   assoc_to_list(PathContainerOut,Pairs),
   print_list(Pairs).

print_list([]).
print_list([Vertex-Path|More]) :-
   reverse(Path,ReversedPath),
   Path=[_-Dist|_],
   format("Vertex ~q is at distance ~d via ~q~n",[Vertex,Dist,ReversedPath]),
   print_list(More).
   
% =========
% dijsktra([CurCost-CurVertex|More],PathContainerIn,PathContainerOut).
% 
% - The first argument is the "list of vertexes to be visited next", named 
%   "VisitThese" (i.e. the "boundary" of the search), sorted by the distance/cost
%   of their path from the initial vertex, ascending (so we always need
%   to just grab the head of "VisitThese" to find the vertex which is guaranteed 
%   nearest the start vertex on visit).
% - The second argument is the "container of the best-path-known-so-far to 
%   vertexes already seen (all of those visited or tentatively visited in 
%   handle_neighbors/7, for which a best-path-known-so-far could be determined)
%   Note that a cheaper representation than keeping the full path would be 
%   to just keep the last edge of the path.
%   The container is used to create a new container, accumulator-style, which 
%   is the third argument, which contains all the best paths to all the vertices
%   at success-time.
% =========

dijsktra([],PathContainer,PathContainer) :- !.

dijsktra(VisitThese,PathContainerIn,PathContainerOut) :-
   VisitThese = [CurCost-CurVertex|MoreToVisit],
   format("VisitThese is currently: ~q ~n",[VisitThese]),   
   % bagof/3 fails if no neighbors, but that's only the case if the initial vertex is unconnected
   bagof(LocalNeighbor-LocalCost,
         sym_connection(CurVertex,LocalNeighbor,LocalCost),
         Neighbors),  
   format("Neighbors of vertex ~q : ~q ~n",[CurVertex,Neighbors]),
   get_assoc(CurVertex,PathContainerIn,CurPath),  
   % format("Found path for current vertex ~q: ~q~n",[CurVertex,CurPath]),
   handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainer2,VisitTheseToo),
   append(VisitTheseToo,MoreToVisit,Dirty),
   clean_visit_these_list(Dirty,Clean),
   format("Dirty visitations : ~q, Clean visitations : ~q~n",[Dirty,Clean]),
   dijsktra(Clean,PathContainer2,PathContainerOut).
    
% =========
% "Tentatively visit" all the neighbors of "CurVertex" ("CurVertex" can be reached through
% "CurPath" at cost "CurCost"), determining for each a path and overall cost
%
% We may reach a neighbor of "CurVertex":
%
% - for the first time if no path to it is stored in PathContainer yet. 
%   Then a new path is stored in PathContainer and the vertex is added to
%   the list of vertexes-to-be-visted-next, "VisitThese" (which will have
%   to be sorted by overall path cost before the recursive call to dijkstar/3)
% - for a not-the-first time if a path to it is stored in PathContainer yet
%   (so the neighbor has been visited (and its old path is - by construction - 
%    cheaper and it shall not be visited again) or it has been only 
%    tentatively visited (and its old path *may* be costlier and thus demand
%    replacement by the new path as well as addition of a cheaper 
%    entry in the list of vertexes to be visited next)
% 
% Note that the list "Neighbors" also contains the neighboring vertex on
% the path "CurPath" through which "CurVertex" was reached in the first place.
% But there is no need to handle that case in a special way. This is just a
% vertex that has been visited previously and the old path is cheaper.
%
% handle_neighbors(+CurVertex,+CurPath,+CurCost,
%                  +Neighbors,
%                  +PathContainerIn,-PathContainerOut,
%                  -VisitThese)
%
% =========

% case of "all neighbor vertices handled, we are done"

handle_neighbors(_CurVertex,_CurPath,_CurCost,[],PathContainer,PathContainer,[]).

% case of "neighbor vertex already has a path but the new path is costlier

handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainerOut,VisitThese) :-
   Neighbors=[Vertex-LocalCost|More],                           % grab the next neighbor
   get_assoc(Vertex,PathContainerIn,[Vertex-BestCostSoFar|_]),  % grab its known path, if it exists (fails if not)
   NewCost is CurCost+LocalCost, 
   NewCost >= BestCostSoFar,                                    % the new path is costlier
   !,                                                           % do nothing, move to next neighbor
   handle_neighbors(CurVertex,CurPath,CurCost,More,PathContainerIn,PathContainerOut,VisitThese).

% case of "neighbor vertex already has a path but the new path is cheaper; note that it is added to the "VisitThese" list

handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainerOut,[NewCost-Vertex|VisitThese]) :-
   Neighbors=[Vertex-LocalCost|More],                           % grab the next neighbor
   get_assoc(Vertex,PathContainerIn,[Vertex-BestCostSoFar|_]),  % grab its known path, if it exists (fails if not)
   NewCost is CurCost+LocalCost,
   NewCost < BestCostSoFar,                                     % new path is cheaper
   !,                                                           % replace path, retain neighbor as "to be visited"
   put_assoc(Vertex,PathContainerIn,[Vertex-NewCost|CurPath],PathContainer2),
   handle_neighbors(CurVertex,CurPath,CurCost,More,PathContainer2,PathContainerOut,VisitThese).

% case of "neighbor vertex has no path yet"; note that it is added to the "VisitThese" list

handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainerOut,[NewCost-Vertex|VisitThese]) :-
   Neighbors=[Vertex-LocalCost|More],                   % grab the next neighbor
   \+ get_assoc(Vertex,PathContainerIn,_),              % vertex has no path yet
   !,                                                   % the cut is not really needed
   NewCost is CurCost+LocalCost,
   put_assoc(Vertex,PathContainerIn,[Vertex-NewCost|CurPath],PathContainer2),
   handle_neighbors(CurVertex,CurPath,CurCost,More,PathContainer2,PathContainerOut,VisitThese).
   
% ---
% Transfrom list of elements "Cost-Vertex" 
%
% ... which is the list of vertexes and their "best cost yet" that 
%     shall be expanded by the Dijkstra algorithm, but in which several
%     entries for the same "Vertex" may appear
% ... into a new list where there is always only exactly one entry for
%     any "Vertex" appaearing in the original list, and the "Cost"
%     associated to it is the minimum cost for that "Vertex".
% ---

clean_visit_these_list(Dirty,SortedByCostAsc) :-
   predsort(sort_pairs,Dirty,SortedByVertexFirstCostSecond),
   keep_best(SortedByVertexFirstCostSecond,DuplicatesRemovedMinCostRetained),
   keysort(DuplicatesRemovedMinCostRetained,SortedByCostAsc).
   
sort_pairs(Order,Cost1-Vertex,Cost2-Vertex) :- 
   !,                                   % Same vertex - order depends on associated cost
   compare(Order,Cost1,Cost2). 
   
sort_pairs(Order,_-Vertex1,_-Vertex2) :-
   Vertex1 \== Vertex2,         
   !,                                   % Different vertex - Sort lexicographically (cut is not needed actually)
   compare(Order,Vertex1,Vertex2). 
   
keep_best([Cost-Vertex,_-Vertex|More],Out) :-  
   !,                                   % Vertex appears twice - retain only first entry (with smallest cost)
   keep_best([Cost-Vertex|More],Out).

keep_best([Cost1-Vertex1,Cost2-Vertex2|More],[Cost1-Vertex1|Out]) :-
   Vertex1 \== Vertex2,
   !,                                   % Different vertex - Retain first vertex and its cost, move on
   keep_best([Cost2-Vertex2|More],Out).
   
keep_best([X],[X]).  % Termination case #1
keep_best([],[]).    % Termination case #2

% ---
% Test clean_visit_these_list/2
% ---

:- begin_tests(clean_visit_these_list).

test(1,true(Result == [])) :- clean_visit_these_list([],Result).
test(2,true(Result == [3-b])) :- clean_visit_these_list([3-b],Result).
test(2,true(Result == [3-b])) :- clean_visit_these_list([4-b,3-b,5-b],Result).
test(3,true(Result == [3-b,8-v,9-a,10-w])) :- clean_visit_these_list([8-v,10-w,9-a,3-b],Result).
test(4,true(Result == [2-w,3-b,8-v,9-a]))  :- clean_visit_these_list([8-v,10-w,9-a,4-w,3-b,2-w,12-v],Result).

:- end_tests(clean_visit_these_list).

【讨论】:

  • 哈哈!这是越野车!为变量提供更好的名称并解压缩 if-then-else 揭示了这一点。我的天啊。好的,现在修好了。
【解决方案2】:

dijkstras 算法的主要思想是在已访问节点和未访问节点之间创建边界。一切都发生在边界内。边界只是节点对及其与起始节点的距离。在每一步中,来自未访问集的节点与起点的总距离最小将被转移到访问集。为此,检查从任何已访问节点到未访问节点的所有“连接”:到已访问节点的距离 + 连接值是总距离。这样,您可以获得从起点到目标的最小距离。请注意,这个(主要是确定性的)算法适用于这两组而不是具体路径。但是,如果您想要一条路径,您还必须跟踪访问集中每个节点的源节点。

您的代码似乎有多个问题,我发现实现自己的解决方案更容易。问题之一是起始节点出现在未访问列表中。您可以使用以下代码作为参考来调试您的代码:

minmember([A],A).
minmember([(E,N,S)|T],R):-
    minmember(T,(Etmp,Ntmp,Stmp)),
    (   N>Ntmp
    ->  R=(Etmp,Ntmp,Stmp)
    ;   R=(E,N,S)
    ).

boundary(Visited, (Connected, Dist, Node_Name), Unvisited):-
    member((Node_Name, Value_to, _),Visited), 
    are_connected(Node_Name, Connected, Dist0), 
    member(Connected,Unvisited), 
    Dist is Dist0+Value_to.
    
boundary2path(B,Sink,[(Sink,Dist)]):-
    member((Sink,Dist,start),B),
    !.
boundary2path(B,Sink,[(Sink,Dist)|R]):-
    member((Sink,Dist,Source),B),
    boundary2path(B,Source,R).

dijkstra(Boundary, _, Dest_Node, Path):-
    Boundary = [(Dest_Node,_,_)|_],
    !,
    format('Hit the end :) \n'),
    boundary2path(Boundary,Dest_Node,Path),
    format('Path found = ~w\n', [Path]).
dijkstra(Visited, Unvisited, Dest_Node, Path) :-
    findall(E, boundary(Visited, E, Unvisited), Connections),
    format('Connections = ~w\n', [Connections]),        
    minmember(Connections,(Node_Name,Dist,Source)),
    format('Choosen = ~w\n', [(Node_Name,Dist,Source)]),        
    append(U1,[Node_Name|U2],Unvisited), 
    append(U1,U2,New_Unvisited),
    format('New_Unvisited = ~w\n', [New_Unvisited]),        
    format('Visited = ~w\n', [[(Node_Name,Dist,Source)|Visited]]),
    dijkstra([(Node_Name,Dist,Source)|Visited], New_Unvisited, Dest_Node, Path).

现在的输出:

?- dijkstra([(h, 0, start)], [b, g, e, k, j, i, l, c, s, a, d, f], g, L).

Connections = [(f,3,h),(g,2,h),(b,1,h)]
Choosen = b,1,h
New_Unvisited = [g,e,k,j,i,l,c,s,a,d,f]
Visited = [(b,1,h),(h,0,start)]
Connections = [(d,5,b),(a,4,b),(s,3,b),(f,3,h),(g,2,h)]
Choosen = g,2,h
New_Unvisited = [e,k,j,i,l,c,s,a,d,f]
Visited = [(g,2,h),(b,1,h),(h,0,start)]
Hit the end :) 
Path found = [(g,2),(h,0)]
L = [(g,2), (h,0)] ;
false.

对于目标j,没有未访问的节点:

?- dijkstra([(h, 0, start)], [b, g, e, k, j, i, l, c, s, a, d, f], j, L).

...
Visited = [(i,12,l),(k,9,e),(l,8,c),(c,6,s),(d,5,b),(a,4,b),(e,4,g),(f,3,h),(s,3,b),(g,2,h),(b,1,h),(h,0,start)]
Connections = [(j,18,i),(j,13,k),(j,12,l)]
Choosen = j,12,l
New_Unvisited = []
Visited = [(j,12,l),(i,12,l),(k,9,e),(l,8,c),(c,6,s),(d,5,b),(a,4,b),(e,4,g),(f,3,h),(s,3,b),(g,2,h),(b,1,h),(h,0,start)]
Hit the end :) 
Path found = [(j,12),(l,8),(c,6),(s,3),(b,1),(h,0)]
L = [(j,12), (l,8), (c,6), (s,3), (b,1), (h,0)] ;
false.

​对于最后一个查询,我为边框画了一个草图:

【讨论】:

  • 我还是有点困惑。边界是否只是保存一些距离数据的两个顶点之间的一条边? Sink 是什么?
  • 下沉是目标。边界是从访问集中的节点到未访问集中的节点的所有连接的集合。您甚至可以将其可视化:第一步在您的起始节点周围画一个圆圈。找到总距离最短的邻居,擦除附近的线并重新绘制包括提到的邻居的线。
  • @CaspianAhlberg 我为你做了一张照片:i.stack.imgur.com/j3RhJ.png
  • 太酷了!它像章鱼一样环绕着目的地。哇!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-18
  • 2013-01-17
  • 1970-01-01
  • 2020-05-16
  • 1970-01-01
  • 2014-05-31
相关资源
最近更新 更多