[C Language] Systematic and intensive lecture on functions (3)


1. Recursion example

. Through the previous chapter ([C Language] Systematic Lecture on Functions (2)) we learned about the constraints of recursion. When recursion is written, there are two necessary conditions:
When recursion is written, there are two necessary conditions:
• Recursion must have a restriction condition. When it is satisfied When this condition occurs, recursion stops.
• After each recursive call, this limit is approached.
Let’s give an example of recursion to understand it more deeply!

2. Recursion example

2.1 Find the factorial of n

Calculate the factorial of n (without considering overflow). The factorial of n is the cumulative multiplication of numbers from 1 to n.
Analysis:
We know the formula of the factorial of n: n! = n ∗ (n − 1)!

For example:
5! = 5 * 4 * 3 * 2 * 1
4! = 4 * 3 * 2 * 1
So we can directly: 5! =5*4!
If we think about it this way, we can convert a large problem into a smaller one that is similar to the original problem to solve!

Insert image description here
After a little analysis, when n<=0, the factorial of n is 1, and the factorial of the remaining n can be calculated through the above formula.
The recursive formula of the factorial of n is as follows:

Code:

int Fact(int n)
{
    
    
	if (n <= 0)
		return 1;
	else
		return n * Fact(n - 1);
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = Fact(n);
	printf("%d\n", ret);
	return 0;
}

Insert image description here

Insert image description here
Of course, the value of n here does not consider the case that it is too large. If n is too large, it will cause stack overflow.

2.2 Print each digit of an integer sequentially

Input an integer m and print each digit of the integer in order.
For example:
Input: 1024 Output: 1 0 2 4
Input: 520 Output: 5 2 0< /span>
Analysis:

First, let’s look at 1024. How do we get each digit of this number?
1024%10 will get 4, and then 1024/10 will get 102, which is equivalent to removing 4
and then continue to 102%10, and you will get 2, then divide 10 to remove 2, and so on
Continue the %10 and \10 operations until every digit of 1234 is obtained;
But here There is a problem that the order of the numbers is reversed
But we got inspiration and found that the lowest digit of a number is actually the easiest to get, and it can be obtained through %10 a>
Then we assume that we want to write a function Print to print each digit of n, as shown below:
Insert image description here

Print(1024)
   |
   └── Print(102)
             |
             └── Print(10)
                       |
                       └── Print(1)
                                 |
                                 └── Print(0)

In this diagram, starting from the rightmost number, the Print function is called recursively, each time printing out the last digit of the current number, and then reducing the size of the problem until the number becomes 0. The specific process is as follows:

  1. Call Print(1024).
  2. Print(1024) calls Print(102).
  3. Print(102) calls Print(10).
  4. Print(10) calls Print(1).
  5. Print(1) calls Print(0).
  6. Print(0) returns directly without any processing.
  7. Print(1) returns, prints out 1, and then returns to the function that called it.
  8. Print(10) returns, prints out 0, and returns to the function that called it.
  9. Print(102) returns, prints out 2, and then returns to the function that called it.
  10. Print(1024) returns, prints out 1, and then the function execution ends.
    11 code:
# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void Print(int n)
{
    
    
	if (n > 9)
	{
    
    
		Print(n / 10);
	}
	printf("%d ", n % 10);
}
int main()
{
    
    
	int m = 0;
	scanf("%d", &m);
	Print(m);
	return 0;
}

Insert image description here

3. Recursion and iteration

3.1 Recursive thinking

Recursion is a useful programming technique, but like other techniques, it can be easily misused. For example, when you see a derived formula, it is easy to write it in a recursive form like a mathematical function.
Insert image description here

int Fact(int n)
{
    
    
 	if(n<=0)
 	return 1;
 	else
 	return n*Fact(n-1);
}

The Fact function can produce correct results, but there is some runtime overhead involved in the recursive function call.
What is the runtime overhead?
In C language, each function call needs to apply for a memory space in the stack area for this function call to save the values ​​​​of various local variables during the function call. This space is called the runtime stack, or function stack frame. If the function does not return, the corresponding stack frame space will always be occupied. Therefore, if there is a recursive call in the function call, each recursive function call will open up its own stack frame space. Until the function recursion stops and starts to return, the stack frame space will be released layer by layer. Therefore, if you use function recursion to complete the code, too deep a recursion level will waste too much stack frame space, and may also cause stack overflow problems.

So if you don’t want to use recursion, you have to think of other methods, usually iteration (usually looping).
For example: Calculating the factorial of n can also produce the cumulative multiplication of numbers from 1 to n.

int Fact(int n)
{
    
    
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
	{
    
    
		ret *= i;
	}
	return ret;
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = Fact(n);
	printf("%d\n", ret);
	return 0;
}

The above code can complete the task, and the efficiency is better than the recursive method.
Insert image description hereIn fact, many of the problems we see are explained in recursive form, simply because it is clearer than the non-recursive form,
But Iterative implementations of these problems are often more efficient than recursive implementations.
When a problem is too complex to be implemented iteratively, the simplicity of the recursive implementation can compensate for the runtime overhead it brings.

3.2 Find the nth Fibonacci number

We can also give more extreme examples, such as calculating the nth Fibonacci number, which is not suitable for recursive solution, but the problem of Fibonacci numbers is usually described in the form of recursion, as follows :
Insert image description here
Seeing this formula, it is easy to think that this is not simple. Let’s start the code recursively, such as:

int Fib(int n)
{
    
    
 	if(n<=2)
 	return 1;
 	else
 	return Fib(n-1)+Fib(n-2);
}

When we input 50, the cursor is still blinking and it takes a long time to calculate the result. The time spent on this calculation is difficult for us to accept. This also shows that the recursive writing method is very inefficient. Why?Insert image description here

At this time, the program does not stop, but continues to calculate. We canCtrl+Shift+Esc open the task manager, and we can see that the CPU share of our program is 13.7% (this 13.7 % is not the highest), (because after the code is run, the computer's fan will spin up and the CPU will directly work. The blogger's computer cannot take screenshots immediately, so the screenshots will not reach the desired high CPU running percentage. I recommend that you also You can try it)
Insert image description here
In fact, the recursive program will continue to expand. During the expansion process, we can easily find that there will be repeated calculations during the recursive process, and the deeper the recursive level. , the more redundant calculations will be.
Insert image description hereInsert image description here

Here we find that when calculating the 40th Fibonacci number, using the recursive method will cause the third Fibonacci number to be recalculated 39088169 times. These calculations are veryredundancy. Therefore, it is very unwise to use recursion to calculate Fibonacci numbers, and we should consider using iteration to solve it.
We know that the first two numbers of Fibonacci numbers are both 1, and then the sum of the first two numbers is the third number, so we can calculate from front to back, from small to large. .

这样就有下⾯的代码
int Fib(int n)
{
    
    
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2)
	{
    
    
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}

Summarize

Although recursion is good, it can also cause some problems, so we must not be obsessed with recursion, as long as it is appropriate.
Choice of recursion and looping:
1. If it is very easy to write code using recursion and the code written is no problem, then use recursion.
2. If the problem written recursively has obvious flaws, then recursion cannot be used and must be dealt with iteratively.

Guess you like

Origin blog.csdn.net/a_hong_sen/article/details/134274705