Algorithms: Recursion

Keywords: less

1: recursion

Basic Concept: A function calls itself, which is recursion.

For example, the recursion of the factorial of n:

#include<iostream>
using namespace std;

int factorial(int n) {
	if (n == 1) 
		return 1;
	else 
		return n * factorial(n - 1);
}

int main() {
	int n;
	cin >> n;
	factorial(n);
	return 0;
}

Here we use the concept of recursion in finding the factorial of n. If n=3, then the function first calls factorial(3), pushes the function call into the stack, continues to call factorial (2), continues to press into the stack, calls factoral (1), factoral (1) is at the top of the stack at this time, because when n==1, the function returns to 1, so after executing factoral (1), the function call into the stack will be popped up first, then executing factorial (2), and then continue to pop up. Until factorial(3) is executed, all function calls are popped up and the function is executed, so the recursive process is the process of stacking out.

The role of recursion:

  • Alternative multiple loops
  • Solve the problem that was originally defined in recursive form
  • Decomposition of the problem into smaller sub-problems

As you can see, the role of recursion is somewhat similar to that of loops, but recursion may, in some ways, make the solution clearer. In fact, it has no performance advantage. In fact, in some cases, the performance of using loops is better. To sum up, borrow a bull's words: using loops may make your program perform better, but using recursion may then make your program seem easier to understand.

II: Full Arrangement Problem

Basic idea: This problem is a permutation and combination problem, mainly using recursion to solve, because the number of characters is not more than 6. Therefore, we can use an array of characters to store changes in different combinations. In addition, in order to output different permutations, we need to set up an access flag bit. The specific procedures are as follows:

It is important to note that recursive conditions need to include two: baseline conditions and recursive conditions. Recursive conditions allow your program to decompose the problem step by step until it is decomposed into a minimum-scale solution problem. The baseline condition is to prevent the program from infinitely decomposing, that is, falling into a dead cycle. So this condition can also be called boundary conditions.

#include<iostream>
#include<string>
#include<cstdio>

using namespace std;

string s1;		//Input string
char s2[6];		//Character combinations used to store constantly adjusted characters
int visit[6] = {0};		//A flag bit that determines whether a bit of a string is accessed
int len;		//Length of strings

void out_result(int steps) {
	if (steps == len) {
		cout << s2 << endl;		//Output of character combinations in a case
		return;
	}
	for (int i = 0; i < len; i++) {
		if (visit[i] == 0) {
			visit[i] = 1;		//Avoid the next recursion to execute judgments in the corresponding loop
			s2[steps] = s1[i];
			out_result(steps + 1);
			visit[i] = 0;
		}
	}
}

int main() {
	
	cin >> s1;
	len = s1.size();
	out_result(0);
	system("pause");
	return 0;

}

Execute the program, input the test sample, print the results as follows:

The Power Problem of 3:2

Basic idea: We can see from the weight that when the power is less than 4, it can be regarded as a special case, that is, the recursive boundary condition. With the boundary condition, the program can be recursive.

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int power_func(int x) {
	if (x > 4) {
		int max_power = 1;
		while (pow(2, max_power) <= x)
			max_power++;				//Get the maximum number of powers greater than x
		cout << "2(";
		power_func(max_power - 1);		//Maximum power times of recursion less than 2 x
		cout << ')';   
		if (x != pow(2,max_power - 1)) {
			cout << '+';				//If the power of 2 is not equal to x, a plus sign is output.
		}
		power_func(x - pow(2,max_power - 1));		//Continue to recurse x-the largest power of the first round less than x
	}
	else {
		switch (x)
		{
			case 0:return 0;
			case 1:cout << "2(0)"; break;
			case 2:cout << "2"; break;
			case 3:cout << "2+2(0)"; break;
			case 4:cout << "2(2)"; break;
		}
	}
}
int main() {
	int x;
	cin >> x;
	power_func(x);
	//system("pause");
	return 0;
}

Enter the test data and get the following results:

IV: Boolean Expressions

The basic idea: This problem is similar to the idea of four arithmetic expressions. It also divides expressions into terms and factors for recursion.

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>


using namespace std;
char s[10000] = { 0 };
bool expression_value();
bool term_value();
bool factor_value();

