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}
p1p1−1 ×
p
2
−
1
p
2
\frac{p_2 - 1}{p_2}
p2p2−1 × … ×
p
m
−
1
p
m
\frac{p_m - 1}{p_m}
pmpm−1
输入格式
第一行包含整数 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} p1p1−1 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p2−1 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm−1
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} p1p1−1 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p2−1 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm−1
ϕ(prime[j] * i ) = prime[j] * i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p1−1 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p2−1 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm−1 = ϕ(i) × prime[j]
因为prime[j] 是 i的一个质因数,而欧拉函数和质因数的指数无关
-
如果不等于0
ϕ(i) = i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p1−1 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p2−1 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm−1
ϕ(prime[j] * i ) = prime[j] * i × p 1 − 1 p 1 \frac{p_1 - 1}{p_1} p1p1−1 × p 2 − 1 p 2 \frac{p_2 - 1}{p_2} p2p2−1 × … × p m − 1 p m \frac{p_m - 1}{p_m} pmpm−1 × p j − 1 p j \frac{p_j - 1}{p_j} pjpj−1 = ϕ(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;
}
}