因数

因数

定义

在整数范围内,若 \(a\) 为整数, \(b\) 为非零整数,若存在整数 \(k\),使得 \(a=kb\),则称 \(b\)\(a\) 的因数,记作 \(b\space | \space a\).

求所有因数

思路:

试除法

枚举每个可能的因数,进行试除。由于一个数的因数也是成对出现的,即如果 \(d|n\) ,则 \(\frac{n}{d}|n\) . 因此,只需要枚举 \([1,\lfloor \sqrt{n} \rfloor )\) 之间的整数即可。

时间复杂度:

\(O(\sqrt{n})\)

代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> get_dividors(int n) {		//求n的所有因数,保存至res向量中
	vector<int> res;
	for (int i = 1; i <= n / i; i++)
		if (n % i == 0) {
			res.push_back(i);
			if (i != n / i)				//完全平方数,不能重复添加
                res.push_back(n / i);
		}
	sort(res.begin(), res.end());		//排序,时间复杂度小于sqrt(n)
	return res;
}
int main() {
	int x;
	cin >> x;
	auto res = get_dividors(x);
	for (auto t : res)	cout << t << " ";
	return 0;
}

分解质因数

思路:

本质上和求因数的方法一样:试除法。每次都用可以整除的因数除到不能再除为止,再枚举下一个数。最后作为除数的都是质数。

代码:

#include<iostream>
using namespace std;
int main() {
	int n, i = 2;
	cin >> n;
	while (n != 1) {
		int t = 0;
		while (n % i == 0) {	//满足这个条件的i一定是质数
			n /= i;
			t++;				//记录质因数i出现的次数
		}
		if (t == 1)
			printf("%d", i);
		if (t > 1)
			printf("%d^%d", i, t);
		if (t > 0 && n != 1)
			printf("*");
		i++;
	}
	return 0;
}

求因数个数

思路:

根据算术基本定理(唯一分解定理),任何一个大于1的自然数 \(N\),如果 \(N\) 不为质数,那么 \(N\) 可以唯一分解成有限个质数的乘积 \(N=P_1^{\alpha_1}P_2^{\alpha_2}P_3^{\alpha_3} \cdots P_k^{\alpha_k}\) ,这里 \(P_1<P_2<P_3< \cdots <P_k\) 均为质数,其中指数 \(\alpha_i\) 是正整数。

\(N\) 的任何一个因数 \(d\) 的形式为:

\[d=P_1^{\beta_1}P_2^{\beta_2}P_3^{\beta_3}\cdots P_n^{\beta_k},其中0\le\beta_i\le\alpha_i \]

相当于在N的质因数里面选,故总因数个数为:

\[cnt = (\alpha_1+1)(\alpha_2+1) \cdots (\alpha_k+1) \]

举个例子:

\(18 = 2^1*3^2\),那么18的因数个数为 \((1+1)*(2+1)=6\) 个。显然18的因数有:\(1,2,3,6,9,18\),确实为6个。

小tip:

int 范围内的数字的因数个数最大为1536个。

时间复杂度:

\(O(\sqrt n)\)

代码:

#include <iostream>
using namespace std;
int cnt_dividors(int n) {
	int res = 1;
	int p = 2;					//枚举每个数
	while (n != 1) {
		int a = 0;				//质因数个数
		while (n % p == 0) {	//成立时p一定为质数
			n /= p;
			a++;
		}
		res *= (a + 1);
		p++;
	}
	return res;
}
int main() {
	int x;
	cin >> x;
	auto cnt = cnt_dividors(x);
	cout << cnt;
	return 0;
}

求因数之和

思路:

公式为:

\[sum=(P_1^0+P_1^1+\cdots +P_1^{\alpha_1})(P_2^0+P_2^1+\cdots+P_2^{\alpha_2})\cdots(P_k^0+P_k^1+\cdots+P_k^{\alpha_k}) \]

即:

\[sum = \prod\limits_{i=1}^k\sum\limits_{j=0}^{\alpha_k}P_i^j \]

将此公式展开可得到一个总共 \(cnt = (\alpha_1+1)(\alpha_2+1)\cdots(\alpha_k+1)\) 项的多项式,其中每项的形式为 \(P_1^{\beta_1}P_2^{\beta_2}P_3^{\beta_3}\cdots P_n^{\beta_k},其中0\le\beta_i\le\alpha_i\) ,恰好符合上一章节所讲内容。故这里的 \(sum\) 为所求的所有因数之和。

