什么是筛法?
筛法的直接用途是用来解决寻找$1~N$中的质数的问题的。
也常用来间接解决一些其他的数论问题。
最简单的筛法:艾氏筛法
简介
艾氏筛法是比较直接就能想到的一种筛法。
每找到一个质数,就用它来筛掉后面所有它的倍数。
这样每找到一个没有被筛掉的数,由于前面所有比他小的数都不是它的因数,所以它一定是一个质数。
代码
代码也很容易实现:
1 |
|
它的复杂度是$O(nloglogn)$。
欧拉筛法
简介
上面的筛法虽然复杂度已经很低了,但是我们仍然需要一种更加快速的筛法。
很明显,我们上一个算法不够迅速的原因是有的数字被筛了多次。如果能避免这种情况,我们的算法就会更快。
欧拉筛法可以保证范围内的每个合数都被删掉(在 bool 数组里面标记为非素数),而且任一合数只被:
“最小质因数 × 最大因数(非自己) = 这个合数”
的途径删掉。由于每个数只被筛一次,时间复杂度为 O(n)。
代码
先上代码:
1 |
|
解释
容易发现,这份代码和刚才的那种筛法主要的区别就在于
1 | if(i % Prime[j] == 0) |
为什么?
刚才说到过,我们只想让一个数的最大因数筛掉这个数。
模拟一下这个程序:
| 当前用于筛素数的数i | 被i筛掉的数 | 被i筛掉的数 | 被i筛掉的数 | 被i筛掉的数 |
|---|---|---|---|---|
| 2 | 2 * 2=4 | |||
| 3 | 3 * 2=6 | 3 * 3=9 | ||
| 4 | 4 * 2=8 | |||
| 5 | 5 * 2=10 | 5 * 3=15 | 5 * 5=25 | |
| 6 | 6 * 2=12 | |||
| 7 | 7 * 2=14 | 7 * 3=21 | 7 * 5=35 | 7 * 7=49 |
| 8 | 8 * 2=16 | |||
| 9 | 9 * 2=18 | 9 * 3=27 | ||
| 10 | 10 * 2=20 |
这就达到了预期,让一个数的最大因数筛掉这个数。
为什么呢?
素数表prime中已有两个素数:2和3。
此时在check数组中已经被标记为1的数(合数)是:4、6、9。
因为$i=4$已经被标记为1,所以我们不将4添加进素数表prime。
此时执行 j 循环:
因为素数表prime中有2和3,所以预计将要被筛掉的数是$4×2=8$ 和 $4×3=12$。
当$4×2=8$被筛掉以后,经过 $i%prime[j]=0$ 判断可以知道$4%2=0$, 此时应结束 j 循环,不再筛 $4×3=12$。
我们都知道12的最大因数(非自身,下同)是6,因此12应该被6筛掉,那么上述判断的原理是什么呢?
因为当出现 $i%prime[j]=0$ 的情况时,意味着$prime[j]$ 是 i 的一个因数(2是4的一个因数)。
既然4是12的因数,2又是4的因数,所以2也是12的因数。
由此可以知道,4不是12的最大因数,12的最大因数会在后面出现,即2所对应的6,12将会在i=6时被筛掉。
同理可以判断:9是45的因数,3又是9的因数,所以3也是45的因数,9不是45的最大因数,45的最大因数将会在后面出现,即3所对应的15,45将会在$i=15$时被筛掉。



