【算法学习笔记】31:试除法分解质因数及求解欧拉函数
试除法分解质因数
原理
在 O ( n ) O(\sqrt{n}) O(n)时间内可以对 n n n分解质因数,原理就是从最小质因数2开始遍历,第一个遍历到的因数就是2,一定是一个质因数。
每次都从 n n n里将遇到的质因子除干净,那么下次遇到的因数 i i i也一定是一个质因数。
可以用反证法:如果下次遇到的因数 i i i不是一个质因数,那么说明有 < i <i <i的另一个质因数 j j j,那么因为我们每次遇到质因数都会从 n n n里除干净,所以 j < i j < i j<i一定在过去的某一轮被除干净了,矛盾,证毕。
这个遍历只需要在 i ⋅ i ≤ n i \cdot i \leq n i⋅i≤n的情况下继续就可以了,因为一旦 i ⋅ i > n i \cdot i > n i⋅i>n,最后剩下的要么 n n n是1,要么 n n n是最后一个质因数(且出现了一次方),一定不存在其它没遍历到的质因数了。
可以用反证法:假如 n n n还是一个合数,还存在没遍历到的质因子 i i i,那么 n i \frac{n}{i} in也一定是一个因数,这个因数的任何一个质因子都 < i < i <i,又一定从 n n n里被剔除过了,矛盾,证毕。
最后记得判断一下 n n n是不是剩下一个一次方的最大质因子,也就是 n n n如果不是1就是最后的那个质因数。
例题:AcWing 867. 分解质因数
#include <iostream>
using namespace std;
int main() {
int t;
cin >> t;
while (t -- ) {
int n;
cin >> n;
for (int i = 2; i * i <= n; i ++ ) {
int mi = 0;
while (n % i == 0) {
mi ++ ;
n /= i;
}
if (mi) cout << i << " " << mi << endl;
}
if (n != 1) cout << n << " " << 1 << endl;
cout << endl;
}
return 0;
}
试除法求解欧拉函数
利用试除法分解质因数,同样可以解决欧拉函数的计算问题。
定义与计算公式
欧拉函数 ϕ ( n ) \phi(n) ϕ(n)表示从1到n中和n互质的数字的个数,两个数字除了1没有其它公因数就是互质。比如从1到6之间和6互质的数字只有1和5,所以 p h y ( 6 ) = 2 phy(6) = 2 phy(6)=2。
设
n
n
n分解质因数之后的结果是:
n
=
p
1
α
1
+
p
2
α
2
+
.
.
+
p
k
α
k
n = p_1^{\alpha_1} + p_2^{\alpha_2} + .. + p_k^{\alpha_k}
n=p1α1+p2α2+..+pkαk
那么欧拉公式的计算公式是:
ϕ
(
n
)
=
n
⋅
(
1
−
1
p
1
)
⋅
(
1
−
1
p
2
)
⋅
.
.
.
⋅
(
1
−
1
p
k
)
\phi(n) = n \cdot (1 - \frac{1}{p_1}) \cdot (1 - \frac{1}{p_2}) \cdot ... \cdot (1 - \frac{1}{p_k})
ϕ(n)=n⋅(1−p11)⋅(1−p21)⋅...⋅(1−pk1)
证明
思路是用容斥原理。假设 n n n的质因子只有 p 1 p_1 p1到 p k p_k pk,那么根据欧拉函数的定义,只要从1到n中去掉 p 1 p_1 p1到 p k p_k pk所有数的倍数,最后剩下的就是和 n n n互质的数。
第一步:减去所有 p 1 p_1 p1到 p k p_k pk的倍数
n − ⌊ n p 1 ⌋ − ⌊ n p 2 ⌋ − . . . − ⌊ n p k ⌋ n - \lfloor \frac{n}{p_1} \rfloor - \lfloor \frac{n}{p_2} \rfloor - ... - \lfloor \frac{n}{p_k} \rfloor n−⌊p1n⌋−⌊p2n⌋−...−⌊pkn⌋
但是因为某个 p i p_i pi的倍数同时可能也是 p j p_j pj的倍数,所以直接去掉每个数的倍数就会存在多去除的情况,因此就要把多去掉的部分加回来。
第二步:加上所有 p i ⋅ p j p_i \cdot p_j pi⋅pj的倍数
+ ⌊ n p 1 ⋅ p 2 ⌋ + ⌊ n p 1 ⋅ p 3 ⌋ + . . . + ⌊ n p k − 1 ⋅ p k ⌋ + \lfloor \frac{n}{p_1 \cdot p_2} \rfloor + \lfloor \frac{n}{p_1 \cdot p_3} \rfloor + ... + \lfloor \frac{n}{p_{k-1} \cdot p_k} \rfloor +⌊p1⋅p2n⌋+⌊p1⋅p3n⌋+...+⌊pk−1⋅pkn⌋
进而考虑,如果有一个数是 p i p_i pi、 p j p_j pj、 p k p_k pk的倍数,那么在第一步会被 p i p_i pi、 p j p_j pj、 p k p_k pk各减去一次,第二步会被 p i ⋅ p j p_i \cdot p_j pi⋅pj、 p j ⋅ p k p_j \cdot p_k pj⋅pk、 p i ⋅ p k p_i \cdot p_k pi⋅pk各加上一次,减掉三次加上三次,没有加没有减,所以下一步要减去所有同时是三个数的倍数。
第三步:减去所有 p i ⋅ p j ⋅ p k p_i \cdot p_j \cdot p_k pi⋅pj⋅pk的倍数
− ⌊ n p 1 ⋅ p 2 ⋅ p 3 ⌋ − ⌊ n p 1 ⋅ p 2 ⋅ p 4 ⌋ − . . . − ⌊ n p k − 2 ⋅ p k − 1 ⋅ p k ⌋ - \lfloor \frac{n}{p_1 \cdot p_2 \cdot p_3} \rfloor - \lfloor \frac{n}{p_1 \cdot p_2 \cdot p_4} \rfloor - ... - \lfloor \frac{n}{p_{k-2} \cdot p_{k-1} \cdot p_k} \rfloor −⌊p1⋅p2⋅p3n⌋−⌊p1⋅p2⋅p4n⌋−...−⌊pk−2⋅pk−1⋅pkn⌋
所以接下来就是加上所有四个质数乘积分之 n n n,再减去所有五个质数乘积分之 n n n…
将前面给出的欧拉公式的计算公式展开,就可以发现和这个加加减减的式子是相等的,即:
ϕ
(
n
)
=
n
−
⌊
n
p
1
⌋
−
⌊
n
p
2
⌋
−
.
.
.
−
⌊
n
p
k
⌋
+
⌊
n
p
1
⋅
p
2
⌋
+
⌊
n
p
1
⋅
p
3
⌋
+
.
.
.
+
⌊
n
p
k
−
1
⋅
p
k
⌋
−
⌊
n
p
1
⋅
p
2
⋅
p
3
⌋
−
⌊
n
p
1
⋅
p
2
⋅
p
4
⌋
−
.
.
.
−
⌊
n
p
k
−
2
⋅
p
k
−
1
⋅
p
k
⌋
.
.
.
=
n
⋅
(
1
−
1
p
1
)
⋅
(
1
−
1
p
2
)
⋅
.
.
.
⋅
(
1
−
1
p
k
)
\begin{align} \phi(n) &= n - \lfloor \frac{n}{p_1} \rfloor - \lfloor \frac{n}{p_2} \rfloor - ... - \lfloor \frac{n}{p_k} \rfloor \\ & + \lfloor \frac{n}{p_1 \cdot p_2} \rfloor + \lfloor \frac{n}{p_1 \cdot p_3} \rfloor + ... + \lfloor \frac{n}{p_{k-1} \cdot p_k} \rfloor \\ & - \lfloor \frac{n}{p_1 \cdot p_2 \cdot p_3} \rfloor - \lfloor \frac{n}{p_1 \cdot p_2 \cdot p_4} \rfloor - ... - \lfloor \frac{n}{p_{k-2} \cdot p_{k-1} \cdot p_k} \rfloor \\ & ... \\ & = n \cdot (1 - \frac{1}{p_1}) \cdot (1 - \frac{1}{p_2}) \cdot ... \cdot (1 - \frac{1}{p_k}) \end{align}
ϕ(n)=n−⌊p1n⌋−⌊p2n⌋−...−⌊pkn⌋+⌊p1⋅p2n⌋+⌊p1⋅p3n⌋+...+⌊pk−1⋅pkn⌋−⌊p1⋅p2⋅p3n⌋−⌊p1⋅p2⋅p4n⌋−...−⌊pk−2⋅pk−1⋅pkn⌋...=n⋅(1−p11)⋅(1−p21)⋅...⋅(1−pk1)
用眼睛看也能看出上下两个式子相等,首先把上面式子里的 n n n提取出来,就是下面式子最外面的 n n n。然后上面式子里的开头1就是下面式子乘法展开时候每一项选择前面的1的结果。上面式子里的 1 p i \frac{1}{p_i} pi1就是下面式子里只有 ( 1 − 1 p i ) (1 - \frac{1}{p_i}) (1−pi1)选择了 − 1 p i - \frac{1}{p_i} −pi1,其它式子都选择1的结果,因此符号一定是负的。上面式子里的 1 p i ⋅ p j \frac{1}{p_i \cdot p_j} pi⋅pj1就是下面式子里只有 ( 1 − 1 p i ) (1 - \frac{1}{p_i}) (1−pi1)和 ( 1 − 1 p j ) (1 - \frac{1}{p_j}) (1−pj1)选择了 − 1 p i - \frac{1}{p_i} −pi1和 − 1 p j - \frac{1}{p_j} −pj1,其它式子都选择1的结果,因此符号一定是正的。以此类推。
例题:AcWing 873. 欧拉函数
根据前面推导出的计算公式,只要用最开始的 n n n,每次抠出质因数 p i p_i pi,然后乘上 ( 1 − 1 p i ) (1 - \frac{1}{p_i}) (1−pi1),这里可以改写成除以 p i p_i pi再乘以 p i − 1 p_i - 1 pi−1,避免了浮点运算的同时也防止乘爆了溢出。
#include <iostream>
using namespace std;
int phi(int n) {
int res = n;
for (int i = 2; i * i <= n; i ++ ) {
if (n % i) continue;
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n != 1) res = res / n * (n - 1);
return res;
}
int main() {
int t; cin >> t;
while (t -- ) {
int n; cin >> n;
cout << phi(n) << endl;
}
return 0;
}
原文地址:https://blog.csdn.net/SHU15121856/article/details/145149826
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!