三种素数筛的总结和思考

1 篇文章 0 订阅
1 篇文章 0 订阅

素数筛

我们在判断一个数是否是素数时,通常会写一个函数来判断

bool isprime(int x){
	int m = sqrt(x);
	for(int i = 2; i <= m; i++){
		if(x % i == 0) return false;
	}
	return true;
}

当我要求几个数是否是素数时,这样的做法确实比较方便。

但是,当我们要求一定范围内的数,有多少是素数,一个一个判断就显得效率有些低下。

这个时候就要用到素数筛。0.

Eratosthenes素数筛(又称埃氏筛法)的基本原理:

对于不超过n的每个正整数p,删除2p,3p,4p…,当处理完所有的数据之后,没有删除的数据就是素数。

代码层面,我们可以初始化一个vis数组为0,表示所有的数都为素数,当这个数被删除之后,就将对应的vis[i]置为1,表示这个数不是素数。 换言之,vis数组为0的元素是素数。

void Prime(int n){
    memset(vis,0,sizeof(vis));
	for(int i = 2; i <= n; i++)
		for(int j = 2*i; j <= n; j += i) vis[j] = 1;
}

虽然这样做的时间复杂度已经很高了,但是还可以继续优化。

首先是第一重循环,我们在求取数组中的素数时,可以将n开方,到sqrt(n)时,所有的非素数都已经筛选完毕。

因为素数的倍数不是素数,所以我们可以将p限定为素数

最后j循环也不必从2i开始,因为2i已经在2的时候被筛掉了,3,4,5同理,所以将其改为i的平方

所以最后的模板为:

void Prime(int n){
    int m = sqrt(n+0.5);
	memset(vis,0,sizeof(vis));
	for(int i = 2; i <= m; i++)
		if(!vis[i]) for(int j = i*i; j <= n; j += i) vis[j] = 1;
}

到这里,Eratosthenes素数筛的效率进一步被优化,但是我们可以看到,由于一个数可能有很多因数,所以也就会被筛到很多次,不经意间做了很多无用功。

所以这里引出最强素数筛:

线性筛

基本思路:在筛法的基础上 我们让每一个合数只能被他自己最小素因子筛到一次

在筛i*prime[j]的时候,他的最小素因子就是prime[j],也就是在第一次满足于i % prime[j]为0的时候,将这个数筛掉,就可以保证所有的合数只筛一次。

也可以这样说,因为素数数组的值是递增的,所以在i整除prime[j]时,prime[j+1]一定会被后面的某个数筛掉

举个例子:当i = 5,prime[j] = 5,筛掉25,prime[j+1] = 7时的35会被在i = 7,prime[j] = 5的时候被筛掉

线性筛的模板如下:

int Prime(int n){
	int cnt = 0;
	for(int i = 2; i < n; i++){
		if(!vis[i]) prime[cnt++] = i;
		for(int j = 0; j < cnt && i * prime[j] < n; j++){
			vis[i*prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
	return cnt;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值