【数论】欧拉筛法-埃拉托斯特尼筛法
本文最后更新于:2023年11月25日 晚上
欧拉筛法
概念
筛法就是求出小于等于
n的所有素数的方法,找到一个素数后,就将它的倍数标记为合数,也就是把它的倍数“筛掉”。如果一个数没有被比它小的素数“筛掉”,那它就是素数。
思路
1 | |
假设要筛出n以内的素数。我们先把所有数标记为素数。枚举i从2到n,所以因为i是从小到大的,如果i没有被比它小的数标记为合数,那i就是素数(任意合数的因数一定比它本身小,若i没有被前面的数筛掉,那么它就没有比自己小的因数,当然它就是素数),加入素数列表。
现在用i来筛后面的数,枚举已经筛出来的素数prime[j](j∈[1,cnt]),标记i * prime[j]为合数(i ≥ 2,prime[j] ≥ 2,它们的乘积一定为合数),当i是prime[j]的倍数时退出循环,i++。
正确性证明
假设我们要筛掉合数a,且a的最小质因数为P1,令a = P1 * b ,那么显然b < a,b先被外层循环碰到。现在b要筛掉它的倍数。
因为P1是a的最小质因数,所以b的最小质因数必不小于P1,这样就保证b筛掉a前不会在if(i % prime[j] == 0) break;处跳出循环。
即使b的最小质因数等于P1,也会先筛掉a后再退出循环。这样就保证了每个合数都会被筛掉。
时间复杂度证明
欧拉筛法的时间、空间复杂度为Θ(n)。
空间复杂度是显然的,下面证明时间复杂度为线性。
我们的核心就是要证明一个合数只会被筛掉一次,即标记isprime[n]=1一次。首先,对于a = P1 * b,b当然只会筛掉a一次,因为我们从小到大枚举prime[j],也就是说b * prime[j]递增,因此不可能遇到a两次。会不会有其他的数筛掉a呢?假设a又被c筛掉了,其中a = Px * c,Px就是c用来筛掉a的prime[j]。
- 若
c > b,则Px < P1,与P1是a最小的质因数矛盾,假设不成立; - 若
c < b,则Px > P1,这意味着P1是c的质因数。那么c从小到大筛掉它的素数倍,在筛到P1 * c时就break了,所以根本轮不到a。
综上所述,每个数都只会被筛掉一次,再加上外层的i的循环是线性复杂度,总的时间复杂度是线性的。
示例
来自:头歌-Python大数据与人工智能实践
计算并输出指定范围内的所有素数。
第一行输入正整数
n,表示测试样例组数,接下来输入n行,每行输入两个正整数a和b,要求输出a和b之间(包括a和b)所有的素数,保证a<b,且b不超过10^7
测试输入:
1 | |
预期输出:
1 | |
CODE:
1 | |
埃拉托斯特尼筛法
埃拉托斯特尼筛法 sieve of Eratosthenes,简称埃氏筛,也称素数筛。
从2开始,将每个素数的各个倍数,标记成合数。一个素数的各个倍数,是一个差为此素数本身的等差数列。
定出要筛数值的范围n,找出$\sqrt{n}$以内的素数$\displaystyle p_{1},p_{2},\dots,p_{k}$。先用2去筛,把2留下,把2的倍数剔除掉;再用下个素数3筛,把3留下,把3的倍数剔除掉;接下去用下个素数5筛,把5留下,把5的倍数剔除掉,如果最大数不大于最后一个标出的素数的平方,那么剩下的所有的数都是质数,否则继续上述过程。
1 | |