线性筛法求素数

来源:互联网 发布:全球气候变暖数据 编辑:程序博客网 时间:2024/06/13 12:35

普通筛法

先讲一下普通的筛法。筛法,顾名思义,就是筛掉合数,剩下的就是素数了。
我们知道,合数一定可以分解为两个或以上的素数,所以我们只需要对于每一个素数i,枚举一个大于2的数j,将ij(此为某个合数)筛掉。如果i是合数,那一定会被它的一个质因数乘上某个数而被筛掉,反之,它将不会被筛掉。

Code(pascal) normal

    for i:=2 to n do    if bz[i] then  //如果此数没有被小于它的质数乘上某个数所筛掉,说明它是质数    for j:=2 to n div i do  //枚举i的乘数j    bz[i*j]:=false;  //筛掉此数

这个算法的时间复杂度是n2的,我们看一下能不能优化一下。
我们可以发现,对于一个大于n的质数,我们可以发现用此数来筛是没有意义的。
我们设这个数为y,则j一定小于y(因为y>√n,j最大为n/y,所以j<√n<y)
设j=P1P2P3...Pn(Pi),因为j<y,所以Pi<y,所以jy=Piu()

综上所述,当i=Pi(Pi<y)时,jyy>n)这个合数被Piu筛掉了,所以只需循环到√n,且当j<i时,筛ij没有意义(ij已经被筛),所以i只需循环到√n,且枚举j时,可以从i开始枚举。

Code(pascal) optimization

    for i:=2 to trunc(sqrt(n)) do    if bz[i] then  //判断质数    for j:=i to n div i do  //枚举i的乘数j(j≥i)    bz[i*j]:=false;  //筛数

线性筛法

现在开始讲主角了,先看看代码。

Code(pascal) linear

for i:=1 to n dobegin    if bz[i]=false then  //i为素数则bz[i]=false,为合数则bz[i]=true    begin        inc(o);        s[o]:=i;  //s为素数数组    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;

重点在于为什么i mod s[j]就不需要筛了?
事实上我们这样每个合数只会被筛一遍。
接下来我们需要证明两个东西:
1、质数一定不会被筛。
2、合数一定会被筛,且只会被筛一次。
我们知道质数只有一个质因数,因此,所以不会被两个大于1的数的乘积所筛。
对于合数,我们设合数K=P1P2P3...Pm1PmPwPw+1
所以使得循环break掉的素数就是P1,所以比P1大的质数(设其为U),不会在此时把iU筛掉,但是iU一定会被筛掉.
(iU)=P1P2P3...Pm1PmU(U>P1)
=UP2P3...Pm1PmP1
=iP1(i>i)
又因为i没有比P1要小的质因数,所以当i=i,一定会枚举到P1此素数将P1iiU筛掉。
通过上面我们可以知道,每个合数只会被自己最小的质因数乘上某数筛掉,因此只会被筛一次且一定被筛,所以时间复杂度就降至了O(n)

4 1
原创粉丝点击