[C language] judge and output prime numbers (Trial Division)

Posted by skroks609 on Mon, 24 Jan 2022 18:09:28 +0100

This is a programming topic I like very much. Don't underestimate this problem. It seems simple, but it's actually mysterious. Since this is an introduction to C language, it only introduces the simplest and easiest way to think of: trial division. But even the trial division has changed a lot.

To understand trial division, you must first know what prime numbers are. In short (it may not be rigorous, but we only pay attention to ideas): prime numbers are prime numbers, that is, numbers that can only be divided by 1 and itself.

Therefore, the simplest and simplest method to judge the prime number was born. If you want to judge whether the number I is a prime number, you only need to divide I by the number between 2 ~ (i-1). If one of the numbers is divided by I, then I is not a prime number; On the contrary, if the numbers between 2 ~ (i-1) cannot be divided by I, it means that I is a prime number.

For example, when judging whether 7 is a prime number, we find that 2, 3, 4, 5 and 6 cannot be divided by 7, and come to the conclusion that 7 is a prime number. Or judge whether 15 is a prime number. It is found that 2 cannot be divided by 15, but try to find that 3 can be divided by 15, indicating that 15 is a composite number, not a prime number.

With these foreshadowing, it is easy to solve the following programming problem:

Print prime numbers between 100 and 200 and count the number

The complete code is as follows:

#include <stdio.h>

//Print prime numbers between 100 and 200 and count the number
int main()
{
	int i = 0;
	int count = 0;//Number of Statistics
	//Generate an integer between 100 and 200
	for (i = 100; i <= 200; i++)
	{
		int flag = 1;//Suppose i is prime
		//Judge whether i is a prime number
		//Take the number of 2~i-1 and try to divide i. if you divide it, it is not prime. If you don't divide it, it is prime
		//Number of generated 2~i-1
		int j = 0;
		for (j = 2; j < i; j++)
		{
			if (i % j == 0)
			{
				flag = 0;//Change j to 0 when i is not prime
				break;
			}
		}
		if (1 == flag)//Since i is not changed to 0, i is prime
		{
			count++;
			//Output prime
			printf("%d ", i);
		}
	}
	//Number of outputs
	printf("\ncount = %d\n", count);

	return 0;
}

Here, use the for loop to generate an integer between 100 and 200, and then generate a number between 2 ~ (i-1) to try to divide I. if it is divided, set the flag to 0. When the inner loop ends, judge the value of flag. If it is 1, it means that I is not divided by any j, it means that I is a prime number; If I is 0, it means that the middle I is divided by some j, so it means that I is not prime.

Do you have to use flag to judge whether it is a prime number? Not really. You can get rid of the flag by modifying the code a little

#include <stdio.h>

//Print prime numbers between 100 and 200 and count the number
int main()
{
	int i = 0;
	int count = 0;//Number of Statistics
	//Generate an integer between 100 and 200
	for (i = 100; i <= 200; i++)
	{
		//Judge whether i is a prime
		//Take the number of 2~i-1 and try to divide i. if you divide it, it is not prime. If you don't divide it, it is prime
		//Number of generated 2~i-1
		int j = 0;
		for (j = 2; j < i; j++)
		{
			if (i % j == 0)
			{
				break;
			}
		}
		if (i == j)//Judge whether I is a prime number. If the numbers between 2~(i-1) cannot be divided by I, j will jump out of the above cycle when it is the value of I
		{
			count++;
			//Output prime
			printf("%d ", i);
		}
	}
	//Number of outputs
	printf("\ncount = %d\n", count);

	return 0;
}

However, after removing the flag, the code is not so easy to understand, so I still prefer the version with flag. Next, we will optimize the version with flag to improve the efficiency of the code.

First of all, it's easy to think of that all even numbers are definitely not prime numbers, so you just need to generate all odd numbers.

#include <stdio.h>

int main()
{
	int i = 0;
	int count = 0;//Number of Statistics
	//Generate an integer between 100 and 200
	for (i = 101; i < 200; i += 2)//Even numbers are definitely not prime numbers, so you only need to generate odd numbers between 100 and 200
	{
		int flag = 1;//Suppose i is prime
		//Judge whether i is a prime number
		//Take the number of 2~i-1 and try to divide i. if you divide it, it is not prime. If you don't divide it, it is prime
		//Number of generated 2~i-1
		int j = 0;
		for (j = 2; j < i; j++)
		{
			if (i % j == 0)
			{
				flag = 0;//Change j to 0 when i is not prime
				break;
			}
		}
		if (1 == flag)//Since i is not changed to 0, i is prime
		{
			count++;
			//Output prime
			printf("%d ", i);
		}
	}
	//Number of outputs
	printf("\ncount = %d\n", count);

	return 0;
}

Then there's the point! First of all, there is the following theorem (also a less rigorous expression, just for ease of understanding): if a number m can be written in the form of a*b, at least one of a and b will be less than or equal to the arithmetic square root of M! (Theorem 1)

For example: 100 = 4 * 25 = 10 * 10

When written as 4 * 25, 4 is smaller than the arithmetic square root (10) of 100; When written as 10 * 10, both factors are 10, which is equal to the arithmetic square root (10) of 100.

