欧拉函数

欧拉函数的性质

1、若n是素数p的k次幂,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质
2、欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)
3、当n为奇数时,φ(2n)=φ(n)
4、p是素数,φ(p) = p - 1,φ(p)称为p的欧拉值 φ(1)=1
5、利用这种线性筛法求欧拉函数,需要用到以下几个性质:
若(N%a==0 && (N/a)%a==0) 则有:E(N)=E(N/a)*a;
若(N%a==0 && (N/a)%a!=0) 则有:E(N)=E(N/a)*(a-1);
其中a是N的质因数。

我们通过性质1可以得到求解单个欧拉值的代码

注意那个while(a%i==0)这个循环的作用是 同样的i只能计算一次
比如12==2*2*3
这个2就只能计算一次
我开始看很多博客都没有讲到这个while的作用 想了很久才明白

int solve(int n){
     int res=n,a=n;
     for(int i=2;i*i<=a;i++){
         if(a%i==0){
             res=res/i*(i-1);
             while(a%i==0)
                a/=i;
         }
     }
     if(a>1) res=res/a*(a-1);
     return res;
}

接下来我们将1-n的筛选
首先O(nlglgn)的算法

void solve(){
     euler[1]=1;
     for(int i=2;i<Max;i++)
       euler[i]=i;
     for(int i=2;i<Max;i++)
        if(euler[i]==i)
           for(int j=i;j<Max;j+=ie)
              euler[j]=euler[j]/i*(i-1);
}

这个筛选很简单 可以通过性质1得到
再来一个最终级的代码
O(n)复杂度的

void solve()
{
    int i,j;
    phi[1]=1;
    for(i=2; i<=N; i++) ///相当于分解质因式的逆过程
    {
        if(!mark[i])
        {
            prime[++tot]=i;///筛素数的时候首先会判断i是否是素数。
            phi[i]=i-1;///当 i 是素数时 phi[i]=i-1
        }
        for(j=1; j<=tot; j++)
        {
            if(i*prime[j]>N)  break;
            mark[i*prime[j]]=1;///确定i*prime[j]不是素数
            if(i%prime[j]==0)///接着我们会看prime[j]是否是i的约数
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else  phi[i*prime[j]]=phi[i]*(prime[j]-1);
       ///其实这里prime[j]-1就是phi[prime[j]],利用了欧拉函数的积性
        }
    }
}

我们先看这个代码的原理
通过性质4可以得到 !mark[i]可以得出i是素数 于是phi[i]的欧拉值就是i-1就是了

if(!mark[i])
{
     prime[++tot]=i;///筛素数的时候首先会判断i是否是素数。
     phi[i]=i-1;///当 i 是素数时 phi[i]=i-1
}

我们在看看这段代码

if(i%prime[j]==0)///接着我们会看prime[j]是否是i的约数
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else  phi[i*prime[j]]=phi[i]*(prime[j]-1);

这个是通过性质5得到的
套用一下性质就好了

你可能感兴趣的:(欧拉函数)