N 的正约数集合为:{x∣x=p1b1p2b2…pkbk}(0≤bi≤ri,bi∈N∗) N 的正约数个数为: (r1+1)×(r2+1)×⋯×(rk+1)=i=1∏k(ri+1)。
除了完全平方数,约数总是成对出现的,即 d≤n 和 dN≤n 都是 N 的约数。 N 的约数个数上界为 2N。 N 的所有正约数的和为: (1+p1+p12+⋯+p1r1)×⋯×(1+pk+pk2+⋯+pkrk)=i=1∏k(j=0∑ri(pi)j)
最大公约数
定义
设 a 和 b 是不都为 0 的整数,c 为满足 c∣a 且 c∣b 的最大整数,则称 c 是 a 和 b 的最大公约数,记为 gcd(a,b) 或 (a,b)。
int Decompose(int a, int b) {
int ans = 1;
for(int x = 2; x * x <= min(a,b); x++){
while(a % x == 0 && b % x == 0) a /= x, b /= x, ans *= x;
while(a % x == 0) a /= x;
while(b % x == 0) b /= x;
}
if (a % b == 0) ans *= b;
else if (b % a == 0) ans *= a;
return ans;
}
方法三:欧几里得算法:
“两个整数 a,b(a≥b) 的公约数集合与 a−b 和 b 的公约数集合相同”,可得:gcd(a,b)=gcd(b,amodb),俗名曰“辗转相减法”。
int gcd(int a, int b) {
if (b == 0) return a;
if (a < b) return gcd(b, a);
return gcd(b, a - b);
}
如果输入一组数 1 1000000000,可以设想该算法的执行效率,因为每一次都会直接将 b 减少 1,所以会减 109 次才能使其为 0,效率甚至不如暴力枚举,那么,我们可以用模运算来代替减,就是小学奥数中常说的“辗转相除法”!代码如下:
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
// 或者可以写三目运算符成如下形式
// return (b == 0) ? a : gcd(b, a % b);
}
时间复杂度分析:
根据 gcd(a,b)=gcd(b,amodb),设 a>b:
当 a≥2b 时,b≤a/2,b 的规模至少缩小一半;
当 a<2b 时,amodb<a/2,余数的规模至少缩小一半;
所以时间复杂度为 O(loga+b),在实际使用中,复杂度远远达不到如此。
方法四:库函数
当然,我相信没有人会像写这个东西,所以 c++ 提供了库函数 __gcd(),其用法与上面的 gcd 函数一样,传入两个参数 a 和 b 即可返回 a 与 b 的最大公约数,要注意的是,这个函数属于 algorithm 库,而不是 cmath,用之前记得要写头文件。