/ #Algorithms

# Algorithms Techniques - Part 3: Recursion

An overview of recursive algorithms.

A recursive algorithm is an algorithm that calls itself repeatedly until a certain condition is met. For problem solving, it means that we can apply the same algorithm multiple times to a smaller subset of the original input until we find a solution.

Imagine you have a glass full of M&M’s and you want to empty the glass, but you can only remove one M&M at the time. It would work like this:

Is the glass empty? No, remove 1 M&M.
Is the glass empty? No, remove 1 M&M.
Is the glass empty? No, remove 1 M&M.
… Is the glass empty? Yes, stop.

Using pseudo-code:

``````function empty_glass(int n):
if n equals 0:
// Do nothing
else:
empty_glass(n - 1)
``````

This is a very simple example that could’ve been solved using a simple for loop, but recursion is still the best (or at least more elegant) approach for some classes of problems.

## Fibonacci

A classic example of a problem that can be solved using recursion is calculating a Fibonacci number. Fibonacci numbers form a sequence starting with 1 and 1 where each number is the sum of the previous 2 numbers:
1, 1, 2, 3, 5, 8, …

And we have the following Fibonacci numbers:
The 1st number is 1
The 2nd number is 1
The 3rd number is 2
The 4th number is 3
The 5th number is 5
The 6th number is 8
… and so on

Being n the Fibonacci number you want, this can be expressed as:

``````F(n) = F(n - 1) + F(n - 2)
``````

Let’s see the implementation in Go:

``````package main

import "fmt"

func Fb(n int) int {
// If n is 0 or 1, return it
if n < 2 {
return n
}

return Fb(n-1) + Fb(n-2)
}

func fibonacci() {
for n := 1; n <= 6; n++ {
fib := Fb(n)
fmt.Printf("Fibonnacci (%d): %d\n", n, fib)
}
}``````

Running this will produce:

``````Fibonnacci (1): 1
Fibonnacci (2): 1
Fibonnacci (3): 2
Fibonnacci (4): 3
Fibonnacci (5): 5
Fibonnacci (6): 8
``````

## The good and the bad

Recursion has its pros and cons. For some problems, like traversing a tree, recursion provides an easier to understand and somewhat more elegant solution. However, it can be slower, especially if the recursion is too deep.

Running a quick benchmark on the code above will show us that the speed decreases the deeper the recursion goes:

``````package main

import "testing"

// This is the actual test
func benchmarkFb(i int, b *testing.B) {
for n := 0; n < b.N; n++ {
Fb(i)
}
}

// The functions below are different benchmarks, each with a different input
// to our Fibonacci function.

func BenchmarkFb1(b *testing.B) {
benchmarkFb(1, b)
}

func BenchmarkFb2(b *testing.B) {
benchmarkFb(2, b)
}

func BenchmarkFb3(b *testing.B) {
benchmarkFb(3, b)
}

func BenchmarkFb10(b *testing.B) {
benchmarkFb(10, b)
}

func BenchmarkFb25(b *testing.B) {
benchmarkFb(25, b)
}

func BenchmarkFb45(b *testing.B) {
benchmarkFb(45, b)
}``````

We can see that in the output:

``````BenchmarkFb1-4                  434873206                2.55 ns/op
BenchmarkFb2-4                  174979096                6.76 ns/op
BenchmarkFb3-4                  100525826               11.8 ns/op
BenchmarkFb10-4                  2466976               457 ns/op
BenchmarkFb25-4                     1856            613773 ns/op
BenchmarkFb45-4                        1        9282143600 ns/op
``````

Cover picture by Simon Hurry via Unsplash 