素数的判断方法:暴力求解,埃氏筛法,欧拉筛法

判断一个数是否为素数

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

朴素求解

bool is_prime(int n) {
    if (n < 2)
        return false;
    for (int i = 2; i < n; ++i)
        if (n % i == 0)
            return false;
    return true;
}

因为如果 i i i n n n 的一个因子,就必然存在一个 m m m 满足 m = n / i m = n / i m=n/i m < n m < n m<n ,如果 i = m i = m i=m ,则 i = m = n i = m = \sqrt{n} i=m=n ;如果 i ≠ m i \neq m i=m ,则必然有 i , m i, m i,m 其中一个大于 n \sqrt{n} n ,一个小于 n \sqrt{n} n 。所以我们只需要根据 [ 2 , n ] [2, \sqrt{n}] [2,n ] 这个区间是否有 n n n 的因子就能判断 n n n 是否为质数。

注意:如果使用 i < = s q r t ( n ) i <= sqrt(n) i<=sqrt(n) 作为结束条件,反复调用 s q r t sqrt sqrt 这个函数会拖慢程序运行;如果使用 i ∗ i < = n i * i <= n ii<=n 作为判断条件,可能会因为 n n n 接近 i n t int int 型变量上届溢出,比如 n = 2147483647 n = 2147483647 n=2147483647,当 i = 46340 i = 46340 i=46340 时, i ∗ i = 2147395600 < n i * i = 2147395600 < n ii=2147395600<n,循环继续, i = 46341 i = 46341 i=46341 时, i ∗ i = 2147488281 i * i = 2147488281 ii=2147488281 造成溢出;所以推荐判断结束条件为 i < = n / i i <= n / i i<=n/i.

bool is_prime(int n) {
    if (n < 2)
        return false;
    for (int i = 2; i <= n / i; ++i)
        if (n % i == 0)
            return false;
    return true;
}

