Minimum Spanning Tree of weighted graph - prolog

I need to write a predicate that creates a minimum spanning tree of a weighted undirected graph, using Prim's Algorithm. This is what I have so far:
% 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)...
Then the toVisit list should be sorted according to the distance of the edges connected to any of the nodes in the visited list. Any suggestions on how to do this?

So, first of all let's try to create a solution to find a spanning tree, not minimum, from wikipedia: "spanning tree T of an undirected graph G is a subgraph that is a tree which includes all of the vertices of G, with minimum possible number of edges" and "A tree is a connected undirected graph with no cycles. It is a spanning tree of a graph G if it spans G (that is, it includes every vertex of G) and is a subgraph of G (every edge in the tree belongs to G)". In this example, i consider a graph built in this way:
graph(ListOfVertices,ListOfEdges)
and each element of ListOfEdges is edge(X,Y,Cost).
Let's build a predicate that creates a tree (in this case a fully connected graph). make_kn_weighted/4 has as input the degree of each node (Size), the MinValue and MaxValue of edges's cost and creates the Graph in 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)]).
Then we crate a predicate that generates a spanning tree:
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).
So, obviously, both the spanning tree and the graph have the same vertices, and this is why i wrote graph([N|T],Edges),graph([N|T],TreeEdges). To generate the actual tree, firs we select a node from the list, with select/3 (in Edges1 we have all the elements from Edges without Edge. Then with get_vertices/3 we foud the two vertices connected by an edge. With is_connected_to_tree/3 we check if the two vertices are not already connected (in the list or remaining verices). Then we delete the two selected edges to the list of unconnected vertices (Curr) using two times delete/3 applied to Curr. Last call, the recursive call with parameters updated. Test:
?- 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...
Now let's focus to Primm's algorthm: to adapt our predicate to generate the tree with minimum cost, we have, first of all sort the edges considering the cost of each edge, then we can call, as above, generate_spanning_tree/3. So, in prolog code:
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 sorts using compare_edge/3 to determine the order. sum_cost/3 simply sums the cost of every selected edge. Query:
make_kn_weighted(4,2,7,G), mst_prim(G,T,C).
G = graph([1, 2, 3, 4], [edge(1, 2, 3), edge(1, 3, 6), edge(1, 4, 6), edge(2, 3, 5), edge(2, 4, 2), edge(3, 4, 2)]),
T = graph([1, 2, 3, 4], [edge(1, 2, 3), edge(2, 4, 2), edge(3, 4, 2)]),
C = 7 ;
In backtracking, it generates all the spanning trees (if you don't want this behaviour, you can add a cut after calling generate_spanning_tree/2).

Related

A* algorithm - Order of expanded nodes

I am wondering why the order of expansion is ACBDG and not just ACG. From what i understand from the A*-algorithm is that is uses the f-value, which is the heuristic value and the real cost combined to determine the next node to expand. Is is that when expanding a node, the previous cost will be the f-value from the next node and current node combined? Or the f-value of the next node and g-value from current node combined?
It seems strange that after expanding the A, C and B nodes that the next node would not be G, as H(G) is lower than H(D). I observe that the real cost is lower to D, but isnt the point of A* to go for the combined heuristic value and the actual cost?
The starting node is A and the goal is G
In the following steps to the left of the => are the unexpanded nodes, and on the right are the action and the result of the expansion. Nodes on the left contain between the parenthesis the path, the cost of that path, and heuristic value. On the right there is no heuristic value yet. Nodes on the left are sorted by cost of path + heuristic value, with later nodes added after the existing nodes if they have equal sums:
A(-, 7) => Expand A from -: got B(A, 1), C(A, 4)
C(A, 4, 2), B(A, 1, 6) => Expand C from A: got B(AC, 6), D(AC,
7), G(AC, 8)
B(A, 1, 6), G(AC, 8, 0), D(AC, 7, 2), B(AC, 6, 6) =>
Expand B from A: got C(AB, 3), D(AB, 5)
C(AB, 3, 2), D(AB, 5, 2), G(AC, 8, 0), D(AC, 7, 2), B(AC, 6, 6) => Expand C from AB: got D(ABC, 6), G(ABC, 7)
D(AB, 5, 2), G(ABC, 7, 0), G(AC, 8, 0), D(ABC, 6, 2), D(AC, 7, 2), B(AC, 6, 6) => Expand D from AB: got C(ABD, 8), G(ABD, 11)
G(ABC, 7, 0), G(AC, 8, 0), D(ABC, 6, 2), D(AC, 7, 2), C(ABD, 8, 2), G(ABD, 11, 0), B(AC, 6, 6) => Found G from ABC, end of search

Visit every Node of Graph in multitple "splited" paths

Visit every Node of Graph in even Parts
Question
Given an undirected Graph G having n nodes and m edges (with weights). How do I get x Paths, that visit in total all edges m from a constant starting node and return in the end, of that path, to that starting node. Nodes can be visited more than once.
How do I write an algorithm to find those paths, that have the smallest maximum weight count. Secondly the total weight count should be minimized, but the most important part is that the highest weight count of one Path is as small as possible. The path should start from a given starting node and return after all times to that starting node.
Is this problem NP-Complete? (I think that it is, but I'm not sure and like to know why?)
I would be very happy about any help.
Example
Given this Graph with starting point 0 and x = 4
G = [(0, 2, w=1), (0, 8, w=1), (0, 6, w=1), (0, 4, w=1), (3, 2, w=1), (2, 1 w=1),
(1, 8, w=1), (8, 7, w=1), (7, 6, w=1), (6, 5, w=1), (5, 4, w=1), (4, 3, w=1)]
the best paths would be
([0, 6, 5, 4, 0], [0, 6, 7, 8, 0],
[0, 8, 1, 2, 0], [0, 2, 3, 4, 0])
because every path has a total weight count of 4. There are no path combinations where maximum total is smaller ...

Check if a set of numbers satisfies a triple (X, Y, Z) given the following conditions

So I was trying to determine an efficient algorithm to be able to determine this but no real luck. Given a set of distinct positive integers J of length 4-7 in the range 1-10^9, determine the number of triples (X, Y, Z) that are satisfied by this set if satisfaction means that all the numbers in J are any of X, Y, Z, X+Y, X+Z, Y+Z, or X+Y+Z. X, Y, Z are positive integers and are nondecreasing respectively.
I tried to engineer a brute force solution. Given the third smallest number in J is D, we can try (D*D*D) different triples and check if J satisfies each of them. However, the time complexity would be too high.
Example: (2, 2, 5), (2, 3, 4), (2, 4, 5), (3, 4, 5), (4, 5, 7) are the triples that are satisfied by J = {4, 5, 7, 9}

Dynamic programming approach to from number list (use pseudo code)

STATE the running time of the methods. Polynomial time algorithms are expected.
A club wants to organize a party for the students. However, only part of the students can attend
the party. The candidates are choosing by pairs with the following requirements: assume the
students are lined-up, each student is given a lucky number, and some students may have the
same lucky numbers. The club requires that only students who have the same luck numbers may
be paired-up and join in the party while all the students locate within a chosen pair will lose the
opportunity.
We have helped the club to formulate the problem formally.
Given a sequence of positive integers (represent the lucky numbers) A = (a1, a2, ... an), a pair (ai, aj) is defined as a dancing pair if ai=aj, and i<j. A dancing pair list L={(𝑎𝑖1, 𝑎𝑗1),… (𝑎𝑖𝑘, 𝑎𝑗𝑘) }
is good if and only if for any two pairs (𝑎𝑖𝑥, 𝑎𝑗𝑥), (𝑎𝑖𝑦, 𝑎𝑗𝑦) of L, either jx<iy or jy<ix for 1 ≤ 𝑥, 𝑦 ≤ 𝑘.
For example, if A={1, 3, 4, 1, 2, 3, 2, 4, 3, 5, 3, 4, 5}, some good dancing lists might be:
{(1, 1), (2, 2), (3, 3)} (locations in A={1, 3, 4, 1, 2, 3, 2, 4, 3, 5, 3, 4, 5})
{(1, 1), (3, 3) (5, 5)} (locations in A={1, 3, 4, 1, 2, 3, 2, 4, 3, 5, 3, 4, 5})
a) Given a sequence of positive integers A, design a polynomial time algorithm to find a
good dancing pair list with maximum number of dancing pairs.
b) Assume the weight of the dancing pair (ai, aj) is ai+aj. The total weight of a good dancing
list is the sum of all the weights of the dancing pairs in the list. Design a polynomial time
algorithm to find a good dancing pair list with maximum total weight.
for part a,
if i write like this
foreach list L
foreach item I in list L
foreach list L2 such that L2 != L
for each item I2 in L2
if I == I2
return new 3-tuple(L, L2, I) //
how to continue?

Can Hadoop's Shuffle/Sort (or partition) phase be customized to perform a graph traversal?

I am still learning about the MapReduce framework, specifically implemented by Hadoop, and I wonder if it can be modified to perform the following task:
The Map() function will emit (key,value) pairs, whose keys are arrays of size 2, say int[2].
I would like every pair containing any of the two integers in common to be mapped to the same reducer.
So for example, if Map() emits: ([2,3],4),([2,4],5),([6,5],2),([5,7],1), then Reduce1 should receive the first two pairs, and Reduce2 the second two (first two sharing 2, second two sharing 5).
This can be viewed as a connected component problem, where the vertices are the integers in the int[], and edges are shared between any two integers in the same int[].
A change in algorithm and you can probably achieve this - but you're going to need emit each edge twice
For each edge you currently output, you should output them for both vertex IDs, amend the outputted value to include the other edge, the weight and optionally a direction (if edge direction is important to your algorithm).
So instead of this:
([2,3],4)
([2,4],5)
([6,5],2)
([5,7],1)
Output this (S denotes the key was the source, D denotes the key was the destination):
(2, [3, 4, S])
(3, [2, 4, D])
(2, [4, 5, S])
(4, [2, 5, D])
(6, [5, 2, S])
(5, [6, 2, D])
(5, [7, 1, S])
(7, [5, 1, D])
Now in your reducer, you'll be grouping by the vertex ID, and will be able to iterate other the tuples containing the other vertex ID, a weight, and the direction:
(2, [3, 4, S])
(2, [4, 5, S])
(3, [2, 4, D])
(4, [2, 5, D])
(5, [6, 2, D])
(5, [7, 1, S])
(6, [5, 2, S])
(7, [5, 1, D])
You still need to be aware that each edge could potentially be processed twice, especially if the edges exist in both directions between two vertices.

Resources