质数分解-扩展(约数个数、约数和、欧拉函数)
C++
实现- 此处
vector<int> P, C
并非必要,这里只是为了顺序查看分解后的质数 - 约数个数与约数之和适用的场景多为处理一个极大的数(基本类型无法表示,如
N
N
N个
a
i
a_i
ai相乘后的约数个数或约数之和)
- 解决方法:
- 用
unordered_map
替代divide
函数中vector
- 以
p
为key
,c
为value
存储,汇总每个 a i a_i ai的分解结果,最后统一处理 - 在最后一节会给出对应代码,有兴趣的话可以看一下
- 用
- 解决方法:
质数分解-简单回顾
根据算术基本定理,任何一个大于
1
1
1的正整数都能唯一分解为有限个质数的乘积,可写作:
N
=
p
1
c
1
p
2
c
2
⋯
p
m
c
m
N = p_1^{c_1}p_2^{c_2}\cdots p_m^{c_m}
N=p1c1p2c2⋯pmcm
其中,
c
i
c_i
ci都是正整数,
p
i
p_i
pi都是质数,且满足
p
1
<
p
2
<
⋯
<
p
m
p_1\lt p_2 \lt \cdots \lt p_m
p1<p2<⋯<pm
该节是下面各节的基础,
P, C
在之后会被反复用到,请优先理解本节代码后面章节在运行前,都需要先运行一遍本节
divide
函数
vector<int> P, C; // 存储的是对应的p_i, c_i
void divide(int n)
{
for (int i = 2; i <= n / i; i ++)
{
if (n % i == 0)
{
int s = 0;
while (n % i == 0)
{
n /= i;
s ++;
}
P.push_back(i);
C.push_back(s);
}
}
if (n > 1) // 可能会剩余一个大质数,也需将其加入P, C
{
P.push_back(n);
C.push_back(1);
}
}
输入:
n = 100
输出:
p = 2, c = 2
p = 5, c = 2
约数个数
针对一个正整数
N
N
N,它的正约数个数为:
(
c
1
+
1
)
∗
(
c
2
+
1
)
∗
⋯
∗
(
c
m
+
1
)
=
∏
i
=
1
m
c
i
+
1
(c_1+1)* (c_2+1)* \cdots *(c_m+1)=\prod_{i = 1}^{m}{c_i+1}
(c1+1)∗(c2+1)∗⋯∗(cm+1)=i=1∏mci+1
int count()
{
int res = 1;
for (int i = 0; i < C.size(); i ++)
{
int c = C[i];
res *= (c + 1);
}
return res;
}
输入:
n = 100 // 从divide函数输入
输出:
约数个数: 9
约数之和
针对一个正整数
N
N
N,它的正约数之和为:
(
1
+
p
1
+
p
1
2
+
⋯
+
p
1
c
1
)
∗
⋯
∗
(
1
+
p
m
+
p
m
2
+
⋯
+
p
m
c
m
)
=
∏
i
=
1
m
(
∑
j
=
0
c
i
(
p
i
)
j
)
(1+p_1+p_1^2+\cdots+p_1^{c_1})*\cdots*(1+p_m+p_m^2+\cdots+p_m^{c_m})=\prod_{i=1}^{m}\left( \sum_{j=0}^{c_i}{(p_i)^j} \right)
(1+p1+p12+⋯+p1c1)∗⋯∗(1+pm+pm2+⋯+pmcm)=i=1∏m(j=0∑ci(pi)j)
int sum()
{
int res = 1;
for (int i = 0; i < P.size(); i ++)
{
int p = P[i];
int t = 1;
// 秦九韶算法 -> p^3 + p^2 + p + 1 = p(p(p + 1) + 1) + 1
for (int j = 0; j < C[i]; j ++) t = (t * p + 1);
res *= t;
}
return res;
}
输入:
n = 100 // 从divide函数输入
输出:
所有约数之和: 217
欧拉函数
1 ∼ N 1\sim N 1∼N中与 N N N互质的数的个数被称为欧拉函数,记为 φ ( N ) \varphi(N) φ(N)
- ∀ a , b ∈ N \forall a, b \in \mathbb{N} ∀a,b∈N,若 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,则称 a , b a,b a,b互质
φ ( N ) = N ∗ p 1 − 1 p 1 ∗ p 1 − 2 p 2 ∗ ⋯ ∗ p m − 1 p m = N ∗ ∏ p = 1 m ( 1 − 1 p ) \varphi(N) = N * \frac{p_1-1}{p_1} * \frac{p_1-2}{p_2} * \cdots * \frac{p_m-1}{p_m} = N * \prod_{p=1}^{m}\left({1-\frac{1}{p}}\right) φ(N)=N∗p1p1−1∗p2p1−2∗⋯∗pmpm−1=N∗p=1∏m(1−p1)
int phi(int n)
{
int res = n;
for (int i = 0; i < P.size(); i ++)
{
int p = P[i];
res = res / p * (p - 1);
}
return res;
}
输入:
n = 100
输出:
1~100中与100互质的数的个数: 40
附录
#include <iostream>
#include <unordered_map>
using namespace std;
unordered_map<int, int> map;
void divide(int n)
{
for (int i = 2; i <= n / i; i ++)
{
if (n % i == 0)
{
int s = 0;
while (n % i == 0)
{
n /= i;
s ++;
}
map[i] += s;
}
}
if (n > 1) map[n] += 1;
}
int count()
{
int res = 1;
for (auto i : map) res *= (i.second + 1);
return res;
}
int sum()
{
int res = 1;
for (auto i : map)
{
int p = i.first;
int t = 1;
for (int j = 0; j < i.second; j ++) t = (t * p + 1);
res *= t;
}
return res;
}
int main()
{
int a[] = {10, 45, 66, 39, 75};
for (int i = 0; i < sizeof(a) / sizeof(int); i ++) divide(a[i]);
cout << count() << endl; // 360
cout << sum() << endl; // 334317984
}