今天晚上,在家里自学了一下线性筛法,还是比较容易理解的,并没有想象的那么难。
所谓线性就是O(n)的效率咯,线性筛法较快的一个本质就是他不会干重复的事,例如12——普通的筛法在素数2时候筛一次,素数3时筛一次,很明显这些是不必要的选择,我们可以舍去。
所以,在线性筛法里的优化就是,如果枚举素数倍数的数除以当前所拥有的质数的其中一个数的余数为0的时候,则可以退出了。
如果不懂上面那句话的话,可以试着看看代码是如何实现的。
代码:
var
bZ:Array[1..1000000] of boolean;
a:Array[0..200000] of longint;
n,i,j:longint;
begin
readln(n);
for i:=2 to n do
begin
if not bz[i] then
begin
inc(a[0]);
a[a[0]]:=i;
end;
for j:=1 to a[0] do
begin
if a[j]*i>n then break;
bz[i*a[j]]:=true;
if i mod a[j]=0 then break;
end;
end;
for i:=1 to a[0] do
write(a[i],' ');
end.
现在讲讲这种优化的本质。
例如当前筛掉是4的倍数时(x=4),拥有的素数为2,3,那么,我们之前已经讲过,3*4这个数我们无需筛,所以,我们只要判断 x mod 枚举的素数 的答案是否为0,如果为0,则可以直接break,如何证明呢?
设当前枚举的第i个素数所乘的倍数为x,而当前枚举的第i个素数为p,
则如果i mod p=0,则代表i是p的倍数,则如果i不管再乘以什么数,依然是p的倍数.
由此可知,当x*3,x*5,x*7……这些其实都可以被x*2来筛,所以,当i mod p=的时候,接下来所需筛的数就毫无意义了,也就无需筛了,也就可以Break了。
没错,就是这么easy.
但是在此方法下我拓展一个知识点,名为:欧拉函数.
用上面的筛数方法,在线性时间内求出n以内所有的素数,以及n以内所有的欧拉函数.
首先需明白的是,何为欧拉函数。————欧拉函数也就是n以内,所有与n互质的数的数目。
例如n=8,则欧拉函数有:1,3,5,7
求欧拉函数的方法很多,最容易想到的当然就是暴力,用gcd函数判断欧拉函数,时间复杂度为O(n*logn)(网上有gcd函数的时间复杂度证明,这里就不详细写了,logn也是一个不准确的数)
代码也其实很好打,但就是证明需要扯进很多东西,这里就不详细讲了,问问度娘吧。。