质数
质数(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;
}
};
埃拉托色尼筛选法实现简单,缺点是对空间的需求量大。对于这一点,我们可以用位图存储,这样就可以大大减少空间需求量。