筛法(线性筛,厄拉多塞筛)

在前前前前前前…的博客中,我们主要谈了欧拉筛和埃氏筛.

今天我们主要来聊一聊线性筛和厄拉多塞筛(其实和埃氏筛和欧拉筛本质上没区别哎).其实在这两种筛法中厄拉多塞筛最好懂(就连本蒟蒻一看代码就明白了…别看这个名字,容易糊弄人)

首先是厄拉多塞筛"粉墨登场":::::::

厄拉多塞筛

厄拉多塞是个人名,因为这种筛法是他提出来的.

我们先讲一下大致的思路,我们example(举个栗子):

有一张写了2~n之间所有数的表,其中就包括这2和n.然后,我们从2开始一直到n划掉2的倍数.划完之后,我们发现3没有被划去,就证明3是质数,然后我们就在3~n当中划去是3倍数的数,以此类推.

此时,厄拉多塞筛的时间复杂度为O(n+\frac{n}{2}+\frac{n}{3}+\frac{n}{4}+...+)=O(n log log n)

既然已经动了厄拉多塞筛的筛质数的方法,我们下面来就有请我们最喜欢的代码大军:

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,那么

                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        f[ki]=\frac{f(k^{a+1})}{f(k^{a})}f(i)

否则,因为k是质数,那么gcd(k, i) = 1,那么f(ki) =f(k)f(i)。

懂了吗?我们来看下一步

线性筛欧拉函数

例:O(n)求出欧拉函数在1∼n处的所有取值。

由于\frac{\varphi (k^{a+1})}{\varphi (k^{a})}=k,为一个定值,所以线性筛欧拉函数很容易。

代码如下:

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处的所有取值。

由于\frac{\mu (k^{a+1})}{\mu(k^{a})}=0,为一个定值,所以也很容易。

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处的所有取值。

由于\frac{\tau (k^{a+1})}{\tau (k^{a})}=\frac{a+2}{a+1},不为一个定值,所以在筛的过程中还要维护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;
    }
}

今天的内容终于讲完了,感谢大家的聆听

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cqbzcyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值