本文主要介绍总结一下判定素数过程中使用的两种筛选方法——Eratosthenes筛法(Sieve of Eratosthenes)和Eular筛法(Sieve of Euler)。
Eratosthenes筛法
基本思想:
素数的倍数一定不是素数
实现方法
- 使用长度为N的数组保存数字素数信息(0表示是素数,1 表示非素数)
- 首先将数组初始化为0,即认为所有的数都是素数
- 从第一个素数2开始,把所有2的倍数都记为素数(即将数组对应值置为1),一直到超出N的范围;
- 然后进行3的遍历,执行相同的操作;
- 执行到4的时候,由于之前已经将其值置为1,即判定为非素数,则执行
++
对下一个素数5进行操作;- 依次进行上述操作,直到最后可以获得所有的素数。
举例操作(N=20)
i | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | ||||||||||
3 | 1 | 1 | 1 | 1 | 1 | ||||||||||||||
5 | 1 | 1 | 1 | ||||||||||||||||
7 | 1 | ||||||||||||||||||
ALL | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |
0: 素数
1:非素数
代码实现
1 |
|
此算法的时间复杂度是O(nloglogn),空间复杂度是O(n);
由上也可以看出,此算法有很多不足之处,比如6,在素数为2时处理过一次,在为3的时候也处理了一次,重复了相同的操作。下面我们将介绍一个改进算法,欧拉筛法。
Euler筛法
核心思路
规定每个合数只用最小的一个质因数去筛选,比如6有2和3两个因数,只用2进行筛选和置位操作,3的情况通过条件跳过。
举例操作(N=20)
i | j | p[j] | count | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 2 | 1 | 1 | ||||||||||||||||||
3 | 0 | 2 | 2 | 1 | ||||||||||||||||||
3 | 1 | 3 | 2 | 1 | ||||||||||||||||||
4 | 0 | 2 | 2 | 1 | ||||||||||||||||||
5 | 0 | 2 | 3 | 1 | ||||||||||||||||||
5 | 1 | 3 | 3 | 1 | ||||||||||||||||||
6 | 0 | 2 | 3 | 1 | ||||||||||||||||||
7 | 0 | 2 | 4 | 1 | ||||||||||||||||||
8 | 0 | 2 | 4 | 1 | ||||||||||||||||||
9 | 0 | 2 | 4 | 1 | ||||||||||||||||||
10 | 0 | 2 | 4 | 1 | ||||||||||||||||||
ALL | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |
0: 素数
1:非素数
代码
下面直接上代码1
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
using namespace std;
int main()
{
int num;
cin >> num;
int flag[Length] = {0};
int prime[1000];
int count = 0;
for(int i = 2; i <= num; ++i)
{
if(!flag[i])
{
prime[++count] = i;
}
for(int j = 1; j <= count; ++j)
{
if(i * prime[j] > num)
break;
flag[i * prime[j]] = 1;
if(i % prime[j] == 0)
break;
}
}
cout << count << endl;
}
代码分析
核心代码
if(i % prime[j] == 0) break;
- 保证了每个合数都被置位
- 每个合数只会筛选到一次,在取其最小质因子的情况下
Eular算法的时间复杂度为O(n),相较之Eratosthenes算法有了很大的提升。