The proof is very simple. Using the method of counter proof, assuming that both factors are greater than the arithmetic square root of M, then their product will also be greater than m, which is contradictory to their product equal to m! So the hypothesis doesn't hold, that is, at least one factor is less than or equal to M.

After a lot of talking, what does this root have to do with solving prime numbers? In fact, this theorem can reduce the number of division attempts! We tried to divide I with the number of 2 ~ (i-1), but in fact, we only need to try to divide I with the number of the arithmetic square root of 2~i.

A friend wondered again: why? Very simply, if a number between the arithmetic square roots of 2~i can be divided by i, it naturally shows that i is a composite number, not a prime number, which is easy to understand.

If the number between the arithmetic square root of 2~i cannot be divided by I (proposition 1), the number between the arithmetic square root of I and (i-1) cannot be divided by I (Proposition 2). The proof is as follows: still using the counter proof method, assuming that Proposition 2 is false on the premise of satisfying proposition 1, that is, the number between the arithmetic square root of I and (i-1) can be divided by I, that is, I can be decomposed into factors of two integers, then according to theorem 1, at least one factor is between the arithmetic square root of 2~i, that is, There must be at least one number between the arithmetic square roots of 2~i that can be divided by I, which is contrary to proposition 1! So the hypothesis is not true, that is, if proposition 1 is true, Proposition 2 must be true! Therefore, there is no need to try to divide all the numbers between 2 ~ (i-1), just take the numbers between the arithmetic square roots of 2~i to try to divide, which greatly saves the workload.

For example, to judge whether 101 is a prime number, you need to take the number between 2 and 100 to try division, but now you only need to take the number between the arithmetic square root (10:00) of 2 ~ 101 to try division, that is, take the number between 2 and 10 to try Division. The effect can be imagined. Moreover, each judgment of a number can save so much computation, and the overall efficiency is greatly improved.

The optimized code is as follows: (there is a detail. The above are only theoretical arguments. In fact, since we have removed all odd numbers from the source, we can start with 3 instead of 2)

#include <stdio.h>
#include <math.h>

//Print prime numbers between 100 and 200 and count the number
int main()
{
	int i = 0;
	int count = 0;//Number of Statistics
	//Generate an integer between 100 and 200
	for (i = 101; i < 200; i += 2)//Even numbers are definitely not prime numbers, so you only need to generate odd numbers between 100 and 200
	{
		int flag = 1;//Suppose i is prime
		//Judge whether i is a prime number
		//Take the number of 2~i-1 and try to divide i. if you divide it, it is not prime. If you don't divide it, it is prime
		//But in fact, you only need to take the number between the arithmetic square roots of 2~i to try to divide i
		//Generating the arithmetic square root of 2~i
		int j = 0;
		for (j = 3/*In fact, you can start from 3, because all even numbers have been removed from the source*/; j <= sqrt(i); j++)//sqrt here is a library function used to calculate the square root, and the header file is < math h>
		{
			if (i % j == 0)
			{
				flag = 0;//Change j to 0 when i is not prime
				break;
			}
		}
		if (1 == flag)//Since i is not changed to 0, i is prime
		{
			count++;
			//Output prime
			printf("%d ", i);
		}
	}
	//Number of outputs
	printf("\ncount = %d\n", count);

	return 0;
}

But there is still a lot of room for optimization in this code. For example, to judge whether 101 is a prime number, do you really need to divide 2, 3, 4, 5, 6, 7, 8, 9 and 10? Obviously, it doesn't need to be so troublesome. For example, 4, 6 and 8 are unnecessary. In other words, you only need to try to divide odd numbers, and even numbers are not needed. Continue to modify the code

#include <stdio.h>
#include <math.h>

//Print prime numbers between 100 and 200 and count the number
int main()
{
	int i = 0;
	int count = 0;//Number of Statistics
	//Generate an integer between 100 and 200
	for (i = 101; i < 200; i += 2)//Even numbers are definitely not prime numbers, so you only need to generate odd numbers between 100 and 200
	{
		int flag = 1;//Suppose i is prime
		//Judge whether i is a prime number
		//Take the number of 2~i-1 and try to divide i. if you divide it, it is not prime. If you don't divide it, it is prime
		//But in fact, you only need to take the number between the arithmetic square roots of 2~i to try to divide i
		//Generating the arithmetic square root of 2~i
		int j = 0;
		for (j = 3/*In fact, you can start from 3, because all even numbers have been removed from the source*/; j <= sqrt(i); j += 2/*Even numbers don't need to be divided here*/)//sqrt here is a library function used to calculate the square root, and the header file is < math h>
		{
			if (i % j == 0)
			{
				flag = 0;//Change j to 0 when i is not prime
				break;
			}
		}
		if (1 == flag)//Since i is not changed to 0, i is prime
		{
			count++;
			//Output prime
			printf("%d ", i);
		}
	}
	//Number of outputs
	printf("\ncount = %d\n", count);

	return 0;
}

But in essence, if a composite number is divided into several factors, it can be completely divided into prime factors, that is, just take prime numbers to try division. But in this case, the code will be greatly modified. The space is limited, and the code words are a little tired. Let's stop here for the time being.

Finally, trial division is the simplest, most basic and least efficient method of prime number judgment. Other methods, such as screening, will substantially improve the efficiency. I will introduce these in the blog later.

Thank you for seeing here. Please give me a free praise, thanks.

Topics: C Algorithm Back-end