what is the lowelink mean of Tarjan's algorithm - graph

I was reading the description of Tarjan's algorithm for finding the strongly connected components in a driected graph.
But I find it hard to understand these codes snippet:
if (w.index is undefined) then
// Successor w has not yet been visited; recurse on it
strongconnect(w)
v.lowlink := min(v.lowlink, w.lowlink)
else if (w is in S) then
// Successor w is in stack S and hence in the current SCC
v.lowlink := min(v.lowlink, w.index)
end if
the fourth and the seventh lines are different, this make me confused.
And in my opinion,the seveth line could write as the same way with the fourth line
v.lowkink := min(v.lowlink, w.index)
I test this in my program and it works fine, and for me, it's better to understand bcz verdex v cloud reach hight up root, but i couldn't prove itT_T.

I wrote a program that enumerated all graphs of size 4, then run each version (with either min(v.lowlink, w.index) or min(v.lowlink, w.lowlink) if w is in S) and compared the results. Both were exactly identical in all cases, even though w.lowlink and w.index were often different.
The reason why we can use w.index is this: consider where on the stack S relative to the current node v the other node w is.
If it's earlier on the stack then it has a smaller index than the current node (because it was visited earlier, duh), so the current node is not the "head" of its connected component and that would be reflected in v.lowlink <= w.index < v.index anyway. And it's not like w.lowlink has any particular meaning at this point either, it's in the progress of being computed and doesn't necessarily have its final value yet.
Now, if w is later in the stack than v, then the crucial property that the algorithm depends on is that then w is a descendant of v, not some sibling/cousing node still left there from an earlier recursive call. Or, as it is usually stated in a complete proof, strongly connected components never span several unconnected branches of our search tree (forest). Because since it's an SCC, there must be a path from w to v, and since we are enumerating stuff in a depth-first order, we must have visited v using that path from w before we have finished processing w, so w should be earlier in the stack than v!
And if w is a descendant of v then we already got its actual lowlink value the first time we visited it and are not interested in it any more.
On a side note, it's trivial to get rid of the lowlink property on nodes and make strongconnect return it directly. Then we wouldn't be tempted to check it instead of w.index in the second case =)

Related

Can someone explain me this kind of recursion?

I want to understand recursion.
I understand stupid example with math but i m not sure to know the essence of it.
I have 1 example that i don t understand how it works:
TREE-ROOT-INSERT(x, z)
if x = NIL
return z
if z.key < x.key
x.left = TREE-ROOT-INSERT(x.left, z)
return RIGHT-ROTATE(x)
else
x.right = TREE-ROOT-INSERT(x.right, z)
return LEFT-ROTATE(x)
I know what this code does:
First insert a node in a BST and then rotate each time so the new node became the root.
But in my mind analysing the code i suppose that it insert the node where it has to go and then JUST 1 TIME it rotates the tree.
How is it possible that the tree is rotated every time?
You need to maintain your place in the recursive call for each level of the tree. When you hit return RIGHT-ROTATE (or left) for the first time, you're not completely done; you take the tree that is the result of the ROTATE function, and place it in the code where the recursive TREE-ROOT-INSERT call was one level higher in the stack. You then rotate again, and return the current tree one level higher up in the stack, until you've hit the original root of the tree.
What is important for understanding recursion is to think of the recursive function as an abstract black box. In other words, when reading or reasoning about recursive function, you should focus on the current iteration, treat the invocation of the recursive function as atomic (something you cannot explore into) assuming it can do what it is supposed to do, and see how its result can be used to solve the current iteration.
You already know the contract of your TREE-ROOT-INSERT(x, z):
insert z into a binary search tree rooted at x, transform the tree so that z will become the new root.
let's look at this snippet:
if z.key < x.key
x.left = TREE-ROOT-INSERT(x.left, z)
return RIGHT-ROTATE(x)
This says z is less than x so it goes to the left sub-tree (because it is BST). TREE-ROOT-INSERT is invoked again, but we won't follow into it. Instead we just assume it can do what it is meant to do: it will insert z to the tree rooted at x.left, and make z the new root. Then you will get a tree of the bellow structure:
x
/ \
z ...
/ \
... ...
Again, you don't know how exactly calling TREE-ROOT-INSERT(x.left, z) will get you the z-rooted sub-tree. At this moment you don't care, because the real important part is what follows: how to make this entire tree rooted at z? The answer is the RIGHT-ROTATE(x)
But in my mind analysing the code i suppose that it insert the node where it has to go and then JUST 1 TIME it rotates the tree.
How is it possible that the tree is rotated every time?
If I understand you correctly, you are still thinking how to solve the problem in a non-recursive way. It is true that you can insert z into the BST rooted at x using the standard BST insertion procedure. That will put z in the correct position. However to bring z to the root from that position, you need more than 1 rotation.
In the recursive version, rotation is required to bring z to the root after you get a z-rooted sub-tree. But to get the z-rooted sub-tree from the original x.left rooted sub-tree, you need a rotation as well. Rotation is called many times, but on different sub-trees.

