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 Emean for each edge (as the literal meaning offor eachsuggests), or only each edge beginning withv, (as you reasonably assumed)? Then, after the for loop, is thevin question the finalvfrom theforloop, 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 thevat the end were to be the last, arbitrary, value ofvfrom the loop. That suggests that filtering is correct, because in that case,vmeans 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
Graphclass and does couple of basic tests: