质数筛选

质数筛选 \color{red}{\huge{质数筛选}} 质数筛选

质数筛选问题 质数筛选问题 质数筛选问题

首先明白质数筛选问题是什么?
质数筛选问题就是:给定一个 n n n,求 1 − n 1-n 1n中质数的个数。

解决方法 解决方法 解决方法

一般解决质数筛选问题有三种方法,它们的效率是依此提高的,当然优化的思路也是从低到高的:

  1. 朴素筛选法
  2. 埃氏筛选
  3. 现行筛选

①. 朴素筛选 O ( n l o g n ) O(nlogn) O(nlogn)

思路: \color{blue}{思路:} 思路:一个数不论它自己本身是不是质数,它的倍数一定是合数,所以对于一个数,将它的倍数都打上标记表示已经被筛除。经过对所有数的操作,最后剩下的一定是都是质数( 质数定义 \red{质数定义} 质数定义)。

代码实现: \color{olive}{代码实现:} 代码实现:

void get_primes_plain(int n)		//朴素版质数筛
{
	for(int i = 2 ; i <= n ; i++)		//遍历所有的数
	{
		if(st[i] == false)				//如果当前遍历的数没有被删除,说明它是质数
			primes[cnt++] = i;			//将其加入到primes数组中
		for(int j = i + i ; j <= n ; j += i)		//遍历当前数的所有倍数并进行删除
			st[j] = true;
	}
}

②. 埃氏筛选

思路: \color{blue}{思路:} 思路:
由朴素筛选法所想到的…?
为什么朴素筛选法比较慢呢? 朴素筛选法是用所有的数的倍数进行筛除 \purple{朴素筛选法是用所有的数的倍数进行筛除} 朴素筛选法是用所有的数的倍数进行筛除
有必要用所有的数来进行筛除吗?或者更近一步,有必要用 合数 \purple{合数} 合数的倍数去进行筛除吗?
没有必要 ! \purple{\huge{没有必要~!}} 没有必要 
因为每个合数都可以最后拆分成质数的乘积,最开始的质数有 2 2 2 3 3 3,它们在自己的倍数筛除的时候,已经把相应的合数晒出了,其他的质数也依此类推。
所以埃氏筛选法的核心思想就是:
每次进行筛除的时候,都只用质数进行筛除!! \red{{每次进行筛除的时候,都只用质数进行筛除!!}} 每次进行筛除的时候,都只用质数进行筛除!!

代码实现: \color{olive}{代码实现:} 代码实现:

void get_primes_Erichsen(int n)
{
	for(int i = 2 ; i <= n ; i++)
	{
		if(st[i] == false)			//如果当前遍历的数是质数
		{
			primes[cnt++] = i;			//首先加入到primes数组中
			for(int j = i + i ;  j  <= n ; j += i)	//筛选操作放入到了判断质数之中,只用质数进行筛除
				st[j] = true;
		}
	}
}

③. 线性筛选

思路: \color{blue}{思路:} 思路:
埃氏筛选法已经很优化了,直接优化掉了一大部分数的重复筛选。那么还可以进行优化吗?
给个情景
2 2 2在进行筛选的时候,将 12 12 12筛除了。 3 3 3进行筛选的时候,又将 12 12 12筛除了。
所以埃氏筛选法的优化关键就是 让每个数都确保被它的最小质因子筛除! ! \red{{让每个数都确保被它的最小质因子筛除!!}} 让每个数都确保被它的最小质因子筛除!!(这也是线性筛选法的思路)

那么如何保证线性筛选法是如何保证每个数都被自己最小的质因数筛除的呢?

首先有个基础很重要: i i i p r i m e s [ N ] primes[N] primes[N]中的质数都按照 从小到大 \color{purple}{从小到大} 从小到大顺序进行遍历的。

接下来根据 i i i p r i m e s [ j ] primes[j] primes[j]之间的关系,可以分为两种情况:

  1. 如果i % prime[j] != 0,代表prime[j]还不是i的最小质因子,或者说i的最小质因子大于prime[j],那么prime[j]就是i * prime[j]的最小质因子,i * prime[j]这个数就被筛除了,并且删除的时候就是被它的最小质因子prime[j]删除的。
  2. 如果i % prime[j] == 0,那就是已经找到了i的最小质因子了,就是prime[j]。对于i * prime[j + k](k >0)这个数的最小质因子就不是prime[j+k]而是prime[j]了。i * prime[j + k]应该被prime[j]筛掉,所以在此时立刻break,避免后续的prime[j + k]又筛了一次,筛重了。

代码实现: \color{olive}{代码实现:} 代码实现:

void get_primes(int n)
{
	for(int i = 2 ; i <= n ; i++)
	{
		if(st[i] == false)
			primes[cnt++] = i;
		for(int j = 0 ; primes[j] <= n / i ; j++)
		{
			st[primes[j] * i] = true;
			if(i % primes[j] == 0)
				break;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值