1. Description
Recursion is a method of solving a problem by repeatedly decomposing it into identical subproblems.
Scenarios that require recursion usually have the following characteristics.
- the original problem can be decomposed into subproblems and the subproblems are the same as the original problem
- there is a clear termination condition
Common applications of recursion are as follows.
- recursive data solving (Fibonacci function)
- data structure forms with obvious recursive properties such as binary trees, generalized tables, etc.
- typical problems for which recursive solutions are applicable (e.g., Hanoi problem)
Recursive algorithms are not commonly used, and in most programming languages they are less efficient and take up more stack space. Stack storage is used at each level of recursive calls, and too many recursions can cause stack overflow. For example, for Python, each function call adds a layer of stack frames, and the stack size is not infinite, resulting in a stack overflow.
2. Examples
2.1. Finding the factorial of a number N
Formula for factorial of data N: $n!=n\times(n-1)\times(n-2)… \times1$
The code is as follows.
|
|
The output is as follows.
2.2. Fibonacci
Fibonacci is the Fibonacci series, i.e. a special series (0 1 1 2 3 5 8 ...
), with the following characteristics.
- The first two numbers are 0, 1 or 1, 1.
- The value starting from the third number is the sum of the first two numbers.
Go solves the Fibonacci series function as follows.
|
|
The output is as follows.
3. Efficiency Comparison
The disadvantages of recursion were stated at the beginning, as its repeated calls can lead to stack overflow, and recursion is not as efficient as a normal loop in most programming languages.
Take the Fibonacci function as an example, rewritten as a loop.
|
|
The output is as follows.
It can be seen that the time gap is very obvious, and this is one of the disadvantages of recursive algorithms in practical applications.
4. Tail Call Elimination
As mentioned earlier, the reason for poor performance for recursive calls is that stack storage is used at each level of the recursive call.
Too many stack frames lead to a cresting trend in memory usage, and the idea of optimizing the execution efficiency of recursive calls is to reduce their stack frame generation.
In functional programming languages, language standards often require compilers or runtimes to implement tail call elimination.
A tail call is a function whose last statement is also a statement that returns the calling function (Wiki). An example of tail call elimination for the go language, using Fibonacci as an example, is as follows.
|
|
As you can see, the execution efficiency is almost comparable to the loop writing method.