Prime number sieve must learn algorithm for c++ entry

1. What is a prime number sieve

The prime number sieve is also called the prime number sieve. It is an optimization algorithm for finding prime numbers within 1 to n. There are two types of prime number sieves, the Esperanto sieve and the Euler sieve.

The time complexity of the Esperanto sieve is close to O(n*logn), while the Euler sieve can reduce the complexity to O(n). Let’s see how the two algorithms are optimized step by step.

2. Enumeration of violence

Violence method solves complexity O(n)* n \sqrt{n}n , is a must-learn algorithm for novices, and can solve the prime number judgment of small data

1. Basic idea of ​​violent enumeration:

Enumerate each number from 1 to n, and judge whether each number is a prime number. The definition of a prime number is a number that can only be divisible by 1 and itself, such as 2, 3, 5, 7, 11, 13, etc. For each number, we only need to judge this definition

2. Template code

#include<iostream>
#include<cmath>//需要用到sqrt()函数 
using namespace std;

//判断素数
bool prime(int val){
    
    
//     1-3的值需要特判,因为我们循环判断只判断到sqrt(val),1-3是判断不了的
    if(val==1)return 0;
    if(val==2||val==3)return 1;
//     为什么只要判断到sqrt(val)?
//     例如一个数8,它的因数有1、2、4、8,可以发现1<2<sqrt(8)<4<8
//     所以我们只要判断小于sqrt(8)的数就可以了,因为因数都是成对出现的
    for(int i=2;i<=sqrt(val);i++){
    
    
//         如果被整除了,那么该数一定不是素数,返回0
        if(val%i==0)return 0;
    }
//     如果是素数就返回1
    return 1;
}

int main(){
    
    
    int n;
    cin>>n;
    for(int i=2;i<=n;i++){
    
    
//         如果返回值是1,那么该数是素数,输出即可
        if(prime(i)==1){
    
    
            cout<<i<<' ';
        }
    }
}

3. Running results

输入:
100
输出:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

3. Esperanto sieve

The complexity of the Sieve of Elsieve is close to O(n)* log ⁡ n \log nlogn , is an algorithm that is relatively easy to understand and learn

1. Basic idea of ​​Essieve sieve:

If a number is prime, its multiples are not prime.

For example:
2 is a prime number, then 4, 6, 8, 10, 12... are not prime numbers

3 is a prime number, then 6, 9, 12, 15, 18... are not prime numbers

4 is not a prime number. We already know that 4 is not a prime number at the time of 2. We do not need to repeat that 8, 12, 16...etc. are not prime numbers, because these numbers can already prove that they are not prime numbers at the time of 2.

5 is not marked by 2, 3, and 4 as it is not a prime number, then it means that 5 cannot be divisible by 2, 3, and 4, which means that 5 is a prime number

6 was marked as not a prime number when it was 3, skip it directly

7 is the same as 5, it is not marked by 2, 3, 4, 5, 6 as not a prime number, then 7 is a prime number, and 14, 21, 28... are not prime numbers

The above is the basic idea of ​​the Esperanto sieve. We can quickly find all the prime numbers from 1 to n by simulating the above process.

2. Template code

#include<iostream>
using namespace std;
int main(){
    
    
//	  f[i]=1代表i不是素数,f[i]=0代表i是素数 
	int f[100010]={
    
    1,1};
    int n;
    cin>>n;
//    埃氏筛 
    for(int i=2;i<=n;i++){
    
    
    	if(f[i]==1)continue;
    	for(int j=2;i*j<=n;j++){
    
    
    		f[i*j]=1;
		}
    }
//    打印 
    for(int i=0;i<=n;i++){
    
    
    	if(f[i]==0){
    
    
    		cout<<i<<' ';
		}
	}
}

3. Running results

输入:
100
输出:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

4. Euler sieve

Euler sieve is also called linear sieve, which can optimize the time complexity of the problem to O(n). It is the best algorithm to find the prime numbers in the range

1. Compared with Esperanto sieve

Why can't the complexity of the Escherite sieve reach O(n)? Because it has a lot of repetitive operations when the token is not prime.

For example:
12, it was marked 1 time at 2, 3
30, it was marked 1 time at 2, 3, 5

The ingenuity of the Euler sieve is that it removes duplicate tokens

2. The basic idea of ​​Euler sieve

Ensure that each number is marked only by the smallest prime factor other than 1

For example:
12, it has prime factors 2, 3, then it is only marked by 2
15, it has prime factors 3, 5, then it is only marked by 3
30, it has prime factors 2, 3, 5, then it is only marked by 2 marks

Realization:
(1) An array p[n] needs to be used to store the found prime numbers

(2) We need to think backwards to find the smallest prime factor from the largest factor, such as 12, we pass 6 to find the prime factor 2, that is, when we deal with 6, we need to mark 12 as not a prime number

(3) When processing a number, we traverse every existing prime number, and exit when the prime number is a factor of the number, because for the following numbers, the number is not the largest factor! This step is a bit difficult to understand, take your time and think about it a few times

The third point of understanding:
For example, the current number is 9, then the prime numbers 2, 3, 5, and 7 have been found at this time
to traverse these prime numbers, then 9*2=18 is not a prime number, 9*3=27 is not a prime number, 9* 5=45 is not a prime number, and 9*7=63 is not a prime number, but in fact we should quit after updating 27 instead of updating 45 and 63.

Because 9 is not the largest factor of 45, but 15 is, that is, this number will be marked when processing 15, and if it is marked now, there will be useless marks.

The same is true for 63, its largest factor is 21, that is, this number will be marked when processing 21.

Why when dealing with 9, the largest factor of 5 and 7 after 3 is not 9? And the largest factor before 3 and 3 is 9?

We can regard 3 as a bridge, because 3 is a factor of 9. When traversing 5 and 7, 9*5 can be split into 3*3*5=3*15, and 9*7 can be split into 3*3*7 =3*21, and 2 and 3 are inseparable, 2 9, 3 9, the largest factor is 9, their values ​​should be updated at this time to not be prime numbers

3. Template code

#include<iostream>
using namespace std;
int main(){
    
    
	int d=0;
	int p[100010]={
    
    0};
	int f[100010]={
    
    1,1};
	int n;
	cin>>n;
	for(int i=2;i<=n;i++){
    
    
		if(f[i]==0){
    
    //如果没被标记过,那么i是质数 
			p[d++]=i;
		}
		for(int j=0;j<d;j++){
    
    
			if(p[j]*i<=n){
    
    //标记以i为最大因数的数为不是素数(除了1和本身) 
				f[p[j]*i]=1;
			}
			if(i%p[j]==0){
    
    //如果p[j]是i的因数,那么后面的数都不是以i为最大因数的 
				break;
			}
		}
	}
	
	for(int i=0;i<d;i++){
    
    //打印1到n的质数 
		cout<<p[i]<<' ';
	}
	
} 

3. Running results

输入:
100
输出:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

V. Summary

The implementation principle of the Esperanto sieve is relatively simple, and it can be used in a wide range of scenarios. However, in some competition questions, this time will be delayed, and the Euler sieve must be used.

The process of understanding the Euler sieve is a bit difficult, but after the real understanding, the thinking will be very clear. If you don’t understand it, read it twice

Come on! brother cute

Guess you like

Origin blog.csdn.net/weixin_52115456/article/details/127816660