素数筛法
关键思想
素数筛法是ACM 及各大比赛中必须熟练掌握的最低级的算法,在已知某些素数的情况下对未判断的数进行筛选,筛选掉必然不是素数的数。如何对数进行筛选,依据素数的性质,某个除1以外的正整数是素数,则该数的倍数一定不是素数
举个栗子
从1-10中筛选出所有素数
步骤 | 当前元素 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
原始数组 | / | / | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
第一步 | 2 | / | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
第二步 | 3 | / | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
第三步 | 5 | / | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
第四步 | 7 | / | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
以第一步为例:当前元素为2,则将2∗2=4,2∗3=6,2∗5=10都置为合数,即每次都将当前为素数的元素的倍数从候选的数中除去。
循环上述过程,则可得到最终的数组,数组中为0的则该元素为素数,否则为合数。
代码如下
#include <cstdio>
#define MAXN 11
int prime[MAXN] = {1, 1}; //第0,1个元素直接设为合数,合数设为1,素数为0
int main() {
//素数筛法求素数
for (int i = 0; i < MAXN; ++i) {
if (!prime[i]) {//若该数是素数,则将该数的倍数全设为合数。
//printf("%d ",i); 答应当前元素
for (int j = 2 * i; j <= MAXN; j+=i) {//筛选掉必然是合数的数
prime[j] = 1;
}
}
}
return 0;
}
复杂度
该算法的复杂度为O(n2),但常系数非常小,实际运行时接近O(N)复杂度,并且随着筛选的进行,可以仔细思考一下,最后筛选掉的数越来越多,剩下的素数越来越小,即素数之间的间隔越来越大。
关于优化
在素数筛法的过程中,有许多重复计算的过程可以进行优化,例如上述例子中,元素6和元素10都筛选了两次,就是表格中红了两次。
下边列一下可以优化的点,就不具体实现了。
1. 避免重复筛选
2. 偶数直接可以不参与筛选,并且依据这个规则可以将数组大小优化掉一半
说明一下第二点可以优化空间的方法,例如原数组的第i个元素代表数字i是否为素数,若直接将偶数去除,则数组的第
进阶
想继续学习素数有关知识的可以看看以下几个知识点
- 费马测试
- Miller-Rabin 质数测试
- 欧拉-雅科比测试
- AKS质数测试
等等素性测试算法,可能以后会在详细讨论这些算法。