Graph algorithms - DFS, generators callbacks, and optimisation
Robin Becker
robin at jessikat.fsnet.co.uk
Sat Nov 29 09:01:01 EST 2003
Well in this case couldn't you make seen do the detection for you.
If I understand your analysis you could make the seen dictionary a
special kind of dictionary.
ie
class DFSSeen(dict):
def __contains__(self,x):
r = super(DFSSeen,self).__contains__(x)
if r:
print 'back link to',x
return r
G = {
1: (2,3),
2: (3,5),
3: (4,),
4: (6,),
5: (2,6),
6: (1,),
}
for v in DFS(G,1,DFSSeen({1:1})):
print v
using the above I get
C:\tmp>dfs.py
(1, 2)
(2, 3)
(3, 4)
(4, 6)
back link to 1
(2, 5)
back link to 2
back link to 6
back link to 3
so cycle detection is possible without altering the original code, but
it probably isn't sufficient for your purposes in that we don't have the
start vertex.
In article <oeuvcume.fsf at yahoo.co.uk>, Paul Moore <pf_moore at yahoo.co.uk>
writes
>I'm trying to check a graph (represented in the usual Python adjacency
>list format) for loops. This is dead simple - you use a depth-first
>search, and look out for "back edges" (edges from a vertex "back" to
>one you've already seen).
>
>I already have a DFS algorithm which generates each edge in turn. It's
>great for what it does, but it ignores back edges.
>
>def DFS(graph, start, seen = None):
> if not seen:
> seen = {start: 1}
> for v in graph[start]:
> if v not in seen:
> seen[v] = 1
> yield start, v
> if v in graph:
> for e in DFS(graph, v, seen):
> yield e
>
>I could code a variation on this, just for this one problem, but that
>sort of cut and paste coding offends me (and DFS is *just* fiddly
>enough that I'm not confident of getting it right each time I
>reimplement it). So what I'd rather do is enhance my existing code to
>be a bit more general.
>
>I have a C++ reference which implements a highly general DFS
>algorithm, using the visitor pattern (pass a visitor object to the
>function, and various callback methods are called at points in the
>algorithm - there are discover_vertex, discover_edge, back_edge, etc
>callbacks).
>
>I could code something like this (the pseudocode for the algorithm is
>almost executable in Python!) but it doesn't feel particularly
>"pythonic". Having to write a visitor class for each use case feels
>unwieldy (as well as being perceptibly slower than the generator
>implementation). Having the various callbacks as arguments (pass a
>callable or None) isn't much faster or cleaner.
>
>Can anybody think of a good way of making the DFS code above a little
>more generic, without losing the efficient and simple (from the
>caller's POV) approach of the current version?
>
>Paul.
--
Robin Becker
More information about the Python-list
mailing list