I am trying to solve a problem on Udacity described as follows:
# Find Eulerian Tour
#
# Write a function that takes in a graph
# represented as a list of tuples
# and return a list of nodes that
# you would follow on an Eulerian Tour
#
# For example, if the input graph was
# [(1, 2), (2, 3), (3, 1)]
# A possible Eulerian tour would be [1, 2, 3, 1]
I came up with the following solution, which, while not as elegant as some of the recursive algorithms, does seem to work within my test case.
def find_eulerian_tour(graph):
tour = []
start_vertex = graph[0][0]
tour.append(start_vertex)
while len(graph) > 0:
current_vertex = tour[len(tour) - 1]
for edge in graph:
if current_vertex in edge:
if edge[0] == current_vertex:
current_vertex = edge[1]
elif edge[1] == current_vertex:
current_vertex = edge[0]
else:
# Edit to account for case no tour is possible
return False
graph.remove(edge)
tour.append(current_vertex)
break
return tour
graph = [(1, 2), (2, 3), (3, 1)]
print find_eulerian_tour(graph)
>> [1, 2, 3, 1]
However, when submitting this, I get rejected by the grader. I am doing something wrong? I can't see any errors.
Here is the original code in Gregor Ulm's webpage and it works.
I'm also in the same lecture, WolframH's answer doesn't work for me. Here is my solution (has been accepted by the grader):
Push all possible
next node
into a heap(search
) then search each one of them while recording.You can mimic the behavior of BFS algorithm and piggyback on it.
Note: I haven't tried write the answer using linked lists because linked lists requires defining 2 classes (one to define nodes and their behaviors, and one to define the entire linked list and its behaviors). However, for more efficiency in (append) and (remove) behaviors, you should use linked lists instead of arrays :
Here's a case your algorithm can't handle: the complete graph on 4 vertices. Sticking a
print tour
in there, you get:I'll leave you to find the problem with your approach -- you could easily google for a complete implementation, so since you didn't, I'm assuming you want the fun of figuring it out for yourself. :^)
Edit:
Hmmph. I admit I thought it was simply a missed failure case at the start. Anyway, @WolframH beat me to an updated example, but you could also look at the complete graph on 5 vertices, where your code gives
and misses the edges (2,3), (2,4), and (3,4).
This solution is optimized for O(V+E) complexity i.e. linear in number of Edges and Vertices in the graph
For those directly wishing to see the code: https://github.com/cubohan/py-algos/blob/master/eulerian_tour.py
Note that the code breaches readability and DRY design majorly but after reading the explanation, you can easily churn out your own version.
**First the problem can be divided into these subtasks: **
Build the graph into a more friendly structure than a list of edges for easier processing i.e (Adjacency Lists)
Find the degrees of each vertex to first check if an Eulerian Tour is possible (Are there only even degrees? Also, STORE these values in a dict with vertex as key => to be used later)
Build the Eulerian tour
The concept behind my solution is simple.
You choose the vertex with the highest degree as a starting point and set it as the current vertex. (Note: You accomplish this at the same time while calculating degrees of every vertex. You store all these degrees in a dictionary.)
You insert current vertex in the route list which is your answer (Note: also make a dictionary of vertices and their indexes in the route list. This will be used later.)
You visit the first edge of the current vertex if it's not already visited. (Note: A dictionary of visited edges is maintained, the key to this dict is a sorted tuple of the pair of vertices constituting the edge. After visiting an edge, mark it visited by inserting it into the dict.)
You maintain a count of degrees remaining of the current vertex and the visited vertex (This will prove useful later) (Note: you only need to subtract 1 from the dict of degrees you generate before each time you choose an edge)
You switch current vertex to the vertex on the other end of the edge you decided to visit.
Repeat steps 2-5 until you can't find an unvisited edge in the current vertex. (Note: this means you have returned to your starting vertex)
Now consider this: Notice that any unvisited edges/vertices will constitute subgraphs in the main graph which have the same properties as the main graph i.e. an Eulerian tour is possible from any of the vertices in the subgraph starting and ending at the same vertex.
All unvisted edges can be thus visited by taking Eulerian tours in these subgraphs You just need to merge these sub-tours with the first tour.
Next:
You loop through all the vertices of the graph and build a subtour in the same process as listed for the main tour if and only if the reduced degree of this vertex is non-zero
The way these tours will merge with the route list computed previously is that you replace the position of the vertex you're considering to start a subtour from in the route list with the subtour output list and later flattening this route list
We're still not done! What's wrong above?
What happens when you get a non-zero degree vertex which HASN'T BEEN VISITED and isn't present in the route list?!
A caveat: This is an exceptional case.
You might encounter vertices not visited before and thus they will not be present in the main route list.
IGNORE THESE while looping! One of the vertices you have already visited with non-zero reduced degree is GUARANTEED to lead to these vertices in the subtour you will create starting from those.
How is that possible??!!
Draw a graph from the test case given in the link to the code and you will understand. Trace out what your algo is doing every step of the process. Draw it! An image is log(N) complexity to understanding and words O(n2).
Ah, and note that this guarantee holds only if all the edges in the input list form a single graph and not two separate disjointed graphs.
I was going through the same course on Udacity. And I implemented Hierholzer's algorithm after reading it from Wikipedia. Here is the link to algorithm https://en.wikipedia.org/wiki/Eulerian_path
And below is my code. No doubt, it got accepted by the grader(after doing some Python3 to Python2 changes). :)
Hope this helps.