prolog in math - searching the level of node in prolog

Assuming here is a binary search tree, and given the rule that above(X,Y) - X is directly above Y. Also I created the rule root(X) - X has no parent.
Then, I was trying to figure out what the depth of node in this tree.
Assume the root node of tree is "r" So I got fact level(r,0). In order to implement the rule level(N,D) :-, what I was thinking is it should be have a recursion here.
Thus, I tried
level(N,D): \+ root(N), above(X,N), D is D+1, level(X,D).
So if N is not a root, there has a node X above N and level D plus one, then recursion. But when I tested this, it just works for the root condition. When I created more facts, such as node "s" is leftchild of node "r", My query is level(s,D). It returns me "no". I traced the query, it shows me
1 1 Call: level(s,_16) ?
1 1 Fail: level(s,_16) ?
I just confusing why it fails when I call level(s,D)?
There are some problems with your query:
In Prolog you cannot write something like D is D+1, because a variable can only be assigned one value;
at the moment you call D is D+1, D is not yet instantiated, so it will probably cause an error; and
You never state (at least not in the visible code) that the level/2 of the root is 0.
A solution is to first state that the level of any root is 0:
level(N,0) :-
root(N).
Now we have to define the inductive case: first we indeed look for a parent using the above/2 predicate. Performing a check that N is no root/1 is not necessary strictly speaking, because it would conflict with the fact that there is an above/2. Next we determine the level of that parent LP and finally we calculate the level of our node by stating that L is LP+1 where L is the level of N and LP the level op P:
level(N,L) :-
above(P,N),
level(P,LP),
L is LP+1.
Or putting it all together:
level(N,0) :-
root(N).
level(N,L) :-
above(P,N),
level(P,LP),
L is LP+1.
Since you did not provide a sample tree, I have no means to test whether this predicate behaves as you expect it to.
About root/1
Note that by writing root/1, you introduce data duplication: you can simply write:
root(R) :-
\+ above(_,R).

How could I calculate the number of recursions that a recursive rule does?

