【数论】欧拉函数

1、欧拉函数

给定 n 个正整数 ai,请你求出每个数的欧拉函数。

欧拉函数的定义

1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中,N = p 1 a 1 {p_1}^{a_1} p1a1 p 2 a 2 {p_2}^{a_2} p2a2 p m a m {p_m}^{a_m} pmam,则:
ϕ(N) = N × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p11 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p21 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm1

输入格式

第一行包含整数 n。

接下来 n 行,每行包含一个正整数 ai。

输出格式

输出共 n 行,每行输出一个正整数 ai 的欧拉函数。

数据范围

1≤n≤100,
1≤ai≤2× 1 0 9 10^9 109

输入样例:
3
3
6
8
输出样例:
2
2
4

【思路分析】

可以通过容斥原理推导出欧拉函数

我们求1-N中与N互质的数的个数,可以让N除掉所有其分解质数序列的底数的倍数

例如N 分解质因数是 p 1 α 1 {p_1}^{α_1} p1α1 p 2 α 2 {p_2}^{α_2} p2α2× … × p m α m {p_m}^{α_m} pmαm

我们只需让 N - N p 1 \frac{N}{p_1} p1N - N p 2 \frac{N}{p_2} p2N - … - N p m \frac{N}{p_m} pmN

但是这样的话我们就重复减了某个数,这个数既是 p i p_i pi又是 p j p_j pj的倍数,因此还需要再加上去

+ N p 1 p 2 \frac{N}{{p_1}{p_2}} p1p2N + N p 1 p 3 \frac{N}{{p_1}{p_3}} p1p3N

但是又重复加上了某个数既是 p i p_i pi还是 p j p_j pj还是 p k p_k pk的倍数,因此还需要再减去

- N p 1 p 2 p 3 \frac{N}{{p_1}{p_2}{p_3}} p1p2p3N - N p 1 p 2 p 4 \frac{N}{{p_1}{p_2}{p_4}} p1p2p4N - …

依次进行下去,直到分母是 p 1 p_1 p1 p m p_m pm的乘积的时候

而上面的式子化简即可得到欧拉函数:

ϕ(N) = N × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p11 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p21 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm1

import java.util.*; 

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        while(n-- > 0) {
            int a = sc.nextInt();
            int res = a; // 初始化结果为 a
            // 试除法分解质因数
            for(int i = 2; i <= a / i; i++) {
                if(a % i == 0) {
                    // 找到一个质因数 i,更新 res = res / i * (i-1)
                    res = res / i * (i - 1);
                    // 除尽所有的 i
                    while(a % i == 0) {
                        a /= i;
                    }
                }
            }
            // 如果剩余的 a > 1,说明 a 是一个大于根号a的质数,且只能有一个
            if(a > 1) {
                res = res / a * (a - 1);
            }
            System.out.println(res);
        }
        sc.close();
    }
}

2、 筛法求欧拉函数

给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。

输入格式

共一行,包含一个整数 n。

输出格式

共一行,包含一个整数,表示 1∼n 中每个数的欧拉函数之和。

数据范围

1≤n≤ 1 0 6 10^6 106

输入样例:
6
输出样例:
12

【思路分析】

这一题可以改造线性筛法求1-n的所有质数的过程来完成计算1-n中每个数的欧拉函数之和

这是线性筛法求质数的代码

import java.io.*;
import java.util.*;
public class Main {
    static final int N = 1000010;
    static int prime[] = new int[N];
    static boolean st[] = new boolean[N];
    static int idx;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        function(sc.nextInt());
        System.out.println(idx);
        sc.close();
    }
    public static void function(int n) {
        for(int i = 2; i<= n; i++) {
            //如果i不是合数,则i为质数
            if(!st[i]) {
                prime[idx++] = i;
            }
            //从小到大枚举质数,循环条件为prime[j] <= n / i的原因是在循环体里面用prime[j]筛掉prime[j] * i的质数
            for(int j = 0; prime[j] <= n / i; j++) {
                st[prime[j] * i] = true;
                if(i % prime[j] == 0) {
                    break;
                }
            }
        }
    }
}

最外层枚举了从2开始到n结束的所有的数

进入循环后,先判断i是否为质数,如果是质数的话,质数的欧拉数显然只有p - 1个

例如,5和1 2 3 4互质

进入内层循环后,判断i % prime[j]是否等于0

  • 如果等于0

    ϕ(i) = i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p11 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p21 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm1

    ϕ(prime[j] * i ) = prime[j] * i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p11 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p21 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm1 = ϕ(i) × prime[j]

    因为prime[j] 是 i的一个质因数,而欧拉函数和质因数的指数无关

  • 如果不等于0

    ϕ(i) = i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p11 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p21 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm1

    ϕ(prime[j] * i ) = prime[j] * i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p11 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p21 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm1 × p j − 1 p j \frac{p_j - 1}{p_j} pjpj1 = ϕ(i) × prime[j] - 1

因此代码为

import java.util.*;
public class Main {
    static final int N = 1000010;
    static int[] prime = new int[N];
    static boolean st[] = new boolean[N];
    static int[] phi = new int[N];
    static int idx;
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        System.out.println(function(sc.nextInt()));
        sc.close();
    }
    public static long function(int a) {
        phi[1] = 1;
        for(int i = 2; i <= a; i++) {
            //如果i是质数
            if(!st[i]) {
                prime[idx++] = i;
                phi[i] = i - 1;
            }
            //从小到大枚举所有质数
            for(int j = 0; prime[j] <= a / i; j++) {
                st[prime[j] * i] = true;
                if(i % prime[j] == 0) {
                    phi[prime[j] * i] = phi[i] * prime[j];
                    break;
                }
                phi[prime[j] * i] = phi[i] * (prime[j] - 1);
            }
        }
        long res = 0;
        for(int i = 1; i <= a; i++) {
            res += phi[i];
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值