质数筛选总结_埃氏筛法、欧拉筛法

 目录

 概述

1 试除法

2 试除法优化

3 埃氏筛法

4 欧拉筛法


概述

质数:只有两个正因数(1和它本身)的自然数即为质数。

合数:比1大但不是质数的数称为合数。

10既非质数也非合数。

问题描述:求 1,2, , N中素数的个数。

输入:一行一个整数 N

输出:一行一个整数,表示素数的个数

1 试除法

给定的整数n,除以1<i<n的数,如果都不能整除,则n为质数。10个测试用例全部超时

#include <bits/stdc++.h>
using namespace std;
int n ,sum=0,flag=0;
int main(){
	cin >> n;
	for(int i=2;i<=n;i++)
	{
		flag=0;
		for(int j=2;j<i;j++)
		{
			if (i%j==0)
			{
				flag=1;
			}
		}
		if(flag==0)
		{
			sum++;
		}
		
	}
	cout <<sum;
	return 0;
}

2 试除法优化

对于所有的整数,因数是成对出现的a*b=n,当a,b无限接近时a<= sqrt(n)。因此,枚举范围可以缩小至2~sqrt(n)10个测试用例通过2,其它超时。

#include <bits/stdc++.h>
using namespace std;
int n ,sum=0,flag=0;
int main(){
	cin >> n;
	
	for(int i=2;i<=n;i++)
	{
		flag=0;
		for(int j=2;j<=sqrt(i);j++)
		{
			if (i%j==0)
			{
				flag=1;
			}
		}
		if(flag==0)
		{
			sum+=1;
		
		}
	}
	cout <<sum;
	return 0;
}

3 埃氏筛法

埃氏筛法对多个整数进行质数判断时,算法更优。原理首先找出最小的素数2,把2的倍数都标记为合数,然后再找下一个没有被标记为合数的数3,把3的倍数标记为合数,依次类推。假设多个整数中的最大值为20,算法实现过程:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

2

2

3

-

5

-

7

-

9

-

11

-

13

-

15

-

17

-

19

-

3

2

3

-

5

-

7

-

-

-

11

-

13

-

-

-

17

-

19

-

5

2

3

-

5

-

7

-

-

-

11

-

13

-

-

-

17

-

19

-

7

2

3

-

5

-

7

-

-

-

11

-

13

-

-

-

17

-

19

-

  1. 遍历2~20所有整数数组a[i] (2<=i<=20),默认值a[i]=1;
  2. 最小整数i=2是质数,所有2的倍数均为不是质数,都划掉a[i]=0
  3. i=3,如果a[3]=1,所有3的倍数均不是质数,都划掉a[i]=1
  4. i=4,因为a[4]=04不是质数,继续下一个
  5. i=5,以为a[5]=1,所有5的倍数均不是质数,都划掉a[i]=1;
  6. 最后,剩余的a[i]=0的数,均为质数。

10个测试用例通过8,其它超时

#include <bits/stdc++.h>
using namespace std;
int n ,sum=0,a[100000001];//a[i]=0是素数,a[i]=1不是素数。 

int main(){
	cin >> n;
	a[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(a[i]==0)
		{
			for(int j=2;j<=n;j++)
			{
				if(i*j>n) break;
				a[i*j]=1;
			}
		}
		
	}
	for(int i=2;i<=n;i++)
	{
		if(a[i]==0)
		{
			sum++;
		}
	}
	cout <<sum;
	return 0;
}

4 欧拉筛法

欧拉筛法是在埃式筛法的基础做了改进。埃氏筛法中一个合数可能会被筛多次,如6可以被2筛去也可以被3筛去,而欧拉筛法是让每个合数只被它的最小质因子筛选一次

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

2

1

3

1

1

4

1

5

1

1

6

1

7

1

创建一个额外数组prime存储素数,然后从2开始依次遍历2~n区间内的数:

i=2时:

判断素数:a[2]未被标记为合数=>a[2]=true标记为素数,prime数组增加一个素数。prime[0]=0 ++prime[0] 后,prime[0]=1,prime[1]=2;

判断合数:遍历prime数组的所有素数

j=1a[prime[1]*2]=a[4]=true;

i=3时:

判断素数:a[3]未被标记为合数=>a[3]=true标记为素数,prime数组增加一个素数。prime[0]=1 ++prime[0] 后,prime[0]=2,prime[2]=3;

判断合数:遍历prime数组的所有素数

j=1时,a[prime[1]*3]=a[6]=true;

j=2时,a[prime[2]*3]=a[9]=true;

因为 3%prime[2]=3%3=0,所以跳出循环。

i=4时,

判断素数:a[4]被标记为合数,prime数组不增加;

判断合数:遍历prime数组的所有素数

j=1时,a[prime[1]*4]=a[8]=true;

因为 4%prime[1]=4%2=0,所以跳出循环。

注:如果不跳出循环,因为i%prime[j]=0,故i=tprime[j],所以i*prime[j+1]=t * prime[j] * prime[j+1],即当j=2时: prime[2]*4=2*prime[1]*prime[2] =12;相当于prime[j]的倍数已经标记,prime[j+1]就是重复标记了。

10个测试用例全部通过

#include <bits/stdc++.h>
using namespace std;
int n ,sum=0,prime[1000000];
bool a[100000001];//a[i]=0没有访问过,1访问过。prime存素数 
int main(){
	cin >> n;
	memset(prime,0,sizeof(prime));
	memset(a,0,sizeof(a));
	a[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!a[i]) // 没有被标记为合数 
		{
			a[i]=true;
			//i=2时,prime[0]=0 ,++prime[0] 后,prime[0]=1,prime[1]=2; 
			//i=3时,prime[0]=1 ,++prime[0] 后,prime[0]=2,prime[2]=3;
			//i=5时,prime[0]=2 ,++prime[0] 后,prime[0]=3,prime[3]=5; 
			prime[++prime[0]]=i; 
			 
		}
		for(int j=1 ;j<=prime[0] && i*prime[j]<=n;j++)
		{
			a[prime[j]*i]=true;//合数
			if(i%prime[j]==0) break; 
		}
		
	}
	cout <<prime[0];
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值