加权图的最小生成树

时间:2018-05-01 13:03:48

标签: prolog

我需要编写一个谓词,使用Prim's Algorithm创建加权无向图的最小生成树。这就是我到目前为止所做的:

% call the recursive predicate with the list of all nodes and
%    one element (in this case the last one).
mst(T) :- nodes(N), last(N,E), mst(T,N,E).

% The element E is added to the visited list and removed from the toVisit list.
mst(T,N,E) :- append(T,E,S), delete(R,E,L)...

然后,toVisit列表应根据连接到访问列表中任何节点的边的距离进行排序。有关如何做到这一点的任何建议吗?

1 个答案:

答案 0 :(得分:0)

因此,首先让我们尝试创建一个解决方案,以便从维基百科中找到生成树而不是最小值:“无向图的生成树T G是一个树的子图其中包括G的所有顶点,边数最小可能“和”树是一个没有周期的连通无向图。如果它跨越G,它就是图G的生成树{ {1}}(也就是说,它包含G的每个顶点)并且是G的子图(树中的每个边都属于G)“。在这个例子中,我考虑以这种方式构建的图形:

graph(ListOfVertices,ListOfEdges)

并且ListOfEdges的每个元素都是edge(X,Y,Cost)

让我们构建一个创建树的谓词(在本例中是一个完全连接的图)。 make_kn_weighted/4输入每个节点的度数(Size),边缘费用的MinValueMaxValue,并在graph(LV,Comb)中创建图表。

make_kn_weighted(Size,MinValue,MaxValue,graph(LV,Comb)):-
    Size1 is Size+1,
    make_ordered_list(1,Size1,LV),
    find_all_combinations_weighted(LV,MinValue,MaxValue,[],Comb).

make_ordered_list(Max,Max,[]):- !.
make_ordered_list(I,Max,[I|T]):-
    I < Max,
    I1 is I+1,
    make_ordered_list(I1,Max,T).

find_all_combinations_weighted([_],_,_,C,C):- !.
find_all_combinations_weighted([H|T],Min,Max,CT,CO):-
    find_combinations_weighted(H,T,Min,Max,C),
    append(CT,C,C1),
    find_all_combinations_weighted(T,Min,Max,C1,CO).

find_combinations_weighted(_,[],_,_,[]):- !.
find_combinations_weighted(E,[H|T],Min,Max,[edge(E,H,V)|TE]):-
    random(Min,Max,V),
    find_combinations_weighted(E,T,Min,Max,TE).

?- make_kn_weighted(4,2,7,G).
G = graph([1, 2, 3, 4], [edge(1, 2, 6), edge(1, 3, 6), edge(1, 4, 5), edge(2, 3, 4), edge(2, 4, 5), edge(3, 4, 5)]).

然后我们创建一个生成生成树的谓词:

spanning_tree(graph([N|T],Edges),graph([N|T],TreeEdges)) :- 
   generate_spanning_tree(T,Edges,TreeEdgesUnsorted),
   sort(TreeEdgesUnsorted,TreeEdges).

generate_spanning_tree([],_,[]).
generate_spanning_tree(Curr,Edges,[Edge|T]) :- 
    select(Edge,Edges,Edges1),
    get_vertices(Edge,X,Y),
    is_connected_to_tree(X,Y,Curr),
    delete(Curr,X,Curr1),
    delete(Curr1,Y,Curr2),
    generate_spanning_tree(Curr2,Edges1,T).

get_vertices(edge(X,Y),X,Y).
get_vertices(edge(X,Y,_),X,Y).

is_connected_to_tree(X,Y,Ns):- 
    memberchk(X,Ns), 
    \+ memberchk(Y,Ns), !.
is_connected_to_tree(X,Y,Ns):- 
    memberchk(Y,Ns), 
    \+ memberchk(X,Ns).

所以,显然,生成树和图形都有相同的顶点,这就是我写graph([N|T],Edges),graph([N|T],TreeEdges)的原因。为了生成实际树,我们从列表中选择一个节点select/3(在Edges1中,我们拥有Edges中没有Edge的所有元素。然后使用{{我们用边缘连接两个顶点。用get_vertices/3我们检查两个顶点是否已经连接(在列表中或剩下的副本中)。然后我们将两个选定的边删除到未连接的列表中顶点(is_connected_to_tree/3)使用两次Curr应用于delete/3。最后一次调用,参数更新的递归调用。测试:

Curr

现在让我们关注Primm的算法:为了调整我们的谓词以最低成本生成树,我们首先考虑每条边的成本对边进行排序,然后我们可以调用,如上所述{{1} }。所以,在prolog代码中:

?- make_kn(4,G), spanning_tree(G,T).
G = graph([1, 2, 3, 4], [edge(1, 2), edge(1, 3), edge(1, 4), edge(2, 3), edge(2, 4), edge(3, 4)]),
T = graph([1, 2, 3, 4], [edge(1, 2), edge(1, 3), edge(1, 4)]) ;
G = graph([1, 2, 3, 4], [edge(1, 2), edge(1, 3), edge(1, 4), edge(2, 3), edge(2, 4), edge(3, 4)]),
T = graph([1, 2, 3, 4], [edge(1, 2), edge(1, 3), edge(2, 4)])
and so on...

generate_spanning_tree/3使用mst_prim(graph([H|T],Edges),graph([H|T],TreeEdges),Cost):- predsort(compare_edges_value,Edges,SortedEdges), generate_spanning_tree(T,SortedEdges,TreeEdgesUnsorted), sort(TreeEdgesUnsorted,TreeEdges), sum_cost(TreeEdges,0,Cost). compare_edges_value(O,edge(X1,Y1,C1),edge(X2,Y2,C2)):- compare(O,C1+X1+Y1,C2+X2+Y2). sum_cost([],C,C). sum_cost([edge(_,_,C)|T],CT,Tot):- CT1 is CT+C, sum_cost(T,CT1,Tot). 进行排序以确定订单。 predsort/3只是总结每个选定边的成本。查询:

compare_edge/3

在回溯中,它会生成所有生成树(如果您不想要此行为,则可以在调用sum_cost/3后添加剪切)。