枚举n以内的素数(枚举+埃氏筛+线性筛+奇数筛)

统计[2,n)区间内的素数个数。

1.枚举

[2,n) 从2开始遍历,一个一个数去判断是否是素数。

#include <bits/stdc++.h> 
using namespace std;
const int N = 1e5+5;
int prime[N];		//prime数组存放素数 
int isPrime(int n)	//判断n是否为素数,是素数返回1,不是返回0
{
	for(int i=2;i*i<=n;i++){
		if(n % i == 0) return 0;
	}
	return 1;
}
int main() 
{
	int n;
	cin>>n;
	int res = 0;
	for(int i=2;i<n;i++){
		if(isPrime(i)) prime[res++] = i;
	}
	//输出素数 
	cout<<res<<'\n';
	for(int i=0;i<res;i++) cout<<prime[i]<<" ";	
    return 0;
}

时间复杂度:O(n√n)

2.埃氏筛

[2,n) 从2开始遍历,2为素数,记下,并将2的所有倍数划去;接下来3为素数,记下,并将3的所有倍数划去;4是2的倍数,已经被划去,所以接下来是5,5是素数记下,并将5的所有倍数划去;以此类推直到n。

有个动图可以很直观地理解这个筛法。

 

#include <bits/stdc++.h> 
using namespace std;
const int N = 1e5+5;
int prime[N];	//prime数组记录状态,1为素数,0不是素数
int res = 0;	//res记录素数个数 
int main() 
{
	int n;
	cin>>n;
	memset(prime,1,sizeof(prime));	//初始化prime数组全部为1
	for(int i=2;i<n;i++){
		if(prime[i]){
			res++;
			for(int j=2*i;j<n;j+=i) prime[j] = 0;	//i的所有倍数都不是素数,状态更新为0
		}
	}
	//输出素数 
	cout<<res<<'\n';
	for(int i=2;i<n;i++){
		if(prime[i]) cout<<i<<" ";
	}
    return 0;
}

时间复杂度:O(n㏒n㏒n)

3.线性筛

在埃氏筛的遍历过程中,会出现重复标记的情况。例如30既是2的倍数也是3的倍速还是5的倍数,那么在遍历2的所有倍数时会被标记一次,在遍历3的倍数时又会被标记一次,在遍历5的倍数时又会被标记一次。可以优化埃氏筛,但还是会有被重复标记的数,而线性筛可以实现完全无重复的解法。

每一个合数都可以以唯一形式被写成质数的乘积。也就是说,一个合数可以表示成一个质数和一个数的乘积。

所以if(i % prime[j] == 0) break;

举个栗子,i=8,i % 2 == 0,所以对于i=8就只需要检查prime[1]=2即可,因为对于大于prime[1]的质数,例如3,有:8 * 3 = 2 * 4 * 3 = 12 * 2,也就是说24(8 * 3 = 24)并不需要在8时检查,在12时才检查。

参考https://blog.csdn.net/leolin_/article/details/6642126

#include <bits/stdc++.h> 
using namespace std;
const int N = 1e5+5;
int prime[N];	//prime数组存放素数
int res = 0;	//res记录素数个数 
int vis[N];		//0为素数,1不是素数 
int main() 
{
	int n;
	cin>>n;
	for(int i=2;i<n;i++){
		if(!vis[i]) prime[res++] = i;	//素数存入数组 
		for(int j=0;j<res&&i*prime[j]<n;j++){
			vis[i*prime[j]] = 1;
			if(i % prime[j] == 0) break;	//遇到i最小的素数因子 
		}
	}
	cout<<res<<'\n';
	for(int i=0;i<res;i++){
		cout<<prime[i]<<" ";
	}
//	for(int i=2;i<n;i++){
//		if(!vis[i]) cout<<i<<" ";
//	}
    return 0;
}

时间复杂度:O(n)

4.奇数筛

已知偶数不一定是质数,而质数一定是奇数,所以只需要在奇数范围内标记出合数,剩下的奇数便都是质数了。

另外,奇数 * 奇数 = 奇数,奇数 * 偶数 = 偶数。

根据上述条件,可以在已经有的埃氏筛的基础上进行优化。可以在遍历时选择i+=2避开偶数,也可以直接将状态数组中偶数全部改为0。

#include <bits/stdc++.h> 
using namespace std;
const int N = 1e5+5;
int prime[N];	//prime数组记录状态,1为素数,0不是素数
int res = 0;	//res记录素数个数 
int main() 
{
	int n;
	cin>>n;
//	memset(prime,0,sizeof(prime));
	prime[2] = 1; 	
	for(int i=3;i<n;i++){
		if(i%2 == 1) prime[i] = 1;	//奇数标记为1 
		else prime[i] = 0;			//偶数标记为0 
	}
	for(int i=2;i<n;i++){
		if(prime[i]){
			res++;
			for(int j=2*i;j<=n;j+=i) prime[j] = 0;
		}
	}
	//输出素数 
	cout<<res<<'\n';
	for(int i=2;i<n;i++){
		if(prime[i]) cout<<i<<" ";
	}
    return 0;
}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值