在前前前前前前…的博客中,我们主要谈了欧拉筛和埃氏筛.
今天我们主要来聊一聊线性筛和厄拉多塞筛(其实和埃氏筛和欧拉筛本质上没区别哎).其实在这两种筛法中厄拉多塞筛最好懂(就连本蒟蒻一看代码就明白了…别看这个名字,容易糊弄人)
首先是厄拉多塞筛"粉墨登场":::::::
厄拉多塞筛
厄拉多塞是个人名,因为这种筛法是他提出来的.
我们先讲一下大致的思路,我们example(举个栗子):
有一张写了2~n之间所有数的表,其中就包括这2和n.然后,我们从2开始一直到n划掉2的倍数.划完之后,我们发现3没有被划去,就证明3是质数,然后我们就在3~n当中划去是3倍数的数,以此类推.
此时,厄拉多塞筛的时间复杂度为
既然已经动了厄拉多塞筛的筛质数的方法,我们下面来就有请我们最喜欢的代码大军:
void ispri(){
cin>>n>>m;
a[1]=1;
for(i=n;i<=m;i++){
if(a[i]==1){
continue;
}else{
for(j=2*i;j<=m;j+=i)
a[j]=1;
}
}
for(i=n;i<=m;i++){
if(a[i]==0)
cout<<i<<" ";
}
return;
}
既然代码大军都已经出来了,我们就接着奔赴下一个战场,线性筛
线性筛
由于厄拉多塞筛在筛30=2*3*5类似于这些数的时候,会被2,3,5重复多筛几次,这样就大大耗费的时间复杂度,因此,我们推出了新产品––线性筛.
线性筛的思路就是要保证每一个数都被他的最小质因子筛去.
首先,我们先亮出代码:
void ispri(){
for(int i=2;i<=n;i++){
if(isp[i]==0) pr[++cnt]=i;
for(int j=1;j<=cnt&&pr[j]*i<=n;j++){
isp[i*pr[j]]=1
if(i%pr[j]==0) break;
}
}
}
保证每一个数只被他的最小的质因子筛去,代码中则体现在了i%pr[j]==0上面
pr数组中的质数是递增的,当pr[j]|i时,pr[j]|pj[j+1]i,那么pj[j+ 1]i这个合数应该被pr[j]这个更小的质数筛掉。另外,当pr[j]|i时,pr[j]是i的最小质因子。否则pr[j]是pr[j]i的最小质因子。
我们沿用线性筛的过程,考虑这个问题。首先,线性筛筛出质数的时候,我们需要求这个积性函数在质数处的取值。其次,在for循环中,设k=pr[j],当k|i时,由上面的讨论,k是i的最小质因子,设k在i中的幂次为a,那么
。
否则,因为k是质数,那么gcd(k, i) = 1,那么f(ki) =f(k)f(i)。
懂了吗?我们来看下一步
线性筛欧拉函数
例:O(n)求出欧拉函数在1∼n处的所有取值。
由于,为一个定值,所以线性筛欧拉函数很容易。
代码如下:
for(int i=2;i<=n;i++){
if(isp[i]==0){
pr[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt&&pr[j]*i<=n;j++){
isp[i*pr[j]]=1;
if(i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j];
break;
}
phi[i*pr[j]]=phi[i]*(pr[j]-1);
}
}
线性筛莫比乌斯函数
例:O(n)求出莫比乌斯函数在1∼n处的所有取值。
由于,为一个定值,所以也很容易。
for(int i=2;i<=n;i++){
if(isp[i]==0){
pr[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&pr[j]*i<=n;j++){
isp[i*pr[j]]=1;
if(i%pr[j]==0) {
mu[i*pr[j]]=0;
break;
}
mu[i*pr[j]]=mu[i]*-1;
}
}
线性筛求约数个数
例:O(n)求出约数个数函数τ在1∼n处的所有取值。
由于,不为一个定值,所以在筛的过程中还要维护i的最小质因子的次数。
for(int i=2;i<=n;i++){
if(isp[i]==0){
pr[++cnt]=i;
tau[i]=2;
g[i]=2;
}
for(int j=1;j<=cnt&&pr[j]*i<=n;j++){
isp[i*pr[j]]=1;
if(i%pr[j]==0){
tau[i*pr[j]]=(g[i]+1)*tau[i]/g[i];
g[i*pr[j]]=g[i]+1;
break;
}
tau[i*pr[j]]=tau[i]*2;
g[i*pr[j]]=2;
}
}
今天的内容终于讲完了,感谢大家的聆听