素数筛(筛选出一定范围内的所有素数)

鄙白第一篇博客,完完全全仅供纯白白借鉴,

如侵必删。

素数筛字面意思就是筛选某范围内(l,r)的素数嘛。

那怎么筛呢。其思想就是  把范围内的合数都筛去,不就只剩下素数了。而任何合数都可以表示为素数的乘积。因此,如果一个数是素数,那么这个素数的倍数一定是合数。上代码!

bool isprime[MAX];            //给数做标志,是素数则为1,不是则为0 
long long su[MAX];            //把素数都存入该数组,当然你也可以用STL容器存储 

void prime(){
	cnt=1;                    //用来计素数个数 
	memset(isprime,1,sizeof(isprime));     //先初始化所有数的标志都是1 
	isprime[0]=isprime[1]=0;               //0和1不是素数 
	for(long long i=2;i<=MAX;i++){
		if(isprime[i])                     //如果是素数,就保存到su[MAX]里
		{
		    su[cnt++]=i; 
		    for(long long j=i*2;j<=MAX;j+=i){  //该素数的倍数都是合数,标记为0 
			    isprime[j]=0;
		} 
	}
}

思路很清晰。但很明显,这个效率很低,因为会有重复计算。比如:当i=2时,j=i*2+2=6。而当i=3时,j=i*3=6;如此往后,庞大的数就会有庞大的重复计算量。

因此我们来优化一下:上述的筛选让每个数的倍数都循环到MAX一遍,这次我们只筛选小于i的素数与i的乘积,这样既不会重复筛选,也不会有遗漏,时间复杂度为线性的(第一次看可能不太懂为什么,但其实写个2,3,5就明白了)

即:欧拉筛   复杂度为O(n)!(重点重点,看起)

欧拉筛的思想:对于合数的筛出判断用唯一的条件,将合数分解为(他的最小质因子*另一个数)。以达到不重复的目的。

先上代码理解一下:

bool isprime[MAX]; 
long long su[MAX];
void prime(){
	cnt=0;
	memset(isprime,1,sizeof(isprime));
	isprime[0]=isprime[1]=0;   
	for(int i=2;i<MAX;i++){
		if(isprime[i])
			su[cnt++]=i;      //以上和普通筛的步骤一样 
		for(int j=0;j<cnt&&i*su[j]<MAX;j++){
			isprime[i*su[j]]=0;   
			if(i%su[j]==0)       //关键之处↓ 
			    break;	  //判断如果prime[j]是i的最小质因数了,那么不再往下循环
		}
	}
}

欧拉筛的关键之处在于 if(i%su[j]==0)的判断。

假设某合数m是su[j]的整数倍且su[j]是m的最小质因子,即m=k1*su[j]。那么m*su[j+1]=(k1*su[j+1])*su[j]=k2*su[j](此公式为理解欧拉筛的关键之处),即任意一个合数都可以表示成(其最小质因子和另一个合数的乘积)。

那么在for(int j=0;j<cnt&&i*su[j]<MAX;j++)循环中,当找到i1的最小质因数su[j]后,就没必要再将i1*su[j+1]标记为合数了,因为在循环i(for(int i=2;i<MAX;i++))的时候,一定会有另一个数i2su[j]相乘等于i1*su[j+1]。以此来达到不重复的原则。

(初次接触欧拉筛有些难以理解,举几个实数理解一下更方便,理解一天应属正常,我是这样安慰自己愚蠢的小脑袋的)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值