This post is partly resulted in by a comment[0] on Hacker Data[1] which mentioned:

- The three examples susceptible to show conceal recursion, towers of Hanoi,

fibonacci, and binary search are no longer potentially the most convincing ones.

All could moreover be solved with iteration.

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

be expressed as an iterative one, and *vice versa,* but I answered[2]

pronouncing:

- Tutorial examples are no longer incessantly ever “staunch world”, and one thing

“staunch world” is incessantly too complex to kind the underlying

solutions sure … I will write a follow-up post to

this with a good staunch-world (properly, Pure Maths calculation)

example … maybe it’s essential to well gain that extra gratifying.

So right here’s that post.

A whereas ago I wrote a post on Pondering About Recursion, and further

no longer too long ago I wrote a pair of scenario referring to the scenario of the

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

that scenario is closely recursive, and offers a huge example

of utilizing recursion to resolve a staunch scenario.

So I believed I’d show conceal you.

## The cycle scenario

The scenario in ask is this:

Given a obvious integer $C$, what’s the minimum option of vertices wished for a graph to possess exactly $C$ cycles? |

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

However how are values computed?

## Counting cycles

On this case we’re going to follow the long-established definition from

Graph Theory, wherein we permit neither loops nor multiple

edges between any pair of vertices. This particular scenario

provides an correct example of why the so-known as “Easy Graph” is

the long-established convention: if we allowed loops then we could well maintain

any option of cycles we wished factual with a single vertex.

The general approach is to secret agent at all graphs on $N$ vertices and

for every body, count how many cycles there are. Since we’re going to be

hunting for the minimum option of vertices for a given option of

cycles we’re going to ignore graphs that are no longer linked, and we’re going to

ignore graphs that maintain a vertex of level 1. So we secret agent at all

the non-isomorphic linked graphs of minimum level 2.

However at its coronary heart, for a given graph we maintain now to count how many

cycles it has. Right here is the algorithm.

Basically the particular illustration of the graph must no longer

be a scenario, and I’ve vulnerable this particular illustration on fable of

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

I could abstract it away extra, but this code feels moderately

direct.

We define routine “count_cycles” which takes a graph within the kill

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

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

is situation to 1. There are extra efficient records structures for this

algorithm, but for the capabilities of describing it we will follow

this one.

Routine “count_cycles” is a recursive routine.

If there usually are no longer any edges then the reply is 0 … there usually are no longer any cycles.

def count_cycles( graph_edges ): if no longer graph_edges: return 0

Otherwise we’re going to earn an edge. In theory we could gain some

artful heuristic for picking the threshold, but in practice we simply

hold the first edge from the checklist. Even so, we name out to a

routine to kind the option:

selected_edge = choose_edge( graph_edges )

Every cycle both *does* encompass this edge, or does *no longer* encompass

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

del graph_edges[ selected_edge ]

Then count the cycles within the graph without that edge. To affect that we

simply name this routine – right here’s the first instance of recursion.

cycles_without = count_cycles( graph_edges )

Cycles with this edge

So now we maintain now to count how many cycles there are at the side of this

chosen edge. We secret agent at this map right here on the appropriate, and our

chosen edge is the one in red joining the vertices $u$ and $v$,

proven in blue.

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

edge, and a route from $u$ to $v$. So having earlier eradicated the

edge $(u,v)$ we’re going to now count the option of cycles by counting the

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

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

We then place the threshold aid, and return the whole:

graph_edges[ selected_edge ] = 1 return cycles_without + cycles_with

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

the paths from $u$ to $v$.

## Counting paths

So now we maintain now to put in writing the code to count the paths from $u$ to $v$:

def count_paths( graph_edges, u, v ):

If $u$ and $v$ are the the same vertex then we maintain now one (degenerate)

route:

Paths from $u$ to $v$

Otherwise our route must employ one amongst the red edges proven right here on

the map. Extra, since paths (and cycles) can no longer repeat a vertex,

once one amongst these red edges is vulnerable, the a sort of ones can no longer be vulnerable

on fable of otherwise we could be returning to vertex $u$. Which is

forbidden.

So we’re going to employ away the red edges, and for every of the red vertices

we then count the option of paths from that to $v$.

So let’s get our checklist of red edges, and employ 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 get our checklist of red 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 whole option of paths from $u$ to $v$ is the whole option of

paths from the red vertices to $v$. So we employ that whole by a

recursive name to this same routine:

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

At reliable, we place all these edges aid, and return our consequence:

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

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 ] whole = 0 for red_vertex in red_vertices: whole += count_paths( graph_edges, red_vertex, v ) for edge in purple_edges: graph_edges[ edge ] = 1 return whole def count_cycles( graph_edges ): if no longer 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 right here is the code whole. The routine “count_cycles” calls

itself recursively, and moreover calls “count_paths”. In its flip

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

finished.

With any recursive routine we can maintain to aloof incessantly query: How will each person is conscious of

the recursion bottoms out?

The reply on this case is that in every name the option of

edges decreases, is a whole quantity, and could no longer streak under zero.

Thanks to that, in every case the option tree has to cease.

And the code is no longer that giant, nor that advanced. Right here is the

energy of recursion when appropriately utilized.

## Dwell Notes

Right here is no longer intended to be especially efficient code, nor is it

in particular correct Python code. That’s no longer the diagram (on this

case). The reliable version became extra efficient, but much less sure due

to the adjustments made for that efficiency.

Even so, I am hoping the algorithm, and the procedure the recursion works, is

sure.

## References and links

[0] The comment: https://news.ycombinator.com/merchandise?id=24744814

[1] Hacker Data: https://news.ycombinator.com/news

[2] My reply: https://news.ycombinator.com/merchandise?id=24744962

My attributable to Neil Walker for some valuable solutions and solutions.