I deal with a problem; I want to calculate how many recursions a recursive rule of my code does.
My program examines whether an object is component of a computer hardware or not(through component(X,Y) predicate).E.g component(computer,motherboard) -> true.
It does even examine the case an object is not directly component but subcomponent of another component. E.g. subcomponent(computer,ram) -> true. (as ram is component of motherboard and motherboard is component of computer)
Because my code is over 400 lines I will present you just some predicates of the form component(X,Y) and the rule subcomponent(X,Y).
So, some predicates are below:
component(computer,case).
component(computer,power_supply).
component(computer,motherboard).
component(computer,storage_devices).
component(computer,expansion_cards).
component(case,buttons).
component(case,fans).
component(case,ribbon_cables).
component(case,cables).
component(motherboard,cpu).
component(motherboard,chipset).
component(motherboard,ram).
component(motherboard,rom).
component(motherboard,heat_sink).
component(cpu,chip_carrier).
component(cpu,signal_pins).
component(cpu,control_pins).
component(cpu,voltage_pins).
component(cpu,capacitors).
component(cpu,resistors).
and so on....
My rule is:
subcomponent(X,Z):- component(X,Z).
subcomponent(X,Z):- component(X,Y),subcomponent(Y,Z).
Well, in order to calculate the number of components that a given component X to a given component Y has-that is the number of recursions that the recursive rule subcomponents(X,Y), I have made some attempts that failed. However, I present them below:
i)
number_of_components(X,Y,N,T):- T is N+1, subcomponent(X,Y).
number_of_components(X,Y,N,T):- M is N+1, subcomponent(X,Z), number_of_components(Z,Y,M,T).
In this case I get this error: "ERROR: is/2: Arguments are not sufficiently instantiated".
ii)
number_of_components(X,Y):- bagof(Y,subcomponent(X,Y),L),
length(L,N),
write(N).
In this case I get as a result either 1 or 11 and after this number true and that's all. No logic at all!
iii)
count_elems_acc([], Total, Total).
count_elems_acc([Head|Tail], Sum, Total) :-
Count is Sum + 1,
count_elems_acc(Tail, Count, Total).
number_of_components(X,Y):- bagof(Y,subcomponent(X,Y),L),
count_elems_acc(L,0,Total),
write(Total).
In this case I get as results numbers which are not right according to my knowledge base.(or I mistranslate them-because this way seems to have some logic)
So, what am I doing wrong and what should I write instead?
I am looking forward to reading your answers!
One thing you could do is iterative deepening with call_with_depth_limit/3. You call your predicate (in this case, subcomponent/2). You increase the limit until you get a result, and if you get a result, the limit is the deepest recursion level used. You can see the documentation for this.
However, there is something easier you can do. Your database can be represented as an unweighted, directed, acyclic graph. So, stick your whole database in a directed graph, as implemented in library(ugraphs), and find its transitive closure. In the transitive closure, the neighbours of a component are all its subcomponents. Done!
To make the graph:
findall(C-S, component(C, S), Es),
vertices_edges_to_ugraph([], Es, Graph)
To find the transitive closure:
transitive_closure(Graph, Closure)
And to find subcomponents:
neighbours(Component, Closure, Subcomponents)
The Subcomponents will be a list, and you can just get its length with length/2.
EDIT
Some random thoughts: in your case, your database seems to describe a graph that is by definition both directed and acyclic (the component-subcomponent relationship goes strictly one way, right?). This is what makes it unnecessary to define your own walk through the graph, as for example nicely demonstrated in this great question and answers. So, you don't need to define your own recursive subcomponent predicate, etc.
One great thing about representing the database as a term when working with it, instead of keeping it as a flat table, is that it becomes trivial to write predicates that manipulate it: you get Prolog's backtracking for free. And since the S-representation of a graph that library(ugraph) uses is well-suited for Prolog, you most probably end up with a more efficient program, too.
The number of calls of a predicate can be a difficult concept. I would say, use the tools that your system make available.
?- profile(number_of_components(computer,X)).
20===================================================================
Total time: 0.00 seconds
=====================================================================
Predicate Box Entries = Calls+Redos Time
=====================================================================
$new_findall_bag/0 1 = 1+0 0.0%
$add_findall_bag/1 20 = 20+0 0.0%
$free_variable_set/3 1 = 1+0 0.0%
...
so:count_elems_acc/3 1 = 1+0 0.0%
so:subcomponent/2 22 = 1+21 0.0%
so:component/2 74 = 42+32 0.0%
so:number_of_components/2 2 = 1+1 0.0%
On the other hand, what is of utmost importance is the relation among clause variables. This is the essence of Prolog. So, try to read - let's say, in plain English - your rules.
i) number_of_components(X,Y,N,T) what relation N,T have to X ? I cannot say. So
?- leash(-all),trace.
?- number_of_components(computer,Y,N,T).
Call: (7) so:number_of_components(computer, _G1931, _G1932, _G1933)
Call: (8) _G1933 is _G1932+1
ERROR: is/2: Arguments are not sufficiently instantiated
Exception: (8) _G1933 is _G1932+1 ?
ii) number_of_components(X,Y) here would make much sense if Y would be the number_of_components of X. Then,
number_of_components(X,Y):- bagof(S,subcomponent(X,S),L), length(L,Y).
that yields
?- number_of_components(computer,X).
X = 20.
or better
?- aggregate(count, S^subcomponent(computer,S), N).
N = 20.
Note the usage of S. It is 'existentially quantified' in the goal where it appears. That is, allowed to change while proving the goal.
iii) count_elements_acc/3 is - more or less - equivalent to length/2, so the outcome (printed) seems correct, but again, it's the relation between X and Y that your last clause fails to establish. Printing from clauses should be used only when the purpose is to perform side effects... for instance, debugging...

How do I learn Tarjan's algorithm?

