I implemented the Tarjan's strongly connected components algorithm, according to wikipedia, in Python, but it isn't working. The algorithm is quite short and I cannot find any difference, so I cannot tell why it isn't working. I tried to check the original paper, but could not find it.
Here is the code.
def strongConnect(v):
global E, idx, CCs, c, S
idx[v] = (c, c) #idx[v][0] for v.index # idx[v][1] for v.lowlink
c += 1
S.append(v)
for w in [u for (v2, u) in E if v == v2]:
if idx[w][0] < 0:
strongConnect(w)
# idx[w] = (idx[w][0], min(idx[v][1], idx[w][1])) #fixed, thx
idx[v] = (idx[v][0], min(idx[v][1], idx[w][1]))
elif w in S:
idx[v] = (idx[v][0], min(idx[v][1], idx[w][0]))
if (idx[v][0] == idx[v][1]):
i = S.index(v)
CCs.append(S[i:])
S = S[:i]
E = [('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'E'), ('E', 'A'), ('A', 'E'), ('C', 'A'), ('C', 'E'), ('D', 'F'), ('F', 'B'), ('E', 'F')]
idx = {}
CCs = []
c = 0
S = []
for (u, v) in E:
idx[u] = (-1, -1)
idx[v] = (-1, -1)
for v in idx.keys():
if idx[v][0] < 0:
strongConnect(v)
print(CCs)
You can check the graph visually if you prefer. As you can see this is a quite forward translation of the pseudocode in wikipedia. However, this is the output:
[['D', 'E', 'F'], ['B', 'C'], ['A']]
There should be only one strongly connected component, not three. I hope the question is right in all its aspects, if not I'm sorry. In any case, thank you very much.
Ok, I had some more time to think about this. I'm no longer certain that filtering the edges was the problem, as I previously stated. In fact, I think there's an ambiguity in the pseudocode; does
for each (v, w) in E
mean for each edge (as the literal meaning offor each
suggests), or only each edge beginning withv
, (as you reasonably assumed)? Then, after the for loop, is thev
in question the finalv
from thefor
loop, as it would be in Python? Or does that go back to being the originalv
? Pseudocode doesn't have clearly defined scoping behavior in this case! (It would be really weird if thev
at the end were to be the last, arbitrary, value ofv
from the loop. That suggests that filtering is correct, because in that case,v
means the same thing all the way through.)However, under any circumstances, the clear error in your code is here:
According to the pseudocode, that should definitely be
Once you make that change, you get the expected result. Frankly it doesn't surprise me that you made that mistake, because you're using a really weird and counterintuitive data structure. Here's what I think is an improvement -- it adds only a few more lines, and I find it to be much more readable.
However, I find the use of global variables here distasteful. You could hide this away in its own module, but I prefer the idea of creating a callable class. After looking more closely at Tarjan's original pseudocode, (which confirms that the "filtered" version is correct, by the way), I wrote this. It includes a simple
Graph
class and does couple of basic tests: