【数论】质数筛选法(埃拉托色尼筛法及线性筛法)

质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

在编程中,有时我们会遇到要找出一个整数范围内所有质数的情况,如果该范围很大,逐个判定该范围内所有数是否为质数(找出它是否能被大于1小于自己的整数整除)就会浪费大量时间,造成TLE。

在这时,我们就需要质数筛选法,直接筛出一个整数范围内的所有质数。

1.埃拉托色尼筛法

时间复杂度 O(nlogn)

 希腊学者埃拉托色尼(公元前275-公元前193,第一个算出地球周长的人)提出的质数筛选法充分利用了质数的特性,非常好理解

基本原理:任意整数 x 的倍数 2x,3x,... 等都不是质数 。

例:找出1-50内所有的质数

第一步:2为质数,标记4,6,8,10……50为合数

第二步:3为质数,标记6,9,12,15……48为合数

第三步:4已被标记为合数,标记8,12,16……48为合数

第四步:5为质数,标记10,15,20……50为合数

(从小到大执行到某个数时,若它未被标记,它即为质数)

……

不断进行下去,我们就可以标记出1-50所有的质数和合数。十分简单,所以我不提供代码实现。

 

 2.线性筛法

时间复杂度 O(logn)

在上面的方法我们发现,有很多重复标记的现象。例如12既会被2又会被3标记,根本原因是没有找到唯一产生12的方式。

基本原理:每个合数必为它的最小质因子和一个最大因数(除它本身)的乘积。(质数的最小质因子为它本身,最大因数(除它本身)为1)

我们用这个最大因数(除它本身)把合数筛掉,避免重复。

也是从2开始筛选,每一步,将当前数与已经找出来的质数中小于等于它的最小质因子的相乘,把乘积作为合数筛掉。

例:找出1-50内所有的质数

第一步:2为质数,标记4为合数

第二步:3为质数,标记6为合数

第三步:4为合数,标记8为合数

第四步:5为质数,标记10,15为合数

(从小到大执行到某个数时,若它未被标记,它即为质数)

……

以下是线性筛法找质数的函数代码实现:

int prime[MAXN],ct=0; //存储找到的质数 
int v[MAXN];         //记录0~MAXN是否为质数 

void fprime(int b) //线性筛法找质数 
{
    for(int i=2;i<=b;i++)  //找到2到b内的所有质数 
    {
	if(v[i]==0)  //v[i]没被筛过,v[i]为质数
	prime[++ct]=i;  //储存第cnt个质数
	for(int j=1;j<=ct;j++) //遍历小于i的所有质数 
	{
		if(i*prime[j]>b) break; //无需判断比b大的数是质数还是偶数
		v[i*prime[j]] = 1;  //标记不为质数的数 
		if(i%prime[j]==0) break;  //当遇到最小的质数是i的因数时,break
	} 
    } 
}

点击进入:一道简单的例题 (题目来自洛谷)

推荐: 该篇也讲的很详细

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值