【数论】欧拉筛法-埃拉托斯特尼筛法
本文最后更新于: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 |
|