zoukankan      html  css  js  c++  java
  • 欧拉函数【学习笔记】

    一、引文

    1.欧拉函数:指不超过n且与n互素的正整数的个数,其中,n是一个正整数。


    二、相关知识

    1.算数函数:定义在所有正整数上的函数称为算数函数。

    2.乘性函数(积性函数):对算数函数ƒ 如果满足对任意两个互素的正整数nm,均有ƒ(nm)=ƒ(n)ƒ(m)

    3.完全乘性函数(完全积性函数):对任意的两个正整数nm,均有ƒ(nm)=ƒ(n)ƒ(m)


    三、性质

    1.如果p是素数,那么φ(p)=p-1;反之,如果p是一个正整数且满足φ(p)=p-1,那么p是素数。

    2.如果p是素数,a是一个正整数,那么φ(pa)=pa-pa-1.

      证明:pa不互质的有p×1,p×2,…,p×(pa-1-1),p×pa-1 共pa-1个,那么与它互质的就刚好有pa-pa-1。得证。

    3.欧拉函数是积性函数,但不是完全积性函数。

    4.推论:当n为奇数时,φ(2n)=φ(n)

      证明:由3可知欧拉函数是积性函数,所以φ(2n)=φ(2)φ(n)=φ(n),得证。

    5.设n=(p1)a1(p2)a2 (pk)ak为正整数n的素数幂分解,那么

    φ(n)=n•(1-1/p1)•(1-1/p2)••(1-1/pk)

      有了上面的一些性质,这些证明都比较易证。一般也通过此公式来求解欧拉函数的值。

    6.若gcd(n,i) = 1, 那么gcd(n, n-i)=1.

      证明:(反证法)先假设gcd(nn-i)=kk!=1,那么n%k=0, (n-i)%k=0,可以推出 n = x*k, n-i = y*k,再往下推一步 i = (x-y)*k,这就与gcd(n,i)=1矛盾了。得证。

    7.设n是一个大于2的正整数,那么φ(n)是偶数。

    8.设n为一个正整数,那么

    sum _{d|n}varphi (d) = n​​​​​​​

      证明:假设左边=F(n),先证明F(n)是积性函数,直接把F(m)F(n)用上式左边代入后展开,可以看到最后的展开式就是m*n的各个因子的欧拉函数值相加,所以得到F(n)是积性函数。再根据

    F(p^k) = F(n) = sum_{dmid n}varphi (d)

      展开

     F(p^k) = varphi (1) + varphi (p) +varphi (p^2)+cdots +varphi (p^k)

      根据上述性质2

     F(p^k) = 1 + (p-1)+(p^2-p)+cdots +(p^k-p^{k-1})

      推出

    F(p^k) = p^k

      得证。

    9.欧拉定理:对任何两个互质的正整数a,m(m≥2),有aφ(m)≡1(mod m).


    四、代码实现

    1.纯粹且不优雅的无脑实现

     兄台,你还能再暴力一点吗!

    int phi(int n)
    {
        int rea = n;
        for(int i = 2; i <= n; i++)
        {
            if(n%i == 0)    //找到了素因子
            {
                rea = rea - rea/i;   //rea*(1-1/i)
                do
                {
                    n/=i;
                }
                while(n%i==0);
            }
        }
        return rea;
    }
    

      

     2.线性筛素数表实现

    我只想说,我TM之前学的筛素数都是假的!

    const int MAXN = 1e5;
    bool isPrime[MAXN];
    int Prime[MAXN], Cnt;
    
    void getPrime()
    {
        memset(isPrime, 0, sizeof(isPrime));
        Cnt = 0;
        for(int i = 2; i < MAXN; i++)
        {
            if(!isPrime[i])
            {
                Prime[Cnt++] = i;
            }
            for(int j = 0; j < Cnt && i*Prime[j] < MAXN; j++)
            {
                isPrime[i*Prime[j]] = 1;
                if(i%Prime[j] == 0)
                    break;
            }
        }
    }
    
    int phi(int n)
    {
        int rea = n;
        for(int i = 0; Prime[i]*Prime[i] <= n; i++)
        {
            if(n%Prime[i] == 0)
            {
                rea = rea - rea/Prime[i];  //rea = rea*(1-1/p)
                do
                {
                    n/=Prime[i];
                }while(n%Prime[i] == 0);
            }
        }
        if(n > 1)
            rea = rea - rea/n;
        return rea;
    }
    

      

    3.递推求欧拉函数

    如果频繁的要用欧拉函数值,就需要先打表。

    咦~~,我们好像在哪见过,和埃式筛素数怎么这么像呢~

    可以先预先置所有欧拉函数值都为本身。如果再从小到大遍历的过程中,发现该欧拉函数的值与下标相当,就表明该数是素数,然后再通过欧拉函数的求法,乘以(1-1/p)。然后再把有该素因子的数的欧拉函数的值再修改一下,就可以了。复杂度为O(n*ln(n))

    const int MAXN = 1e5;
    int Phi[MAXN];
    
    int Euler()
    {
        for(int i = 1; i < MAXN; i++)  Phi[i] = i;
        for(int i = 2; i < MAXN; i+=2)  Phi[i]/=2;
        for(int i = 3; i < MAXN; i+=2)
        {
            if(Phi[i] == i) //i是素数
            {
                for(int j = i; j < MAXN; j+=i)
                {
                    Phi[j] = Phi[j]/i*(i-1);     // x*(1-1/i) Phi[j]有素因子i
                }
            }
        }
    }
    

    4.线性欧拉函数值

    void Pre()
    {
        tot = 0;
        memset(isPrime, 1, sizeof(isPrime));
        isPrime[0] = isPrime[1] = 0;
        Phi[1] = 1;
        for(int i = 2; i < maxn; i++)
        {
            if(isPrime[i])
            {
                Prime[tot++] = i;
                Phi[i] = i - 1;
            }
            for(int j = 0; (ll)i * Prime[j] < maxn && j < tot; j++)
            {
                isPrime[i * Prime[j]] = 0;
                if(i % Prime[j] == 0)
                {
                    Phi[i * Prime[j]] = Prime[j] * Phi[i];
                    break;
                }
                Phi[i * Prime[j]] = Phi[i] * (Prime[j] - 1);
            }
        }
    }
    

      


     五、扩展学习

    下面是学习的另外一篇博主的博客,他写的比我好,我把他没证的一点内容证了,在此感谢这位博主。

    https://www.cnblogs.com/linyujun/p/5194170.html

    另一种,比上面更快的方法

    需要用到如下性质

    p为质数

    1. phi(p)=p-1   因为质数p除了1以外的因数只有p,故1至p的整数只有p与p不互质 

    2. 如果i mod p = 0, 那么 phi(i * p)=phi(i) * p         (我不会证明)

    3.若i mod p ≠0,  那么 phi( i * p )=phi(i) * ( p-1 )   (我不会证明)

    (所以我说我会证明都是骗人的╮( ̄▽ ̄)╭)

    第一个证明如下,第二个证明原理相同,只不过i没有素因子p。

    varphi (i*p) = varphi (i/p^k * p^{k+1})

    欧拉函数是积性函数,能分开的前提是gcd(a,b)=1

    = varphi (i/p^k )*varphi ( p^{k+1})

    = varphi (i/p^k )*(p^{k+1}-p^k)

    = varphi (i/p^k )*(p^k-p^{k-1}})*p

    素数次幂的欧拉函数值反向运用

    = varphi (i/p^k )*varphi (p^k)*p

    = varphi (i)*p

    得证。

    const int MAXN = 1e6;
    int Prime[MAXN], Phi[MAXN];
    int tot;    //记录素数个数
    
    void Euler()
    {
        memset(Phi, 0, sizeof(Phi));
        Phi[1] = 1;
        tot = 0;
        for(int i = 2; i < MAXN; i++)
        {
            if(!Phi[i]) //i为素数
            {
                Prime[tot++] = i;
                Phi[i] = i - 1;
            }
            for(int j = 0; j < tot && (long long)Prime[j]*i < MAXN; j++)
            {
                if(i%Prime[j])
                {
                    Phi[ i*Prime[j] ] =  Phi[i]*(Prime[j] - 1);
                }
                else    //  Prime[j]是i的素因子
                {
                    Phi[ i*Prime[j] ] = Phi[i]*Prime[j];
                    break;
                }
            }
        }
    }
    

      

      

    还有以下几个实用的公式

    假设a与p互质

    a^b \%p != (a\%p)^{(b\%p)}\%p

    因为

    a^{varphi (p)}equiv 1(mod p)

    所以

    a^b\%p =(a\%p)^{b\%varphi (p)}\%p

    如果p是素数

    a^b\%p =(a\%p)^{b\%(p-1)}\%p

    这个公式的意义在于在求指数幂的时候,如果b非常大,我们可以不用死求a^b的快速幂,可以先对b取模再求。


     

  • 相关阅读:
    Android 6.0以上动态获取权限
    大学实验3指导:利用单链表实现A-B
    大学课程实验2指导-二叉树的建立与遍历
    大学实验1 哈夫曼编码
    大学java教案之MySQL安装图解
    DrawableAnimation小练习
    Android学习第7天
    Android学习第6天
    There's no Qt version assigned to this project for platform Win32
    OpenBCI 开发环境配置
  • 原文地址:https://www.cnblogs.com/dybala21/p/9744440.html
Copyright © 2011-2022 走看看