普通筛法
先讲一下普通的筛法。筛法,顾名思义,就是筛掉合数,剩下的就是素数了。
我们知道,合数一定可以分解为两个或以上的素数,所以我们只需要对于每一个素数
i
,枚举一个大于
Code 1.0
for i:=2 to n do
if bz[i] then
for j:=2 to n div i do
bz[i*j]:=false;
这个算法的时间复杂度是
我们可以发现,对于一个大于√
n
的质数,我们可以发现用此数来筛是没有意义的。
我们设这个数为
设j=
综上所述,当
i=Pi
(
Pi<y
)时,
j∗y
(
y>√n
)这个合数被
Pi∗u
筛掉了,所以只需循环到√
n
,且当
Code 2.0
for i:=2 to trunc(sqrt(n)) do
if bz[i] then
for j:=i to n div i do
bz[i*j]:=false;
线性筛法
但这样的筛法还是很费时,因为我们可以发现这个程序的复杂度不够优秀主要是因为同一个合数可能会被筛多次,这样的复杂度就显得很差劲,有没有更好的方法呢?
倘若每一个合数都只被筛一次,那复杂度就大大减小了,于是便有了实现这种猎奇操作的线性筛法。
Code 3.0
for i:=1 to n do
begin
if bz[i]=false then
begin
inc(o);
s[o]:=i;
end;
for j:=1 to o do
begin
bz[i*s[j]]:=true;
if (i mod s[j]=0) or (i*s[j]>n) then break; //关键点
end;
end;
重点在于为什么
事实上我们可以证明这样每个合数只会被筛一遍。
接下来我们需要证明两个东西:
1、质数一定不会被筛。
2、合数一定会被筛,且只会被筛一次。
我们知道质数只有一个质因数,因此,所以不会被两个大于
1
的数的乘积所筛。
对于合数,我们设合数
所以使得循环break掉的素数就是
P1
,所以比
P1
大的质数(设其为
U
),不会在此时把
(i∗U)=P1∗P2∗P3∗...∗Pm−1∗Pm∗U(U>P1)
=U∗P2∗P3∗...∗Pm−1∗Pm∗P1
=i′∗P1(i′>i)
又因为
i′
没有比
P1
要小的质因数,所以当
i=i′
,一定会枚举到
P1
此素数将
P1∗i′(i∗U)
筛掉。
通过上面我们可以知道,每个合数只会被自己最小的质因数乘上某数筛掉,因此只会被筛一次且一定被筛,所以时间复杂度就降至了
O(n)
。
应用
线性筛法的应用很广泛。
虽说这东西本来是用来求素数,但大多时候都是用于莫比乌斯函数,欧拉函数的预处理,有时还应用于预处理在一定范围内的任意数的素数分解。