This put up is partly brought on by a reveal[0] on Hacker Info[1] which acknowledged:

- The three examples used to designate recursion, towers of Hanoi,

fibonacci, and binary search are not essentially the most convincing ones.

All will be solved with iteration.

Others pointed out that (in thought) any recursive algorithm can

be expressed as an iterative one, and *vice versa,* however I spoke back[2]

announcing:

- Academic examples are now and again ever “real world”, and the relaxation

“real world” is typically too advanced to map the underlying

principles obvious … I could write a follow-up put up to

this with a pleasant real-world (smartly, Pure Maths calculation)

instance … presumably that you just must well per chance derive that extra enjoyable.

So here’s that put up.

Some time ago I wrote a put up on Thinking About Recursion, and additional

not too long ago I wrote a couple of deliver touching on the deliver of the

sequence of Vertices Required For Cycles. As it happens, the code for

that deliver is heavily recursive, and affords a large instance

of the yelp of recursion to resolve a real deliver.

So I believed I’d demonstrate you.

## The cycle deliver

The deliver in quiz is this:

Given a run integer $C$, what’s the minimum sequence of vertices wished for a graph to grasp exactly $C$ cycles? |

The characteristic $C{rightarrow}N$ is now in the OEIS:

However how are values computed?

## Counting cycles

On this case we can follow the frequent definition from

Graph Theory, thru which we enable neither loops nor plenty of

edges between any pair of vertices. This explicit deliver

affords an very most involving instance of why the so-known as “Easy Graph” is

the frequent convention: if we allowed loops then we could well per chance presumably even possess

any sequence of cycles we wanted honest with a single vertex.

The final technique is to search at all graphs on $N$ vertices and

for every, depend what number of cycles there are. Since we will be

procuring for the minimum sequence of vertices for a given sequence of

cycles we can ignore graphs which can per chance per chance be not associated, and we can

ignore graphs which possess a vertex of stage 1. So we search at all

the non-isomorphic associated graphs of minimum stage 2.

However at its heart, for a given graph we would prefer to depend what number of

cycles it has. Right here is the algorithm.

In actuality the explicit illustration of the graph have to not

be a deliver, and I’ve used this explicit illustration because

then the code reads moderately naturally. Well, to me, anyway.

I could well per chance presumably also abstract it away additional, however this code feels moderately

recount.

We account for routine “count_cycles” which takes a graph in the secure

of a dictionary mimicking a arena of edges. If {u,v} is an edge in

the graph, then both graph_edges[(u,v)] or graph_edges[(v,u)]

is arena to 1. There are extra atmosphere qualified info structures for this

algorithm, however for the functions of describing it we will follow

this one.

Routine “count_cycles” is a recursive routine.

If there are no edges then the answer is 0 … there are no cycles.

def count_cycles( graph_edges ): if not graph_edges: return 0

In every other case we can catch an edge. In thought we could well per chance presumably also derive some

suave heuristic for selecting the brink, however in practice we simply

obtain the principle edge from the checklist. Even so, we call out to a

routine to map the selection:

selected_edge = choose_edge( graph_edges )

Every cycle both *does* embody this edge, or does *not* embody

this edge. So we obtain away it from the graph:

del graph_edges[ selected_edge ]

Then depend the cycles in the graph with out that edge. To map that we

simply call this routine – here’s the principle instance of recursion.

cycles_without = count_cycles( graph_edges )

Cycles with this edge

So now now we possess to depend what number of cycles there are in conjunction with this

chosen edge. We search at this draw here on the factual, and our

chosen edge is the one in purple becoming a member of the vertices $u$ and $v$,

shown in blue.

Any cycle that comprises this edge $(u,v)$ will then encompass the

edge, and a path from $u$ to $v$. So having earlier eliminated the

edge $(u,v)$ we can now depend the sequence of cycles by counting the

paths from $u$ to $v$ on this reduced graph.

u, v = vertices_of_edge( selected_edge ) cycles_with = count_paths( graph_edges, u, v )

We then put the brink assist, and return the total:

graph_edges[ selected_edge ] = 1 return cycles_without + cycles_with

That’s how we depend the cycles, and all that is left is easy how to depend

the paths from $u$ to $v$.

## Counting paths

So now now we possess to write the code to depend the paths from $u$ to $v$:

def count_paths( graph_edges, u, v ):

If $u$ and $v$ are the identical vertex then now we possess one (degenerate)

path:

Paths from $u$ to $v$

In every other case our path must yelp likely the most purple edges shown here on

the draw. Further, since paths (and cycles) can’t repeat a vertex,

once one of those purple edges is used, the opposite ones can’t be used

because in every other case we would be returning to vertex $u$. Which is

forbidden.

So we can obtain away the purple edges, and for every of the purple vertices

we then depend the sequence of paths from that to $v$.

So let’s obtain our checklist of purple edges, and obtain away them:

purple_edges = [ e for e in graph_edges if e[0]==u or e[1]==u ] for edge in purple_edges: del graph_edges[ edge ]

Then we obtain our checklist of purple vertices.

red_vertices = [ e[1] for e in purple_edges if e[0]==u ] + [ e[0] for e in purple_edges if e[1]==u ]

The total sequence of paths from $u$ to $v$ is the total sequence of

paths from the purple vertices to $v$. So we obtain that total by a

recursive call to this identical routine:

total = 0 for red_vertex in red_vertices: total += count_paths( graph_edges, red_vertex, v )

Within the discontinuance, we put all those edges assist, and return our result:

for edge in purple_edges: graph_edges[ edge ] = 1 return total

def count_paths( graph_edges, u, v ): if u == v: return 1 purple_edges = [ e for e in graph_edges if e[0]==u or e[1]==u ] for edge in purple_edges: del graph_edges[ edge ] red_vertices = [ e[1] for e in purple_edges if e[0]==u ] + [ e[0] for e in purple_edges if e[1]==u ] total = 0 for red_vertex in red_vertices: total += count_paths( graph_edges, red_vertex, v ) for edge in purple_edges: graph_edges[ edge ] = 1 return total def count_cycles( graph_edges ): if not graph_edges: return 0 selected_edge = choose_edge( graph_edges ) del graph_edges[ selected_edge ] cycles_without = count_cycles( graph_edges ) u, v = vertices_of_edge( selected_edge ) cycles_with = count_paths( graph_edges, u, v ) graph_edges[ selected_edge ] = 1 return cycles_without + cycles_with

## Abstract

So here is the code total. The routine “count_cycles” calls

itself recursively, and additionally calls “count_paths”. In its turn

the routine “count_paths” calls itself recursively, and we’re

finished.

With any recursive routine we must consistently search info from: How will we all know

the recursion bottoms out?

The answer on this case is that in every call the sequence of

edges decreases, is a total number, and can not lope beneath zero.

On myth of that, in every case the resolution tree has to discontinuance.

And the code will not be undoubtedly that massive, nor that advanced. Right here’s the

strength of recursion when accurately utilized.

## Discontinuance Notes

Right here’s not intended to be especially atmosphere qualified code, neither is it

particularly magnificent Python code. That’s not the explanation (on this

case). The supreme version changed into once extra atmosphere qualified, however less obvious due

to the modifications made for that efficiency.

Even so, I hope the algorithm, and the intention the recursion works, is

obvious.

## References and links

[0] The reveal: https://info.ycombinator.com/merchandise?identity=24744814

[1] Hacker Info: https://info.ycombinator.com/info

[2] My answer: https://info.ycombinator.com/merchandise?identity=24744962

My ensuing from Neil Walker for some priceless feedback and suggestions.