质数检验
引题:
质数指约数仅为1及其本身的自然数。比如8个最小的质数为2,3,5,7,11,13,17,19。注意,1不是质数。
请编写一个程序,输入n个整数,输出其中质数的个数。
输入:第一行输入n。接下来1行给出n个整数。
输出:输出质数的个数,占1行。
限制:
输入示例:
6
2 3 4 5 6 7
输出实例:
4
1. 初学者的简单算法
检验整数x是否为质数:检查整数x能否被2到x-1的整数整除。
bool isPrime( int x )
{
if( x<=1 ) return false;
for(int i=2;i<x;++i)
if( x%i==0 )
return false;
return true;
}
可以得知,上述算法对于单一数据其复杂度为O(x),算法整体的复杂度与 的总和成正比,显然无法在限制时间内输出答案。我们需要考虑一个更高效的方法。
2. 初学者优化算法
- 除2以外所有的偶数都不是质数,这样就能将复杂度减少一半。
- 在检查x时,由于x不可能被大于 的整数整除,这就又减少了一半复杂度。
但这些小技巧并不能撼动该算法复杂度为O(x)的本质。
3. 性质:若n为合数,则必有质数p|n,且
在检验质数的时候,我们可以利用“合数x拥有满足
的质因数p”这一性质。
举个例子,检验31是否为质数时,只需要看31能否被2到6的整数整除即可。如果7到30中存在能整除31的整数,那么2到6中必然也存在能整除31的整数,所以检查大于6的整数只是浪费资源。
利用这一性质,我们可以将检验范围从2到x-1缩小至2到
,算法的复杂度也就改良到了O(
)。比如x=1000000时,
=1000,此时该算法快了1000倍。
bool isprime(int x)
{
if(x==2) return true;
if( (x<2) || !(x&1) ) return false;//x<2或x为偶数
int i = 3;
while( i<=sqrt(x) )
{
if( x%i==0 ) return false;
i += 2;
}
return true;
}
4. 埃拉托色尼筛选法
有些时候,除了检验给定整数x是否为质数的函数之外,如果能事先准备出质数数列或质数表,就可以帮助我们更有效地求解质数的相关问题。
埃拉托色尼筛选法(The Sieve of Eratosthenes)可以快速列举出给定范围内的所有质数,这个算法如下步骤生成质数表。
埃拉托色尼筛选法:
- 列举大于等于2的整数。
- 留下最小的整数2,删除所有2的倍数。
- 在剩下的整数中留下最小的3,删除所有3的倍数。
- 在剩下的整数中留下最小的5,删除所有5的倍数。
- 以下同理,留下仍未被删除的最小整数,删除该整数的倍数,一直循环到结束。
以最小的4个质数为例,其求解过程如图。
原始表:
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
---|---|---|---|---|---|---|---|---|---|
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
第一轮:
2 | 3 | 5 | 7 | 9 | |||||
---|---|---|---|---|---|---|---|---|---|
11 | 13 | 15 | 17 | 19 | |||||
21 | 23 | 25 | 27 | 29 | |||||
31 | 33 | 35 | 37 | 39 | |||||
41 | 43 | 45 | 47 | 49 | |||||
51 | 53 | 55 | 57 | 59 |
第二轮:
2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | ||
---|---|---|---|---|---|---|---|---|---|
11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | |
22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | ||
31 | 32 | 34 | 35 | 36 | 37 | 38 | 40 | ||
41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | |
52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 |
第三轮:
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
---|---|---|---|---|---|---|---|---|---|
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | |
31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 |
第四轮:
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
---|---|---|---|---|---|---|---|---|---|
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
埃拉托色尼筛选法核心代码:
#define MAX 1000000
bool isprime[MAX];
//bool型数组isprime表示质数表,
//isprime[x]为true表示x是质数,为false表示x是合数。
void eratos(int n)
{
int i,j;
for(i=0;i<=n;++i)//列举整数作为候选的质数
isprime[i] = true;
isprime[0] = isprime[1] = false;//0和1不是质数,所以删除它们
for(i=2;i<=sqrt(n);++i)//留下i,删除i的倍数
if( isprime[i] )
{
j = i+i;
while( j<=n )
{
isprime[j] = false;
j = j+i;
}
}
}
埃拉托色尼筛选法需要占用一部分内存空间(与待检验整数的最大值N成正比),但其复杂度只有O(N log log N)。