C++算法——质数筛法

一、引入

说到质数,你想到了什么?我们很容易判断一个数是不是质数,我们只要从2到 n \sqrt{n} n 遍历,如果有n的因数就返回false,否则返回true。本文不是介绍质数判断,而是介绍在一个区间内得到所有质数的方法——质数筛法1,这里着重介绍质数筛法当中的埃氏筛法和欧拉筛法。

二、最朴素也最慢的方法——逐个判断

简介

得到一个区间内所有的质数,最简单、朴素的方法应该就是遍历每个数,判断是否是质数了。这种方法的代码简单,但时间复杂度较高,适合于小数据量。就是文章开头注释中提到的方法。

程序

出于完整性的考虑,还是将这种方法的程序列出在下方了。给出的代码只是找到n以下的质数。注意,由于数组大小所限,这段代码的n必须 ≤ 1000 \leq1000 1000

bool isPrime(int n){
		if(n<=1){
				return false;
		}
    for(int i=2;i*i<=n;++i){
        if(n%i==0){
            return false;
        }
    }
    return true;
}
bool Prime[1000]={};
void SM(int n){
    for(int i=2;i<=n;++i){
        Prime[i]=isPrime(i);
    }
}

三、埃氏筛法

简介

埃氏筛法是从2开始,“划掉”所有非被标记的数的倍数,直到遍历结束。这样就得到了一个数组 a a a a i a_i ai就是 i i i是否是质数的标记。

过程

从二开始,到n结束。如果 a i a_i ai不是质数,就跳过,否则用循环将 i i i的倍数标记为非质数。这里其实有两种写法。片段如下:
写法一:

for(int j=2;i*j<=n;++j){
	Prime[i*j]=1;
}

写法二:

for(int j=i*2;j<=n;j+=i){
	Prime[j]=1;
}

程序2

void SieveE(int n){
		for(int i=0;i<=n;++i) Prime[i]=0;
    for(int i=2;i<=n;++i){
        if(Prime[i]){
            continue;
        }
        //这里使用了写法二,如果想使用写法一可自行替换
        for(int j=i*2;j<=n;j+=i){
            Prime[j]=1;
        }
    }
}

三、欧拉筛法

简介

虽然埃氏筛法很快,但它有一个问题,就是一个数可能被标记为非质数很多次,很浪费时间。欧拉筛法就解决了这个问题。它的时间复杂度是 O ( n ) O(n) O(n),而埃氏筛法的时间复杂度是 O ( n   l o g   l o g   n ) O(n\,log\,log\,n) O(nloglogn)。可见欧拉筛法的时间复杂度更优。

过程

i i i从2开始到n结束,每次遍历:
如果 a i a_i ai标记为是质数,那么将 i i i加入质数列表。
在所有情况下,都进行下面的运算:
如果 i × P [ j ] ≤ n i×P[j]≤n i×P[j]n(未超过范围),则标记 a [ i × P [ j ] ] a[i×P[j]] a[i×P[j]] 为非质数;否则退出循环。

程序

void Sieve_Euler(int n){
		for(int i=0;i<=n;++i) Prime[i]=0;
    vector<int> P;
    for(int i=2;i<=n;++i){
        if(Prime[i]==0){
            P.push_back(i);
        }
        for(int j=0;j<P.size();++j){
            if(i*P[j]>n){
                break;
            }
            Prime[i*P[j]]=1;
            if(i%P[j]==0){
                break; // 确保每个合数只被最小质因子标记一次
            }
        }
    }
}

结束

筛选质数在编程中是很重要的一个操作,本文列举了三种常用的质数筛法3。掌握它们对编程是很有帮助的。


  1. 本文想优化质数筛选的复杂度,所以在此着重介绍埃拉托斯特尼筛法(以下简称埃氏筛法)和欧拉筛法(或线性筛法),不着重介绍逐个判断的方法。 ↩︎

  2. 从这里开始,我使用质数为0,非质数为1,请注意。 ↩︎

  3. 事实上第一种逐个判断的方法更适合于判断单个数字,判断范围内的数字就略显吃力,但范围小的时候也是有作用的,所以列举在此。 ↩︎

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值