zoukankan      html  css  js  c++  java
  • 基础数论--欧拉函数

    在数论,对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目(因此φ(1)=1)

    互质指的就是gcd(a,b)=1。

    所以对于某个数n,求他的欧拉函数可以直接用暴力。

     1 int gcd(int a,int b){
     2     return b==0?a:gcd(b,a%b);
     3 }
     4 int get_phi(int n){
     5     int res=0;
     6     for(int i=1;i<=n;i++){
     7         if(gcd(i,n)==1){
     8             res++;
     9         }
    10     }
    11     return res;
    12 }

    但是这种方法时间复杂度为n*logn,显然过高,只能寻找其他方法

    根据唯一分解定理,n=p1^a1*p1^a2...,只要将n的质因子的倍数都删掉,就是n的欧拉函数的值,这是因为如果某个数和n的约数不为1,那么这个约数也必然是n的因子

    phi[n]=n-n/p1-n/p2-n/p3...

    但是既是p1的倍数又是p2的倍数的数又被删了两遍(被p1删一遍,p2删一遍),把他们加上

    phi[n]=n-n/p1-n/p2-n/p3...+n/p1p2+n/p2p3+...

    而此时又多加一遍了同时是p1 p2 p3的倍数的数(p1+,p2+, p3+,  p1p2-,p2p3-,p1p3- )

    这时候应该已经发现规律了,经过整理之后

    phi[n]=n(1-1/p1)(1-1/p2)...

    观察每一项可发现公式是正确的,代码实现

     1 int get_phi(int n){
     2     int res=n;
     3     for(int i=2;i<=n/i;i++){
     4         if(n%i==0){
     5             res=res/i*(i-1);//据唯一分解定理,这里的res/i一定是整除
     6             while(n%i==0){
     7                 n/=i;
     8             }
     9         }
    10     }
    11     if(n>1){
    12         res=res/n*(n-1);
    13     }
    14     return res;
    15 }

    时间复杂度为sqrt(n)

    有时候会有一种需求,就是求出1~n每个数的欧拉函数的值

    如果用上述的方法,时间复杂度为n*sqrt(n),1e6的数据救过不了了

    这时候线性筛就派上用场了

    线性筛代码:

     1 int get_primes(int n){
     2     for(int i=2;i<=n;i++){
     3         if(!st[i]){
     4             primes[cnt++]=i;
     5         }
     6         for(int j=0;primes[j]<=n/i;j++){
     7             st[primes[j]*i]=true;
     8             if(i%primes[j]==0) break;
     9         }
    10     }
    11     return cnt;
    12 }

    之前已经论证过了,线性筛中每一个数只会被筛到一次,并且不管 i 是合数或质数,在 i 的倍数之前 i 的状态一定被确定了

    如果能够从 i 的phi值得出 pj * i 的phi值就解决了这个问题了

    那么就需要用到以下性质和公式

    1、如果p是质数,那么phi[p]=p-1

    2、phi[n]=n(1-1/p1)(1-1/p2)...

     1 LL get_phis(int n){
     2     LL res=1;
     3     for(int i=2;i<=n;i++){
     4         if(!st[i]){
     5             primes[cnt++]=i;
     6             phi[i]=i-1;
     7             res+=phi[i];
     8         }
     9         for(int j=0;primes[j]<=n/i;j++){
    10             st[primes[j]*i]=true;
    11             if(i%primes[j]==0){
    12                 phi[primes[j]*i]=primes[j]*phi[i];
    13                 res+=phi[primes[j]*i];
    14                 break;
    15             }else{
    16                 phi[primes[j]*i]=(primes[j]-1)*phi[i];
    17                 res+=phi[primes[j]*i];
    18             }
    19         }
    20     }
    21     return res;
    22 }

    算法复杂度O(n)

  • 相关阅读:
    error: declaration of 'cv::Mat R ' shadows a parameter
    Java网络编程(二)
    排序算法(二)
    Java网络编程(一)
    排序算法(一)
    Python文件访问模式
    Python文件与异常
    递归
    SQL命令的六个主要类别
    iOS-生成Bundle包-引入bundle-使用bundle
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14101900.html
Copyright © 2011-2022 走看看