举个例子:

\(18 = 2^1 \times 3^2\),那么18的因数之和为 \((2^0+2^1)*(3^0+3^1+3^2)=39\) 个。显然18的因数之和:\(1+2+3+6+9+18=39\),确实为39。

关键代码分析:

1.

对于求 \(P_1^0+P_1^1+\cdots +P_1^{\alpha_1}\) 这样一个式子,采用下面这种方法求解:

LL t = 1;
while (a--)	t = t * p + 1;		//p为质因数,a为指数

第一次:\(t = p + 1\)

第二次:\(t=(p+1)\times p+1=p^2+p+1\)

第三次:\(t=(p^2+p+1)\times p+1=p^3+p^2+p+1\)

\(\alpha_1\)次:\(t=p_1^0+p_1^1+\cdots +p_1^{\alpha_1}\)

如此来看,这样计算的时间复杂度为 \(O(\alpha_1)\)

2.

不建议采用快速幂求解,理由如下:

对于 \(P_1^{\alpha_1}\) 来说,快速幂求解的时间复杂度为 \(O(log\alpha_1)\),那么这整个式子的时间复杂度就为 \(O(\alpha_1log\alpha_1)\),效率更低了。

3.

可以选择采用快速幂+等比数列求和公式来求解:

\[P_1^0+P_1^1+\cdots +P_1^{\alpha_1}=\frac{P_1^{\alpha_1+1}-P_1^0}{P_1-1}=\frac{P_1^{\alpha_1+1}-1}{P_1-1} \]

这样的话时间复杂度为 \(O(log\alpha_1)\)

时间复杂度:

采用 1 的话,总计算量为 \(k\times \alpha\),其中 \(k\) 为自然数 \(N\) 的质因数个数。

代码:

LL sum_dividors(int n) {
	LL res = 1;
	int p = 2;					//枚举每个数
	while (n != 1) {
         LL t = 1;
		while (n % p == 0) {	//成立时p一定为质数
			n /= p;
			t = t * p + 1;
		}
		res *= t;
		p++;
	}
	return res;
}

最大公因数

欧几里得算法(辗转相除法)

公式:

用gcd(a,b)表示a与b的最大公因数,则:

\[gcd(a,b) = gcd(b,a\space mod\space b) \]

\(b=0\) 时,\(gcd(a,0)=a\).

举个例子:

当我们求 36 和 24 的最大公因数时:

\[gcd(36,24)=gcd(24,12)=gcd(12,0)=12 \]

证明:

\(a=bk+c\),显然有 \(c=a\space mod \space b\).

1.\(d\space |\space a,d\space |\space b\),则 \(c=a-bk\) 可得出:

\[\frac{c}{d}=\frac{a}{d}-\frac{b}{d}k \space \in Z \]

因此 \(d \space | \space c\).

故:\(d\)\(a\)\(b\) 的公因数,则 \(d\) 也是 \(b\)\(a\space mod\space b\) 的公因数。

2.反过来:

\(d\space |\space b,d\space |\space (a\space mod\space b)\),则同理可得到:

\[\frac{a\space mod\space b}{d}+\frac{b}{d}k=\frac{a}{d} \space \in Z \]

因此 \(d \space | \space a\).

故:\(d\)\(b\)\(a\space mod\space b\) 的公因数,则 \(d\) 也是 \(a\)\(b\) 的因数。

综上所述,\(\space a\space和\space b\space的公因数\Leftrightarrow b \space和\space a\space mod\space b的公因数\),即 \(gcd(a,b) = gcd(b,a\space mod\space b)\).

另:

我们也可以得到以下结论:

求两个数 a 和 b 的所有公因数,即求他们最大公因数的所有因数。

代码:

1.递归写法

int gcd(int a, int b) {
	return b ? gcd(b, a % b) : a;
}

2.循环写法

int gcd(int a, int b) {
	while (b) {
		int temp = b;
		b = a % b;
		a = temp;
	}
	return a;
}

3.多个数字求最大公因数

int mul_gcd(vector<int> vec) {
	int res = vec[0];
	for (auto t : vec)	res = gcd(res, t);
	return res;
}
posted @ 2025-04-11 19:09  H_Elden  阅读(186)  评论(0)    收藏  举报