euler欧拉函数学习入门

本文介绍了欧拉函数Euler phi的基本概念和性质,包括计算公式、欧拉定理与费马小定理。还探讨了Euler函数在求最大公倍数、最简真分数及特定条件下的数的个数等问题,并给出了相应问题的解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


euler函数:对于一个数n,euler(n)的求的是在【1,n】之间与n互质的数的个数,公式为:euler(n) = n * (1 - 1/ p1) * (1 - 1/p2) * (1 - 1/p3) * ......*(1- 1/pn),其中p1,p2,.....pn是n的质因数,不重叠;

实现的算法是

typedef long long ll;
//直接求解欧拉函数
ll euler(ll x){ //返回euler(n) 
     ll ans = x;
     for(ll i = 2;i*i <= x;i ++){
         if(x%i == 0){
             res = res / i * (i-1);//先进行除法是为了防止中间数据的溢出 
             while(x % i == 0) x/=i;
         }
     }
     if(x > 1) res = res / x * (x-1);//未除尽就说明x是素数,之后进行算式
     return res;
}

//筛选法打欧拉函数表 
#define Max 1000001
ll euler[maxn];
void Init(){ 
     euler[1] = 1;
     for(ll i = 2;i < maxn;i ++)
       euler[i] = i;
     for(ll i = 2;i < maxn;i++)
        if(euler[i] == i)
           for(ll j = i;j < Max;j += i)
              euler[j] = euler[j] / i * (i-1);//先进行除法是为了防止中间数据的溢出 
}

更快的时候就是用素数表进行euler函数的运算:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 50000 + 10;
bool is_prime[maxn];
int prime[maxn];
int len = 0;
void Init()//先打个50000的素数表
{
    memset(is_prime,true,sizeof(is_prime));
    is_prime[0] = is_prime[1] = false;
    for(int i = 2; i < maxn ; i ++)
    {
        if(is_prime[i])
        {
            prime[len ++] = i;
            for(int j = i * 2; j < maxn ; j += i)
            is_prime[j] = false;
        }
            
    }
}
ll euler(ll x)
{
    ll ans = x;
    for(ll i = 0 ; prime[i] * prime[i] <= x && i < len; i ++)
    {
        if(x % prime[i])
        {
            ans = ans /prime[i] * (prime[i] - 1);
            while(x % prime[i] == 0)
                x /= prime[i];
        }
    }
    if(x > 1)
        ans = ans / x * (x - 1);
    return ans;
}
int main()//因为公式中的是x的质因数,可以先打一个素数表,之后走素数表就行了
{
    Init();
    cout << euler(2) << endl;
    return 0;
}


以上就是euler函数的实现方法,有打表和直接求;


euler的性质:

先写出两个常用定理:

1.euler定理:对于两个互质的数a,m(m>= 2),有a^(euler(m)) 1 (mod m);

2.费马小定理:当m为质数的时候a^(m - 1)   1 (mod m),因为当m为质数的时候,euler(m) = m - 1;

3.如果p是质数,那么euler(p) = p - 1;推广:p是质数,a是一个正整数,那么 euler(p^n)= p^n - p^(n - 1);

4.如果m,n是互质的两个正整数,那么euler(n*m)= euler(n)*euler(m);

5.对于一个数n,euler(n)的求的是在【1,n】之间与n互质的数的个数,公式为:euler(n) = n * (1 - 1/ p1) * (1 - 1/p2) * (1 - 1/p3) * ......*(1- 1/pn),其中p1,p2,.....pn是n的质因数,不重叠;

6.当n为奇数时,有euler(2n)= euler(n);

7.若n是一个大于2的正整数,那么euler(n)是偶数,即euler(n)%2 == 0;



euler的应用:

1.运用euler求gcd;求小于n的数 并且与n的最大公倍数gcd为i的个数 : 假设是gcd(x,n) == i ,(x < n);对于n来说,因数是i,那么说明 n/i 和 x/i互质,对于gcd(x,n) == i,如果i 一定,要 求 x的个数,就相当于求 与n/i互质的数的个数,即 x/i的个数,因为x<=n,要 求的个数就是euler(n/i)的数目; 

2.求最简真分数,即 分子小于分母 并且 分子和分母互质的分数的个数 :   就相当于   对于一个固定的分母n,求出比小且与他互质的数的个数,即euler(n);



GCD

