质数筛选算法:The Sieve of Eratosthenes

质数

质数(prime number)也叫素数,为大于1的且除1和本身以外不再有其他因数的自然数,与之相对的是合数。质数有无限个。

性质

·质数只有两个因数:1和本身
·任何大于1的自然数,要么本身是质数,要么可以分解为几个质数之积,且这种分解是唯一的
·质数的个数是无限多的
·若n为正整数,在n²到(n+1)²之间至少有一个质数
·若n为大于等于2的正整数,则n到n!之间至少有一个质数
·若质数p为不超过n(n≥4)的最大质数,则p>n/2
·所有大于10的质数中,个位数只有1,3,7,9

埃拉托色尼筛选法

尽管质数有无限多个,但人们还是经常会问某一范围的质数个数。埃拉托色尼筛选法(The Sieve of Eratosthenes),简称埃氏筛法,是解决这一问题的常见算法。
埃拉托色尼筛选法基于一项基本性质:任何大于1的自然数,要么本身是质数,要么可以分解为几个质数之积,且这种分解是唯一的。
以LeetCode上的一道题为例。寻找所有小于非负整数n的质数的数量,埃拉托色尼筛选法是这么工作的。

假设从起点开始(起点可由要求指定)的所有数都是质数。从起点开始向前搜寻,若为质数,则将其倍数(不超过上界n)标记为非质数。例如2为质数,则标记4,6,8, ...这些2的倍数都为非质数,然后标记下一个……依此类推。

class Solution1 {
public:
    size_t countPrimes(size_t n) {
        bool *p = new bool[n+1];
        size_t i, j;
        for (i = 0; i <= n; ++i)
            p[i] = true;
        p[0] = p[1] = false;
        for (i = 2; i < n; ++i)
                if (p[i])
                    for (j = 2; i*j < n; ++j)
                        p[i*j] = false;
        size_t cnt = 0;
        for (i = 2; i < n; ++i)
            if (p[i])
                cnt++;
        return cnt;
    }
};

        由流程图可知,在筛选过程中我们进行了很多重复筛选。实际上在内层循环中,只要从i*i开始筛就可以了,因为i*2, i*3, ..., i*i-1都被筛过了。

class Solution2 {
public:
    size_t countPrimes(size_t n) {
        bool *p = new bool[n+1];
        size_t i, j;
        p[0] = p[1] = false;
        for (i = 2; i <= n; ++i)
            p[i] = true;
        for (i = 2; i < n; ++i)
                if (p[i])
                    for (j = i*i; j < n; j += i)
                        p[j] = false;
        size_t cnt = 0;
        for (i = 2; i < n; ++i)
            if (p[i])
                cnt++;
        return cnt;
    }
};

埃拉托色尼筛选法实现简单,缺点是对空间的需求量大。对于这一点,我们可以用位图存储,这样就可以大大减少空间需求量。

猜你喜欢

转载自blog.csdn.net/qq_37653144/article/details/80470029
今日推荐