欧拉函数【学习笔记】

一、引文

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_{d\mid 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取模再求。


 

 

posted @ 2018-10-06 12:03  Dybala21  阅读(440)  评论(0编辑  收藏  举报