题意:输入一个n,m,然后要求比n小的数与n的gcd大于m的个数,即gcd(x,n)>= m,其中x 属于【1,n】,求x的个数;

思路:遍历一遍,然后判断因数是不是 大于等于m,是的话就加上;

#include<iostream>
#include<cstdio>
#include<cmath>
const int maxn = 3000000 + 10;
using namespace std;
typedef long long ll;
ll euler(ll x)
{
    ll ans = x;
    for(int i = 2; i <= sqrt(x) ; i ++)
    {
        if(x % i == 0)
        {
            ans = ans / i * (i - 1);
            while(x% i == 0)
            x /= i;
        }
    }
    if(x > 1)
        ans = ans / x * (x - 1);
    return ans;
}

int main()
{
    ll n,m;
    int Tcase;
    scanf("%d",&Tcase);
    for(int ii = 1; ii <= Tcase ; ii ++)
    {
        scanf("%I64d%I64d",&n,&m);
        double t = sqrt(n);
        ll temp = euler(m);
        ll ans = 0;
        int i;
        for(i = 1; i < t; i ++)
        {
            if(n % i == 0)
            {
                if(i >= m)
                    ans += euler(n/i);
                if(n / i >= m )
                    ans += euler(i);
            }
        }
        if(i * i == n && i >= m)
        {
            ans += euler(i);
        }
        cout << ans << endl;
    }
    return 0;
}

The Euler Function


题意:直接的一个求euler,输入一个a,b,求出euler(x)之和,x属于【a,b】;

思路:打表一下;

#include<iostream>
#include<cstdio>
#include<cmath>
const int maxn = 3000000 + 10;
using namespace std;
typedef long long ll;
//ll euler(ll x)
//{
//    ll ans = x;
//    for(int i = 2; i <= sqrt(x) ; i ++)
//    {
//        if(x % i == 0)
//        {
//            ans = ans / i * (i - 1);
//            while(x% i == 0)
//            x /= i;
//        }
//    }
//    if(x > 1)
//        ans = ans / x * (x - 1);
//    return ans;
//}
int euler[maxn];
void Init()
{
    for(int i = 2; i < maxn ; i ++)
        euler[i] = 0;
    euler[1] = 1;
    for(int i = 2; i < maxn ; i ++)
    {
        if(!euler[i])
        for(int j = i ; j < maxn ; j += i)
        {
            if( ! euler[j])
                euler[j] = j;
            euler[j] = euler[j] /i *(i - 1);
        }
    }
}
int main()
{
    Init();
    int n,m;
    while( ~ scanf("%d%d",&n,&m) )
    {
        ll ans = 0;
        for(int i = n ; i <= m ; i ++)
        {
            ans += euler[i];
        }
        cout << ans << endl;
    }
    return 0;
}

找新朋友

题意:新朋友就是小于n,并且与互质的数,求新朋友的个数;

思路:因为x不能取n,所以gcd不能等于1,之后直接euler;

#include<iostream>
#include<cstdio>
#include<cmath>
const int maxn = 3000000 + 10;
using namespace std;
typedef long long ll;
ll euler(ll x)
{
    ll ans = x;
    for(int i = 2; i <= sqrt(x) ; i ++)
    {
        if(x % i == 0)
        {
            ans = ans / i * (i - 1);
            while(x% i == 0)
            x /= i;
        }
    }
    if(x > 1)
        ans = ans / x * (x - 1);
    return ans;
}

int main()
{
    ll n,m;
    int Tcase;
    scanf("%d",&Tcase);
    for(int ii = 1; ii <= Tcase ; ii ++)
    {
        scanf("%I64d",&n);
        double t = sqrt(n);
        ll ans = 0;
        int i ;
        for(i = 1; i < t; i ++)
        {
            if(n % i == 0)
            {
                if(i > 1)
                ans += euler(n/i);//公因数是i;
                if(n / i > 1)
                ans += euler(i);//公因数是n /i;
            }
        }
        if(i * i == n && i != 1)
        {
            ans += euler(i);
        }
        cout << n - ans << endl;
    }
    return 0;
}


Goffi and GCD


题意:求满足    gcd(na,n)×gcd(nb,n)=nk  

的a,b的数目:其中a,b属于【1,n】;

思路:gcd(0,x) == x,所以gcd(n - a ,n)是可以等于 n的;分析:k == 2的时候,只有a == b == n的时候,才能使得n * n == n ^ 2;

