欧拉线性筛法求素数(顺便实现欧拉函数的求值)

我们先来看一下最经典的埃拉特斯特尼筛法。时间复杂度为O(n loglog n)

int ans[MAXN];void Prime(int n){int cnt=0;memset(prime,1,sizeof(prime));prime[0]=prime[1]=0;for(int i=2;i<n;i++){if(vis[i]){ans[cnt++]=i;//保存素数for(int j=i*i;j<n;j+=i)//i*i开始进行了稍微的优化prime[j]=0;//不是素数 }}return ;}显然,当一个数是素数的时候,那么他的倍数肯定是合数,筛选标记即可。从i*i而不从i*2开始,是因为已经i*3,i*2早已经被2,3筛过了。

由此,我们也可以发现有的合数被重复筛除,例如30,2*15筛了一次,5*6重复筛除,所以也就有了我们下面要提到的欧拉线性筛法。

不会重复筛除,是线性O(n)的复杂度。

const int MAXN=3000001;int prime[MAXN];//保存素数 bool vis[MAXN];//初始化 int Prime(int n){int cnt=0;memset(vis,0,sizeof(vis));for(int i=2;i<n;i++){if(!vis[i])prime[cnt++]=i;for(int j=0;j<cnt&&i*prime[j]<n;j++){vis[i*prime[j]]=1;if(i%prime[j]==0)//关键break;}}return cnt;//返回小于n的素数的个数 }

首先,先明确一个条件,任何合数都能表示成一系列素数的积。然后利用了每个合数必有一个最小素因子,每个合数仅被它的最小素因子筛去正好一次。所以为线性时间。代码中体现在:if(i%prime[j]==0)break;prime数组 中的素数是递增的,当 i 能整除 prime[j],那么 i*prime[j+1] 这个合数肯定被 prime[j] 乘以某个数筛掉。因为i中含有prime[j], prime[j] 比 prime[j+1] 小。接下去的素数同理。所以不用筛下去了。在满足i%prme[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。

如果还不是很理解,可以手动模拟一下。

直接应用的一个简单例子。求n以内的素数个数。

先给出一个结论:

设P是素数,

若p是x的约数,则E(x*p)=E(x)*p.

若p不是x的约数,则E(x*p)=E(x)*E(p)=E(x)*(p-1).

证明如下:

E(x)表示比x小的且与x互质的正整数的个数。*若p是素数,E(p)=p-1。*E(p^k)=p^k-p^(k-1)=(p-1)*P^(k-1)证:令n=p^k,小于n的正整数数共有n-1即(p^k-1)个,其中与p不质的数共[p^(k-1)-1]个(分别为1*p,2*p,3*p…p(p^(k-1)-1))。所以E(p^k)=(p^k-1)-(p^(k-1)-1)=p^k-p^(k-1).得证。*若ab互质,则E(a*b)=E(a)*E(b),欧拉函数是积性函数.*对任意数n都可以唯一分解成n=p1^a1*p2^a2*p3^a3*…*pn^an(pi为素数).则E(n)=E(p1^a1)*E(p2^a2)*E(p3^a3)*…*E(pn^an) =(p1-1)*p1^(a1-1)*(p2-1)*p2^(a2-1)*…*(pn-1)*pn^(an-1) =(p1^a1*p2^a2*p3^a3*…*pn^an)*[(p1-1)*(p2-1)*(p3-1)*…*(pn-1)]/(p1*p2*p3*…*pn) =n*(1-1/p1)*(1-1/p2)*…*(1-1/pn)* E(p^k) =(p-1)*p^(k-1)=(p-1)*p^(k-2)*p E(p^(k-1))=(p-1)*p^(k-2)->当k>1时,E(p^k)=E(p*p^(k-1))=E(p^(k-1))*p. (当k=1时,,E(p)=p-1.)由上式: 设P是素数, 若p是x的约数,则E(x*p)=E(x)*p. 若p不是x的约数,则E(x*p)=E(x)*E(p)=E(x)*(p-1). 证明结束。

?pid=2824 具体的应用

求一段区间的欧拉函数的和。

#include<iostream>#include<string>#include<cstring>using namespace std;const int MAXN=3000001;int prime[MAXN];//保存素数 bool vis[MAXN];//初始化 int phi[MAXN];//欧拉函数 void Prime(int n){int cnt=0;memset(vis,0,sizeof(vis));for(int i=2;i<n;i++){if(!vis[i]){prime[cnt++]=i;phi[i]=i-1;// if p is prime,then phi[i]=i-1}for(int j=0;j<cnt&&i*prime[j]<n;j++){__int64 k=i*prime[j];vis[k]=1;if(i%prime[j]==0)//关键{phi[k]=phi[i]*prime[j];break;}elsephi[k]=phi[i]*(prime[j]-1);}}}int main(){int a,b;Prime(3000000);while(cin>>a>>b){__int64 ans=0;for(int i=a;i<=b;i++)ans+=phi[i];cout<<ans<<endl;}}

还有?pid=3501

分析:对于整数n,如果x(x<n)与n互质,那么(n-x)也与n是互质的;同理如果x(x<n)与n不互质,那么(n-x)也与n是不互质的。知道这个之后就可以得出:在0<x<n时,存在这样的x与n互质的个数假设为num(可以通过欧拉函数求得),那么所有与n互质的x的和sum=num*n/2.

/*利用欧拉函数即可求解,1~n比n小且与n互素的数的总和为sum(n) = n * phi(n) / 2;那么可以先求出1~n-1的总和,然后减去sum(n)即可。*/#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;typedef long long LL;#define MOD 1000000007LL n;LL Eular(LL n) {LL cnt=1;for(int i=2; i*i<=n; i++) {if(n%i==0) {cnt*=(i-1);n/=i;while(n%i==0) {n/=i;cnt*=i;}}}if(n>1)cnt*=(n-1);return cnt;}int main() {while(~scanf("%lld",&n)&&n) {LL ans=(n+1)*n/2-n;ans-=Eular(n)*n/2;printf("%I64d\n",(ans%MOD+MOD)%MOD);}return 0;}

版权声明:本文为博主原创文章,未经博主允许不得转载。

值不值得,真是不足为外人道,自己心里有数就行。

欧拉线性筛法求素数(顺便实现欧拉函数的求值)

相关文章:

你感兴趣的文章:

标签云: