约数(也叫因数)的定义大家肯定也都知道。
第一个知识点:求一个数的所有约数——试除法
跟分解质因数差不多,也都是枚举到 √n 就行了,大于 √n 的直接算就行了,反正约数都是成对成对地出现的。
vector< int > get_divisors(int n){
vector< int > ans;
for (int i = 1; i <= n / i; i++){
if (n % i == 0){
ans.push_back(i);
if (i != n / i) ans.push_back(n / i);
}
}
sort(ans.begin(), ans.end());
return ans;
}
第二个知识点:约数个数和约数之和——公式
假如一个数 n 分解质因数是这样的形式:
Pα11⋅Pα22⋯Pαkk
那么对于 n 的一个约数就应该是这种形式:
Pβ11⋅Pβ22⋯Pβkk(0≤βi≤αi)
然后就可以通过乘法原理来求出因数个数,每一个 βi 的个数都是 αi+1 然后把所有的 αi+1 乘起来,就是约数个数,公式是:
(α1+1)(α2+1)⋯(αk+1)
举个例子,540=22×33×51。
在这里:
(α1+1)(α2+1)⋯(αk+1)=(2+1)×(3+1)×(1+1)=3×4×2=24
就说明 540 的因数个数是 24,我们枚举一下试试看:1,2,3,4,5,6,9,10,12,15,18,20,27,30,36,45,54,60,90,108,135,180,270,540
恰好 24 个。
再看约数之和,首先,约数之和的求法是这样(假设分解质因数还是上面的形式):
(P01+P11+⋯+Pα11)(P02+P12+⋯+Pα22)⋯(P0k+P1k+⋯+Pαkk)
那为什么是这样也很简单,直接用乘法分配律展开,然后就会得到一堆两个数的乘积加在一起,我们可以惊奇的发现,每一个乘积都是 n 的其中一个约数,正好把它们加到了一块,这就是约数之和。
代码就不写了吧,这么简单。
第三个知识点:求最大公因数——欧几里得算法(辗转相除法)
首先我们要了解一些性质,如果 d 能整除 a,d 也能整除 b,那么 d 就能整除 ax+by(x,y均为整数)。
而且,b 和 a 的最大公因数,就等于 a 和 bmod 的最大公因数,我们可以把 b \bmod a 写成 b - a \times \lfloor \frac{b}{a} \rfloor 的形式,假设我们把 \lfloor \frac{b}{a} \rfloor 写成 c,那么 b 和 a 的最大公约数就要证明一下是不是跟 a 和 b - a \times c 的最大公约数是相等的。
假设 d 是 b 和 a 的最大公约数,根据最上面的性质,d 能整除 b,d 能整除 a,那么 d 就能整除 b \times 1 + a \times (-c)(毕竟 c 还是一个整数),也就相当于 d 能整除 b - a \times c,所以就证明完了。
int gcd(int a, int b){
if (b == 0) return a; // 如果 b == 0,那么 a 和 0 的最大公约数肯定就是 a 因为 0 能整除任何数
return gcd(b, a % b);
}
更简洁的就是这样:
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}