当k > 2的时候,除了n == 1的数都是0,因为前面最多是n ^2;

于是就剩下对k == 1的分析了,即gcd(na,n)×gcd(nb,n)=n;





当n % i == 0的时候,如果 i * i  != n,那么就存在两种情况: gcd(n - a,n) == i && gcd(n - b,n) == n /i  ||  gcd(n - a,n) == n/i && gcd(n - b,n) == i;
                                  反之,就是一种情况了:gcd(n - a,n) == gcd(n - b,n) ==  i了;
而对于类似 gcd(n - a,n) == i,而言,就相当于求小于n的数x ,求满足gcd(x,n) == i的数有多少个,用euler(n/i)就行了;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 1000000000 + 7
ll euler(ll x)
{
    int ans = x;
    for(int i = 2; i * i <= x; i ++)
    {
        if(x % i == 0)
        {
            ans = ans / i * (i - 1);
            while(x % i == 0)
            {
                x /= i;
            }
        }
    }
    if(x > 1)
    {
        ans = ans /x * (x - 1);
    }
    return ans;
}
int main()
{
    ll n,k;
    while( ~ scanf("%I64d%I64d",&n,&k) )
    {
        if(n == 1 )
        {
            cout << 1 << endl;
            continue;
        }
        if(k > 2)
        {
            cout << 0 << endl;
            continue;
        }
        if(k == 2)
        {
            cout << 1 << endl;
            continue;
        }
        double t = sqrt(n);
        ll ans = 0;
        int i;
        for(i = 1; i < t ; i ++)
        {
            if( n % i == 0 )//因为两个因数没有一个是能够达到n
            {
                ans += euler(i) * euler(n / i) * 2;
                ans %= mod;
            }
        }
        if(i * i == n)
        {
            ans += euler(i) * euler(i);//当两个因数相同的时候;
        }
        ans %= mod;
        cout << ans << endl;
    }
    return 0;
}


Longge's Problem 

题意:  ∑gcd(i, N) 1<=i <=N. 
思路:找到n 的因数 i,然后然后与n的gcd == i的个数为  euler(n / i);
所以加上  i * gcd(n / i)就行了,记得特判一下i * i == n的情况;

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;
typedef long long ll;
ll euler(ll x)
{
    ll ans = x;
    for(int i = 2; i <= sqrt(x) ; i ++)
    {
        if(x % i == 0)
        {
            ans = ans / i * (i - 1);
            while(x% i == 0)
            x /= i;
        }
    }
    if(x > 1)
        ans = ans / x * (x - 1);
    return ans;
}
int main()
{
    ll n;
    while( ~ scanf("%I64d",&n) )
    {
        ll ans = 0;
        int i ;
        for(i = 1; i < sqrt(n) ; i ++)
        {
            if(n % i == 0)
            {
                ans += i * euler(n/i) + n / i * euler(i);
            }
        }
        if(i * i == n)
        {
            ans += i * euler(i);
        }
        cout << ans << endl;
    }
    return 0;
}

Stern-Brocot Tree

不知道法里数列是什么。
题意:对于这样的一棵树的结构,有如下性质:
           1. 关于1/1左右对称,左边的都是最简真分数小于1,右边都是相对于左边大于1的分数;
           2. 只对左边的一半分析,第i行行添加的都是以i为分母的真分数,因为分子是小于分母i的,并且是最简真分数,所以分子的就相当于小于i并且与i互质的数,所以每行添加的分数的个数就是euler(i);
思路:打表;

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000000 + 10;
typedef long long ll;
ll euler[maxn];
ll ans[maxn];
void Init()
{
    memset(euler,0,sizeof(euler));
    euler[1] = 1;
    for(int i = 2 ; i < maxn ; i ++)
    {
        if( !euler[i])
        {
            for(int j = i; j < maxn ; j += i)
            {
                if(!euler[j])
                    euler[j] = j;
                euler[j] = euler[j] / i * (i- 1);
            }
        }
    }
    ans[1] = 1;
    for(int i = 2; i < maxn ;i ++)
    {
        ans[i] = ans[i - 1] + euler[i];
    }
}
int main()
{
    Init();
    int n;
    while( ~ scanf("%d",&n) )
    {
        cout << ans[n] * 2 + 1 << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值