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

我们先来看一下最经典的埃拉特斯特尼筛法。时间复杂度为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;//因为 i 为素数 所以 i 的倍数  都是 合数 
        }
    }
    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];//初始化 
void Prime(int n)
{
    int cnt=0;
    memset(vis,0,sizeof(vis));
    for(int i=2;i<n;i++)
    {
        if(!vis[i])// 如果 i 是素数 
        prime[cnt++]=i;// 存入 数组 里面
        for(int j=0;j<cnt&&i*prime[j]<n;j++)
        {
            vis[i*prime[j]]=1;// 根据 素数的倍数 都是合数  , 任何合数 都可以写成 多个 素数 的  乘积   i与 prime【j】 即 素数的 成绩
            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以内的素数个数。

强调内容
欧拉函数:在数论中,对正整数n,欧拉函数是少于或等于n的数中与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). 证明结束

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

#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;
            }
            else
            phi[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;
    }
}

分析:对于整数n,如果x(x

/*利用欧拉函数即可求解,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 1000000007
LL 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值