【ICPC模板】欧拉函数

目录

欧拉函数

质因数分解实现(适用于单次求值)

打表实现

欧拉函数的性质

偶数性质


 

欧拉函数

质因数分解实现(适用于单次求值)

含义:小于n且与n互素的正整数个数。

积性:如果gcd(n, m) = 1,则

求值公式:

特别地:

注意:此实现需要提前调用euler()筛素数函数

代码:

#include <iostream>
#include <cstring>

using namespace std;

typedef long long LL;

// 欧拉线性筛素数
const int LIM = 1e6 + 10;       /* The limit of number to be test */
int prime[LIM / 3];
bool notPrime[LIM];
int primepos;
void euler() {
    int tmp = 0;
    memset(notPrime, false, sizeof notPrime);
    primepos = 0;
    for (int i = 2; i < LIM; i++) {
        if (!notPrime[i]) prime[primepos++] = i;
        for (int j = 0; j < primepos && (tmp = i * prime[j]) < LIM; j++) {
            notPrime[tmp] = true;
            if (!(i % prime[j])) break;
        }
    }
}

LL fastPow(LL base, LL p) {
    LL ret = 1;
    do {
        if (p & 1) ret = ret * base;
        base = base * base;
    } while (p >>= 1);
    return ret;
}

// 欧拉函数
int phi(int n) {
    int ret = 1;
    for (int i = 0; i < primepos && n; i++) {
        int cnt = 0;
        while (!(n % prime[i])) n /= prime[i], cnt++;
        if (cnt) ret *= fastPow(prime[i], cnt - 1) * (prime[i] - 1);
    }
    return ret;
}

int main(void) {
    euler();
    for (int i = 1; i <= 10; i++) {
        printf("phi(%d) = %d\n", i, phi(i));
    }
    return 0;
}

 

下面是更简便且时间复杂度为O(sqrt(n))的方法:

LL phi(LL n) {
    LL ret = 1;
    for (LL i = 2; i * i <= n; i++) {
        if (!(n % i)) {
            ret *= i - 1;
            n /= i;
            while (!(n % i))
                ret *= i, n /= i;
        }
    }
    if (n ^ 1) ret *= (n - 1);
    return ret;
}

Time Complexity: O(Sqrt(n))

 

 

打表实现

递推性质:

我们假设对于每个数n,它的最小质因子是p,接下来的递推分两类讨论,

如果

如果

简单证明:

对于一个数m,可以将其某个质因子拆出来,如下:

则如果p | m,即k不等于0,那么有:

如果k等于0,则m中不含有因子p,显然gcd(m, p) = 1,则可直接使用欧拉函数的积性公式:

令n = m * p即得到上述递推式。

证毕。

如果取n的最小质因子p,则可以通过筛素数打表来找到每个数n的p,这一点十分便于实现,于是我们取p为n的最小质因子。

代码:

#include <iostream>

using namespace std;

inline int mini(int a, int b) {return a < b ? a : b;}

// 使用欧拉线性筛打最小质因子表
const int LIM = 1e6 + 10;       /* The limit of number to be test */
int prime[LIM / 3];
int miniFactor[LIM];
int primepos;
void euler() {
    int tmp;
    for (int i = 2; i < LIM; i++) {
        if (!miniFactor[i]) prime[primepos++] = i, miniFactor[i] = i;
        for (int j = 0; j < primepos && (tmp = i * prime[j]) < LIM; j++) {
            miniFactor[tmp] = mini(prime[j], miniFactor[i]);
            if (!(i % prime[j])) break;
        }
    }
}

int phi[LIM];
void phiTable() {
    phi[1] = 1;
    for (int i = 2; i < LIM; i++) {
        phi[i] = phi[i / miniFactor[i]]
            * ((i / miniFactor[i]) % miniFactor[i] ? miniFactor[i] - 1 : miniFactor[i]);
    }
}

int main(void) {
    // mini factor test
    euler();
    for (int i = 1; i <= 10; i++) {
        printf("the mini factor of %d is %d\n", i, miniFactor[i]);
    }
    printf("the mini factor of 47 is %d\n", miniFactor[47]);
    printf("the mini factor of 61 is %d\n", miniFactor[61]);
    printf("the mini factor of 561 is %d\n", miniFactor[561]);

    // phi table test
    phiTable();
    for (int i = 1; i <= 10; i++) {
        printf("phi(%d) = %d\n", i, phi[i]);
    }
    for (int i = 1; i < LIM; i++)
        if (phi[i] <= 0) printf("Overflow at %d\n", i);
    return 0;
}

Time Complexity: O(n)

 

上面的方法实际上复杂度为2n,因为预先打出了一个最大素因子表,但是实际上可以直接将欧拉函数打表与素数筛选操作合并,如下。

直接打表:

#include <iostream>
#include <algorithm>

using namespace std;

const int LIM = 1e6 + 10;
int pricur = 0;
int prime[LIM / 3];
int phi[LIM];
void build_phi() {
    int tmp;
    phi[1] = 1;
    for (int i = 2; i < LIM; i++) {
        if (!phi[i]) {
            phi[i] = i - 1;
            prime[pricur++] = i;
        }
        for (int j = 0; (tmp = i * prime[j]) < LIM; j++) {
            if (i % prime[j]) {
                phi[tmp] = phi[i] * (prime[j] - 1);
            } else {
                phi[tmp] = phi[i] * prime[j];
                break;
            }
        }
    }
}

int main(void) {
    build_phi();
    for (int i = 1; i < 100; i++) {
        printf("[%d] : %d\n", i, phi[i]);
    }
    return 0;
}

Time Comlexity: O(n)

 

 

欧拉函数的性质

偶数性质

除了1和2的欧拉函数值为1外,其余正整数的欧拉函数值均为偶数,因为对于数a如果满足a < n且gcd(a, n) = 1,即a与n互质,那么一定有gcd(n – a, n) = 1成立,那么对于小于n的与n互质的正整数,总是可以用n减去它找到另外一个与n互质的正整数,如果担心n – a = a是否会导致上面的规律被破坏,可以特别考虑一下,因为唯一一个满足n – x = x的数x是当n为偶数时的n / 2,但是显然这个数不与n互质,有gcd(n / 2, n) = n / 2。因此上面的规律可以确定存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值