I have been trying to learn Tarjan's algorithm from Wikipedia for 3 hours now, but I just can't make head or tail of it. :(
http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm#cite_note-1
Why is it a subtree of the DFS tree? (actually DFS produces a forest? o_O)
And why does v.lowlink=v.index imply that v is a root?
Can someone please explain this to me / give the intuition or motivation behind this algorithm?
The idea is: When traversing the tree, every time you've searched through a branch and are backtracking, you check whether you've encountered an edge to an 'upper' node in the tree.
If you didn't (if (v.lowlink = v.index)), then you've just completed an SCC - it consists of the current node and all nodes on the stack. That's exactly a subtree of the DFS tree, except for the nodes in SCCs that were already completed.
If you did, you propagate this information to 'upper' nodes (v.lowlink := min(v.lowlink, w.lowlink)), because combined with the path in DFS tree the edge creates an 'upward' path.
DFS produces a forest, but you always consider one tree a time. An SCC is always included in one DFS tree, otherwise (being an SCC) there would be a path in both directions between both (all) trees in question - that's a contradiction.
just adding to pjotr's answer: v.lowlink is basically the index of the upmost node that you have found in the tree. Keep in mind that upmost in this context means minimum as you keep increasing indices as you walk down. Now after processing all your successors, there's basically three cases:
v.lowlink < v.index: This indicates that you have found a back edge. Note that we haven't
just found any back edge, but one that points to a node that is "above" the current one. That's what v.lowlink < v.index implies.
v.lowlink = v.index: What we know in this case is that there is no back edge referring to anything above the current node. There might be a back edge to this node (which means that one of your successor nodes w has a lowlink such that w.lowlink = v.lowlink = v.index). It could also be that there was a back edge referring to something below the current node, which means that there was a strongly-connected component below the current node that has been printed out already. The current node, however, is definitely the root of a strongly-connected component as well.
v.lowlink > v.index: That's actually not possible. I'm just listing it for the sake of completeness. ;)
Hope it helps!
Some Intuition about the Tarjan's Algorithm:
During DFS, when we encounter a back edge from vertex v, we update its lowest reachable ancestor i.e. we update the value of low[v]
Now when the all the outgoing edges of a vertex are processed i.e we are about to exit the DFS call for the vertex v, we check the value of low[v], whether low[v] == v (Explanation below). If not this means v is not the root of the SCC and we now give the benefit to the parent of v i.e. the lowest reachable ancestor of parent[v] is now changed to low[v].
This sounds logical as if although there is no direct back edge from the parent[v] to the ancestor of v, but there is a path (back edge of v + edge towards v) via which the parent[v] can still reach the ancestor of v.
Thus we have also updated the low[parent[v]] here.
Therefore, we will keep on updating this chain and low[v] for all v will keep on updating until, we reach to the ancestor (via backtracking). For this ancestor low[v] will be equal to v. And thus this will act as the root of the SCC.
Hope this helps
Path for Mastering Bridges and Articulation Algorithms
Watch this video to get an awesome feeling that you have understood the
algorithm very well.
practice the algorithm from here and here.
practice problems to master it:
A. Cutting Figure
EC_P - Critical Edges
SUBMERGE - Submerging Islands
POLQUERY - Police Query

advice needed with prolog cut?

in this task i have a Prolog database filled with e.g.
edge(1,0)
edge(2,0)
edge(1,3)
an edge signifies that two points are joined.
I am asked to write a function called reach(i,j,k) where i is the start point j is the end point and k is the number of steps you may use.
K is needed to stop the recursion looping e.g.
Suppose the only edge I’ve got goes from 1 to3,and I’m trying to get to 6. Then I can’t get from 1to6 in one go. so I’ll look for somewhere I can get to, and see if I can get from there to 6. The first place I can get to in one go is 3, so I’ll try to get from there to 6.
i have done this as so:
%% Can you get there in one step (need two rules because all links are
%% from smaller to greater, but we may need to get from greater to smaller.
reach1(I, J,_K) :-
edge(I, J).
reach1(I, J,_K) :-
edge(J, I).
%% Chhose somewhere you can get to in one step: can you get from there
%% to your target?
reach1(I,J,K) :-
K>1,
edge(I, B),
K1 is K-1,
reach1(B,J,K1).
reach1(I,J,K) :-
K>1,
edge(B, I),
K1 is K-1,
reach1(B,J,K1).
this works, however i am stuck with the second part in which we are asked not to use k but to use a "cut" to do this.
does anyone know how to do this or can give me some pointers?
The cut ensures that once a goal has been resolved in one way, it doesn't look for another way.
example:
reach(I, J,_K) :-
edge(I, J).
no cut - if for some reason Prolog backtracks, it will try to reach from I to J another way.
You might feel there's no point reaching this node another way if the simple edge works, and in that case you can do:
reach(I, J,_K) :-
edge(I, J),
!.
which "cuts" any alternative to this goal, but the one Prolog has found.

Resources