如果题目需要求出很多个数的素数,仍然使用时间复杂度为 O ( n {O(\sqrt{n}} O(n 的朴素方法会超时,这时我们可以预先获得素数表,求出 [ 1 , n ] [1, n] [1,n] 内的所有素数,对题目中输入的数进行判断时可以 O ( 1 ) O(1) O(1) 时间内判断是否为素数。

埃氏筛法

埃氏筛法,算法从小到大枚举所有的数,对于每一个素数,筛去它所有倍数,筛了一遍后剩下的就是素数,算法复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

[ 1 , 15 ] [1, 15] [1,15] 的素数:

  • 预先需要知道 2 2 2 是一个素数, p r i m e [ 1 ] = 2 prime[1] = 2 prime[1]=2, 筛去它的所有倍数 2 , 4 , 6 , 8 , 10 , 12 , 14 2, 4, 6, 8, 10, 12, 14 2,4,6,8,10,12,14
  • i = 3 i = 3 i=3 3 3 3 未筛去, 3 3 3 是素数, p r i m e [ 2 ] = 3 prime[2] = 3 prime[2]=3,筛去它的所有倍数 6 , 9 , 12 , 15 6, 9, 12, 15 6,9,12,15
  • i = 4 i = 4 i=4 4 4 4 已筛去, 4 4 4 不是素数
  • i = 5 i = 5 i=5 5 5 5 未筛去, 5 5 5 是素数, p r i m e [ 3 ] = 5 prime[3] = 5 prime[3]=5,筛去它的所有倍数 10 , 15 10, 15 10,15
  • i = 6 i = 6 i=6 6 6 6 已筛去, 6 6 6 不是素数
  • i = 7 i = 7 i=7 7 7 7 未筛去, 7 7 7 是素数, p r i m e [ 4 ] = 7 prime[4] = 7 prime[4]=7,筛去它的所有倍数 14 14 14
  • i = 8 i = 8 i=8 8 8 8 已筛去, 8 8 8 不是素数
  • i = 9 i = 9 i=9 9 9 9 已筛去, 9 9 9 不是素数
  • i = 10 i = 10 i=10 10 10 10 已筛去, 10 10 10不是素数
  • i = 11 i = 11 i=11 11 11 11 未筛去, 11 11 11是素数, p r i m e [ 5 ] = 11 prime[5] = 11 prime[5]=11, 在 [ 1 , 15 ] [1, 15] [1,15] 中没有 11 11 11 的倍数
  • i = 12 i = 12 i=12 12 12 12 已筛去, 12 12 12不是素数
  • i = 13 i = 13 i=13 13 13 13 未筛去, 13 13 13是素数, p r i m e [ 6 ] = 13 prime[6] = 13 prime[6]=13, 在 [ 1 , 15 ] [1, 15] [1,15] 中没有 13 13 13 的倍数
  • i = 14 i = 14 i=14 14 14 14 已筛去, 14 14 14不是素数
  • i = 15 i = 15 i=15 15 15 15 已筛去, 15 15 15不是素数
  • 得到 [ 1 , 15 ] [1, 15] [1,15] 内的所有素数 2 , 3 , 5 , 7 , 11 , 13 2, 3, 5, 7, 11, 13 2,3,5,7,11,13
#include <cstdio>
const int maxn = 1e6;
//存储1 ~ 1e6内的所有素数
int primes[maxn], cnt;
//对1 ~ 1e6内的数标记,未被筛掉的素数标记为false
bool sifter[maxn];

void get_primes(int n) {
    for (int i = 2; i <= n; ++i)
        if (sifter[i] == false) {
            primes[cnt++] = i;
            for (int j = i + i; j <= n; j += i)
                sifter[j] = true;
        }
}

int main() {
    int n; scanf("%d", &n);
    get_primes(n);
    printf("%d\n", cnt);
    return 0;
}

欧拉筛法

在上面埃氏筛法中可以看到 12 12 12 被筛去两次;将范围扩大到 [ 1 , 100 ] [1, 100] [1,100] 100 100 100 会分别被 2 , 4 , 5 , 10 , 20 , 25 , 50 2, 4, 5, 10, 20, 25, 50 2,4,5,10,20,25,50 筛去;当范围更大时,重复筛去的次数变多,这不是我们想要的。

欧拉筛法就避免了这一情况,每个非素数仅被筛去一次,欧拉筛法复杂度 O ( n ) O(n) O(n)

#include <cstdio>
const int maxn = 1e6;
//存储1 ~ n内的所有素数
int primes[maxn], cnt;
//最终为false的数是素数
bool sifter[maxn];

void get_primes(int n) {
    //从2开始,判断i是否为素数
    for (int i = 2; i <= n; ++i) {
        if (sifter[i] == false)
            primes[cnt++] = i;

        for (int j = 0; primes[j] <= n / i; ++j) {
            // 每一个倍数标记为不是素数
            sifter[primes[j] * i] = true;

            // primes[j]为i的最小素因子,分析下一个i
            if (i % primes[j] == 0)
                break;
        }
    }
}

int main() {
    int n; scanf("%d", &n);
    get_primes(n);
    printf("%d\n", cnt);
    return 0;
}

e . g . e.g. e.g. [ 1 , 15 ] [1, 15] [1,15] 的素数 ( n = 15 ) (n = 15) (n=15)

  • i = 2 i = 2 i=2, 未筛去, p r i m e [ 0 ] = 2 prime[0] = 2 prime[0]=2
    • j = 0 j = 0 j=0, p r i m e s [ j ] < = n / i primes[j] <= n / i primes[j]<=n/i, i ∗ p r i m e [ 0 ] = 2 ∗ 2 = 4 i * prime[0] = 2 * 2 = 4 iprime[0]=22=4, 4 4 4被筛去, i % p r i m e [ j ] = = 2 % 2 = = 0 , b r e a k i \% prime[j] == 2 \% 2 == 0, break i%prime[j]==2%2==0,break
  • i = 3 i = 3 i=3, 未筛去, p r i m e [ 1 ] = 3 prime[1] = 3 prime[1]=3
    • j = 0 j = 0 j=0, p r i m e s [ j ] < = n / i primes[j] <= n / i primes[j]<=n/i, i ∗ p r i m e [ j ] = 3 ∗ 2 = 6 i * prime[j] = 3 * 2 = 6 iprime[j]=32=6, 6 6 6被筛去, i % p r i m e [ j ] = = 3 % 2 = = 1 i \% prime[j] == 3 \% 2 == 1 i%prime[j]==3%2==1,
    • j = 1 j = 1 j=1, p r i m e s [ j ] < = n / i primes[j] <= n / i primes[j]<=n/i, i ∗ p r i m e [ j ] = 3 ∗ 3 = 9 i * prime[j] = 3 * 3 = 9 iprime[j]=33=9, 9 9 9被筛去, i % p r i m e [ j ] = = 3 % 3 = = 0 , b r e a k i \% prime[j] == 3 \% 3 == 0, break i%prime[j]==3%3==0,break
  • i = 4 i = 4 i=4, 已筛去
    • j = 0 j = 0 j=0, p r i m e s [ j ] < = n / i primes[j] <= n / i primes[j]<=n/i, i ∗ p r i m e [ j ] = 4 ∗ 2 = 8 i * prime[j] = 4 * 2 = 8 iprime[j]=42=8, 8 8 8被筛去, i % p r i m e [ j ] = = 4 % 2 = = 0 , b r e a k i \% prime[j] == 4 \% 2 == 0, break i%prime[j]==4%2==0,break
  • i = 5 i = 5 i=5, 未筛去, p r i m e [ 2 ] = 5 prime[2] = 5 prime[2]=5
    • j = 0 j = 0 j=0, p r i m e s [ j ] < = n / i primes[j] <= n / i primes[j]<=n/i, i ∗ p r i m e [ j ] = 5 ∗ 2 = 10 i * prime[j] = 5 * 2 = 10 iprime[j]=52=10, 10 10 10被筛去, i % p r i m e [ j ] = = 5 % 2 = = 1 i \% prime[j] == 5 \% 2 == 1 i%prime[j]==5%2==1,
    • j = 1 j = 1 j=1, p r i m e s [ j ] < = n / i primes[j] <= n / i primes[j]<=n/i, i ∗ p r i m e [ j ] = 5 ∗ 3 = 15 i * prime[j] = 5 * 3 = 15 iprime[j]=53=15, 15 15 15被筛去, i % p r i m e [ j ] = = 5 % 3 = = 2 i \% prime[j] == 5 \% 3 == 2 i%prime[j]==5%3==2,
    • j = 2 j = 2 j=2, p r i m e s [ j ] > n / i , b r e a k primes[j] > n / i, break primes[j]>n/i,break
  • i = 6 i = 6 i=6, 已筛去
    • j = 0 , p r i m e s [ j ] < = n / i j = 0, primes[j] <= n / i j=0,primes[j]<=n/i, i ∗ p r i m e [ j ] = 6 ∗ 2 = 12 i * prime[j] = 6 * 2 = 12 iprime[j]=62=12, 12 12 12被筛去, i % p r i m e [ j ] = = 6 % 2 = = 0 , b r e a k i \% prime[j] == 6 \% 2 == 0, break i%prime[j]==6%2==0,break
  • i = 7 i = 7 i=7, 未筛去, p r i m e [ 3 ] = 7 prime[3] = 7 prime[3]=7
    • j = 0 , p r i m e s [ j ] < = n / i j = 0, primes[j] <= n / i j=0,primes[j]<=n/i, i ∗ p r i m e [ j ] = 7 ∗ 2 = 14 i * prime[j] = 7 * 2 = 14 iprime[j]=72=14, 14 14 14被筛去, i % p r i m e [ j ] = = 7 % 2 = = 1 i \% prime[j] == 7 \% 2 == 1 i%prime[j]==7%2==1,
    • j = 1 , p r i m e s [ j ] > n / i , b r e a k j = 1, primes[j] > n / i, break j=1,primes[j]>n/i,break
  • i = 8 i = 8 i=8, 已筛去
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 9 i = 9 i=9, 已筛去
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 10 i = 10 i=10, 已筛去
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 11 i = 11 i=11, 未筛去, prime[5] = 11,
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 12 i = 12 i=12, 已筛去
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 13 i = 13 i=13, 未筛去, prime[6] = 13,
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 14 i = 14 i=14, 已筛去
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break
  • i = 15 i = 15 i=15, 已筛去
    • j = 0 , p r i m e s [ j ] > n / i , b r e a k j = 0, primes[j] > n / i, break j=0,primes[j]>n/i,break

筛出的素数:先后被筛去的数是4, 6, 9, 8, 10, 15, 12, 14;分别只筛去一次,剩下素数2, 3, 5, 7, 11, 13

FAQ

这里为什么 i % p r i m e [ j ] = = 0 i \% prime[j] == 0 i%prime[j]==0 ,就要 b r e a k break break 呢?

如果不 b r e a k break break 4 ∗ 3 = 12 4 * 3 = 12 43=12 就要被筛去,实际上 12 12 12 会在 i = 6 i = 6 i=6 p r i m e [ j ] = p r i m e [ 1 ] = 2 prime[j] = prime[1] = 2 prime[j]=prime[1]=2 6 ∗ 2 = 12 6 * 2 = 12 62=12 被筛去。

2 2 2 12 12 12 的最小素因子,比如 100 100 100 的最小素因子是 2 2 2 ,所以 100 100 100 会在 50 ∗ 2 50 * 2 502 时筛选掉,避免重复筛选。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值