C++小于n的素数

文章介绍了求解小于n的素数集合的三种方法:枚举法、埃氏筛和线性筛。枚举法通过遍历并判断每个数的因数来确定素数;埃氏筛通过标记素数的倍数来排除非素数;线性筛优化了埃氏筛,避免重复标记,每个非素数只被标记一次。提供了每种方法的核心代码示例。
摘要由CSDN通过智能技术生成

概述

求小于n的素数集合。

什么是素数?

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

特殊的:偶数2是素数,除2以外的其他素数均是奇数

三种方法

枚举

对于一个非素数n,一定存在两个整数x,y, 满足1 < x <= y < n ,使得 x*y = n,即 n % x = 0。

推导x的取值范围,即遍历的结束条件:
有 1 < x < = y < n x ∗ y = n 则有 x ∗ x < = x ∗ y = n 即 x < = n ,也即 x 2 < = n 有 1 < x <=y<n \\ x*y=n \\ 则有 x * x <= x*y = n \\ 即 x <= \sqrt{n},也即x^2 <= n 1<x<=y<nxy=n则有xx<=xy=nx<=n ,也即x2<=n
核心逻辑代码:

// 判断是否为素数
bool isPrime(int x)
{
    for(int i = 2; i * i <= x; i++){   // 将 i*i <= x作为循环结束标志
        if(x % i == 0) return false;
    }
    return true;
}


// 素数除2外,全部为奇数。
// 从奇数3开始,每次判断后加2,继续判断下一个奇数。
if (n >= 2) primes.push_back(2);
for(int i = 3; i <= n; i += 2){
	if(isPrime(i)) {
        primes.push_back(i);
    }
}

埃氏筛

对于一个素数 x,其素数的整数倍 2x, 3x, 4x … 一定不是素数。

对于一个非素数y,一定可以由一个小于y的素数的乘积表示(两个乘数至少有一个可以是素数),所以y的整数倍也一定可以由比y小的素数的乘积表示(两个乘数至少有一个可以是素数)。
y = a ∗ b   ,其中 a 是小于 y 的素数 2 y = a ∗ ( 2 b )    , 其中 a 是小于 y 的素数 m ∗ y = a ∗ ( m ∗ b )    , 其中 a 是小于 y 的素数 y = a * b \ \ ,其中a是小于y的素数 \\ 2y = a * (2b) \ \ , 其中a是小于y的素数 \\ m*y = a * (m*b) \ \ , 其中a是小于y的素数 \\ y=ab  ,其中a是小于y的素数2y=a(2b)  ,其中a是小于y的素数my=a(mb)  ,其中a是小于y的素数

对于一个质数 x,如果按上文说的我们从 2x,3x,4x 开始标记其实是冗余的,应该直接从 x* x开始标记,因为 2x,3x,4x, … 这些数一定在 x 之前就被其他数的倍数标记过了,例如 2 的所有倍数,3 的所有倍数等。

核心代码:

std::vector<int> isPrime(n, 1);  // 是否是素数
std::vector<int> primes;		// 存放素数

for(int i = 2; i <= n; i++) {
    if(isPrime[i]){
        primes.push_back(i);
        for(long long j = (long long)i * i ; j <= n; j += i){
            isPrime[j] = 0;
        }
    }
}

线性筛

不作为重点,简单解释。

上面提到的埃氏筛,对于某些特殊的数,比如45,是素数3和5的倍数,并且3*3<45,5*5 < 45, 所以45会被3和5标记为非素数,这样重复标记是冗余的。所以线性筛就是实现每个非素数只会被标记一次

与埃氏筛的不同点是,标记过程不再仅当x是素数时才进行,对于每个整数都进行。对于整数 x,我们不再标记其所有的倍数 x,2x, 3x⋅(x+1),而是只标记素数集合primes中的数与 x 相乘的数,即x*primes[0], x*primes[1] … 。结束标志是x%primes[j] = 0

其实,简单的说,就是实现对于每一个非素数,都是被一个最大整数因子标记。即 x = 最大整数因子 * 最小素数因子。对于45,则会在取出15时标记, isPrime[15*3] = 0,而不会是isPrime[9*5] = 0。

如果理解了,那核心代码写起来就很简单了。

std::vector<int> isPrime(n, 1);  // 是否是素数
std::vector<int> primes;		// 存放素数

if(n > 2) primes.push_back(2);
for(int i = 3; i < n; i+=2){
    if(isPrime[i]) primes.push_back(i); 

    for(int j : primes){
        if((long long)i*j < n)  isPrime[i*j] = 0;  // long long 防溢出
        else break;

        if(i % j == 0) break;
    }
}

测试代码

枚举法求素数

#include <iostream>
#include <vector>

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

int main()
{
    int n = 15;
    std::vector<int> primes;

    primes.push_back(2);
    for(int i = 3; i <= n; i += 2){
        if(isPrime(i)) primes.push_back(i);
    }

    if(n < 2) primes.pop_back();

    for(int i = 0; i < primes.size(); i++)
        std::cout << primes[i] << std::endl;

    return 0;
}

埃氏筛求素数:

#include <iostream>
#include <vector>

int main()
{
    int n = 15;
    std::vector<int> isPrime(n, 1);
    std::vector<int> primes;

    for(int i = 2; i <= n; i++) {
        if(isPrime[i]){
            primes.push_back(i);
            for(long long j = (long long)i * i ; j <= n; j += i){
                isPrime[j] = 0;
            }
        }
    }

    for(int i = 0; i < primes.size(); i++)
        std::cout << primes[i] << std::endl;

    return 0;
}

线性筛求素数:

#include <iostream>
#include <vector>

int main()
{
    int n = 15;
    std::vector<int> isPrime(n, 1);
    std::vector<int> primes;

    if(n > 2) primes.push_back(2);
    for(int i = 3; i < n; i+=2){
        if(isPrime[i]) primes.push_back(i); 

        for(int j : primes){
            if((long long)i*j < n)  isPrime[i*j] = 0;
            else break;

            if(i % j == 0) break;
        }
    }

    for(int i = 0; i < primes.size(); i++)
        std::cout << primes[i] << std::endl;

    return 0;
}

参考

204. 计数质数 - 力扣(Leetcode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值