int my_cent;
bool term_value() {		//Term expression
	bool result;
	char op = s[my_cent];
	if (op == '!') {
		my_cent++;
		result = !factor_value();
	}
	else {
		result = factor_value();
	}
	return result;
}
bool factor_value() {		//Factor expression
	bool result;
	char o = s[my_cent];
	if (o == '(') {
		my_cent++;
		result = expression_value();		//If the character is in parentheses, continue to recurse the expressions in parentheses
		my_cent++;
	}
	else if (o == 'V') {
		result = true;
		my_cent++;
	}
	else if (o == 'F') {
		result = false;
		my_cent++;
	}
	else if (o == '!') {
		result = term_value();
	}
	return result;
}
bool expression_value() {
	bool result = term_value();
	bool more = true;
	while (more) {
		char op = s[my_cent];		//Look at the first character, don't take it away
		if (op == '&' || op == '|') {
			my_cent++;		//Remove the first character
			bool value = term_value();
			if (op == '&')
				result = result & value;
			else
				result = result | value;
		}
		else {
			more = false;
		}
	}
	return result;
}

int main(){
	int k = 0;
	while (cin.getline(s,10000)) {
		char t[10000] = { 0 };
		int len = strlen(s);
		int n = 0;
		for (int i = 0; i < len; i++) {
			if (s[i] != ' ') {
				t[n++] = s[i];
			}
		}
		//cout << k;
		len = strlen(t);
		for (int i = 0; i < len; i++) {
			s[i] = t[i];
		}
		s[len] = '\0';  //At this point, the space in the input character is removed
		cout << "Expression " << ++k << ": " << (expression_value() ? 'V' : 'F') << endl;
		my_cent = 0;
		memset(s, 0, 10000);
	}
	system("pause");
	return 0;
}

Five: Simple Integer Partition

Basic idea: If the maximum value of {m1,m2,...,mi} does not exceed m, that is, Max (m1,m2,...,mi)<=m, it belongs to an M division of n. Here we note that the number of M partitions of n is f(n,m); for example, when n=4, he has five partitions, {4}, {3, 1}, {2, 2}, {2, 1, 1}, {1, 1, 1}; note that 4 = 1 + 3 and 4 = 3 + 1 are considered to be the same partition. The problem is to find all the partitions of n, i. e. f(n, n). Next, we consider the method of finding f(n,m).

(1) When n=1, no matter what the value of M is (m > 0), only one division is {1};

(2) When m=1, no matter what the value of n is, there is only one kind of division, i.e. n 1, {1, 1, 1,..., 1};

(3) When n=m, it can be divided into two situations according to whether n is included in the partition:

(a) If n is included in the partition, only one case is {n};

(b) When n is not included in the partition, the largest number in the partition must be smaller than n, that is, all (n-1) partitions of n.

f(n,n) =1 + f(n,n-1);

(4) when n < m, it is equivalent to f(n,n) because there is no negative number in the division.

(5) When n > m, there are two cases according to whether the maximum m is included in the partition.

(a) The case where the partition contains m, i.e. {m, {x1,x2,...xi}, where the sum of {x1,x2,... xi} is n-m, so in this case

f(n-m,m)

(b) If m is not included in the partition, then all the values in the partition are smaller than m, that is, the number of partitions of n (m-1) is f(n,m-1);

So f(n, m) = f(n-m, m)+f(n,m-1);

#include<iostream>
using namespace std;

int equationCount(int n, int m)
{
	if (n == 1 || m == 1)
		return 1;
	else if (n < m)
		return equationCount(n, n);
	else if (n == m)
		return 1 + equationCount(n, n - 1);
	else
		return equationCount(n, m - 1) + equationCount(n - m, m);
}

int main()
{
	int n;
	while (scanf_s("%d", &n) != EOF && (n >= 1 && n <= 120))
	{
		printf("%d\n", equationCount(n, n));
	}
	return 0;
}

Summary:

Recursive advantages:

1. succinct

2. Recursion is much simpler than loops in tree traversal algorithms of preface, middle and postorder.

Recursive drawbacks:

1. Recursion is the function call itself, and function calls consume time and space: every function call needs to allocate space in the memory stack to save parameters, return addresses and temporary variables, while it takes time to push data and pop-up data into the stack. > efficiency

2. Many computations in recursion are repetitive. Because the essence of recursion is to decompose a problem into two or more small problems, where there are overlapping parts of many small problems, there are repetitive computations, such as the recursive implementation of fibonacci Fibonacci sequence. > efficiency

3. Call stack may overflow. In fact, every function call allocates space in the memory stack, and the stack capacity of each process is limited. When there are too many levels of call, the stack capacity will exceed, leading to stack overflow. > performance

Posted by AceE on Sat, 27 Apr 2019 00:42:35 -0700