对于较小的界限,使用筛子就足够了。实事求是
r r
(1) n = ∏ p_k^e_k => τ(n) = ∏ (e_k + 1)
k=1 k=1
很明显,除数的数量可以很容易地从n 的素因数分解中确定,如果gcd(m,n) = 1 则τ(m*n) = τ(m) * τ(n)(即τ 是一个乘法函数)。
因此,如果我们知道n 的任何素因数以及1 <= m < n 的所有τ(m),我们就可以廉价地计算τ(n)。因此
int sieve[limit+1];
// initialise sieve
for(int i = 0; i <= limit; ++i) {
sieve[i] = i;
}
// find a prime factor for all numbers > 1
int root = sqrt(limit); // limit is supposed to be not too large, so no fixup needed here
for(int p = 2; p <= root; ++p) {
if (sieve[p] == p) {
// this means p is prime, mark multiples
for(int m = p*p; m <= limit; m += p) {
sieve[m] = p;
}
}
// Now sieve[n] is a prime factor of n
int p;
for(int n = 2; n <= limit; ++n) {
if ((p = sieve[n]) == n) {
// a prime, two divisors
sieve[n] = 2;
} else {
// count the multiplicity of p in n and find the cofactor of p^multiplicity
int m = 1, q = n;
do {
q /= p;
++m;
}while(q % p == 0);
sieve[n] = m*sieve[q];
}
}
// Now sieve[n] contains τ(n), the number of divisors of n, look for the maximum
int max_div = 0, max_num = 0;
for(int n = 1; n <= limit; ++n) {
if (sieve[n] > max_div) {
max_div = sieve[n];
max_num = n;
}
}
在O(N*log log N) 时间内找到最大除数不超过N 的最小数,具有相对较小的常数因子(可以通过分别处理 2 并仅标记奇质数的奇数倍来进一步减小)。
这是一个简单的蛮力方法,对于小N 来说足够快(“小”的解释取决于“足够快”的概念,例如可以是<= 1000 或<= 1000000)。
对于更大的范围,这太慢且太占用内存。对于这些,我们需要做更多的分析。
从 (1) 中,我们可以推断出在所有具有相同质因数分解结构的数中(意味着相同的数 r 的不同质因数,以及相同的多重指数集,但可能以不同的顺序),都具有相同数量的除数,最小的那个是
- 素数是
r 最小的素数
- 指数按降序显示(2 的指数最大,3 次大,...)
所以我们可以通过考虑所有有限序列来找到具有最多除数<= N 的最小数
e_1 >= e_2 >= ... >= e_r > 0
属性
r
N/2 < n(e_1, ..., e_r) = ∏ p_k^e_k <= N
k=1
并且寻找的号码是他们生产的n(e_1, ..., e_r)之一。 (如果n(e_i) <= N/2 是一个单调非递增有限序列,则在e_1 上加1 的序列将产生一个具有更多除数的数<= N。)
为与1/log p_k 大致成比例的指数生成最大除数。更准确地说,对于固定的r,让
r
T(x_1, ..., x_r) = ∏ (x_k+1)
k=1
r
F(x_1, ..., x_r) = ∏ p_k^x_k
k=1
然后T 在集合{ x : F(x) = N and x_k > 0 for all k } 上取其最大值
r
x_k = (log N + ∑ log p_k)/(r * log p_k) - 1
k=1
我们只承认整数指数,这会使问题复杂化,但偏离比例太远会产生除数比接近比例时要少的数字。
让我们为N = 100000 说明它(它有点太小,无法真正利用比例性,但足够小,完全可以手动完成):
r = 1:e_1 = 16,n(16) = 2^16 = 65536有17个除数。
-
r = 2:设置x_2 = x_1 * log 2 / log 3和N = 2^x_1 * 3^x_2 = 2^(2*x_1),我们得到x_1 ≈ 8.3, x_2 ≈ 5.24。现在让我们看看e_1, e_2 接近x_1, x_2 会发生什么。
2^7 *3^6 = 93312, τ(2^7 *3^6) = (7+1)*(6+1) = 56
2^8 *3^5 = 62208, τ(2^8 *3^5) = (8+1)*(5+1) = 54
2^10*3^4 = 82944, τ(2^10*3^4) = (10+1)*(4+1) = 55
远离比例关系会迅速减少除数,
2^11*3^3 = 55296, τ(2^11*3^3) = (11+1)*(3+1) = 48
2^13*3^2 = 73728, τ(2^13*3^2) = (13+1)*(2+1) = 42
2^15*3^1 = 98304, τ(2^15*3^1) = (15+1)*(1+1) = 32
因此,最接近比例的一对并没有产生最大的除数,但具有大除数的一对是最接近的三个。
-
r = 3:同理,我们得到x_1 ≈ 5.5, x_2 ≈ 3.5, x_3 ≈ 2.4
2^4 *3^3*5^3 = 54000, τ(2^4 *3^3*5^3) = 5*4*4 = 80
2^5 *3^4*5^2 = 64800, τ(2^5 *3^4*5^2) = 6*5*3 = 90
2^7 *3^3*5^2 = 86400, τ(2^7 *3^3*5^2) = 8*4*3 = 96
2^8 *3^2*5^2 = 57600, τ(2^8 *3^2*5^2) = 9*3*3 = 81
2^6 *3^5*5^1 = 77760, τ(2^6 *3^5*5^1) = 7*6*2 = 84
2^7 *3^4*5^1 = 51840, τ(2^7 *3^4*5^1) = 8*5*2 = 80
2^9 *3^3*5^1 = 69120, τ(2^9 *3^3*5^1) = 10*4*2 = 80
2^11*3^2*5^1 = 92160, τ(2^11*3^2*5^1) = 12*3*2 = 72
2^12*3^1*5^1 = 61440, τ(2^12*3^1*5^1) = 13*2*2 = 52
同样,对于接近比例的指数,可以实现较大的除数。
-
r = 4:指数的粗略近似是x_1 ≈ 4.15, x_2 ≈ 2.42, x_3 ≈ 1.79, x_4 ≈ 1.48。对于e_4 = 2,只有一种选择,
2^3*3^2*5^2*7^2 = 88200, τ(2^3*3^2*5^2*7^2) = 4*3*3*3 = 108
对于e_4 = 1,我们有更多选择:
2^4*3^3*5^2*7^1 = 75600, τ(2^4*3^3*5^2*7^1) = 5*4*3*2 = 120
2^5*3^2*5^2*7^1 = 50400, τ(2^5*3^2*5^2*7^1) = 6*3*3*2 = 108
2^5*3^4*5^1*7^1 = 90720, τ(2^5*3^4*5^1*7^1) = 6*5*2*2 = 120
2^6*3^3*5^1*7^1 = 60480, τ(2^6*3^3*5^1*7^1) = 7*4*2*2 = 112
2^8*3^2*5^1*7^1 = 80640, τ(2^8*3^2*5^1*7^1) = 9*3*2*2 = 108
2^9*3^1*5^1*7^1 = 53760, τ(2^9*3^1*5^1*7^1) = 10*2*2*2 = 80
-
r = 5:x_1 ≈ 3.3, x_2 ≈ 2.1, x_3 ≈ 1.43, x_4 ≈ 1.18, x_5 ≈ 0.96。由于2*3*5*7*11 = 2310,7和11的指数一定是1,所以我们找到了候选
2^2*3^2*5^2*7*11 = 69300, τ(2^2*3^2*5^2*7*11) = 3*3*3*2*2 = 108
2^3*3^3*5^1*7*11 = 83160, τ(2^3*3^3*5^1*7*11) = 4*4*2*2*2 = 128
2^4*3^2*5^1*7*11 = 55440, τ(2^4*3^2*5^1*7*11) = 5*3*2*2*2 = 120
2^6*3^1*5^1*7*11 = 73920, τ(2^6*3^1*5^1*7*11) = 7*2*2*2*2 = 112
-
r = 6:由于2*3*5*7*11*13 = 30030,这里只有一个候选人,
2^2*3*5*7*11*13 = 60060, τ(60060) = 3*2^5 = 96
这会产生比使用四个或五个素数的最佳候选者更小的除数。
因此,我们调查了 28 个候选者(并且可以跳过其中几个),发现具有最多除数的最小数字 <= 100000 是 83160(98280 是另一个低于 100000 的数字,具有 128 个除数)。
这是一个程序,它几乎可以立即找到具有最多除数且不超过给定限制< 2^64 的最小数字(没有尝试过捷径,因为它对于 64 位整数来说足够快,可以实现任意精度整数,这在某些时候会变得有价值):
#include <stdlib.h>
#include <stdio.h>
typedef struct {
unsigned long long number;
unsigned long long divisors;
} small_max;
static const unsigned long long primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 };
static const unsigned long long primorials[] =
{ 2, 6, 30, 210, 2310, 30030, 510510, 9699690, 223092870, 6469693230,
200560490130, 7420738134810, 304250263527210, 13082761331670030,
614889782588491410 };
static const unsigned num_primes = sizeof primorials / sizeof primorials[0];
small_max max_divisors(unsigned long long limit);
small_max best_with(unsigned long long limit, unsigned index, unsigned multiplicity);
void factor(unsigned long long number);
int main(int argc, char *argv[]) {
unsigned long long limit;
limit = argc > 1 ? strtoull(argv[1],NULL,0) : 100000;
small_max best = max_divisors(limit);
printf("\nSmallest number not exceeding %llu with most divisors:\n",limit);
printf("%llu with %llu divisors\n", best.number, best.divisors);
factor(best.number);
return 0;
}
small_max max_divisors(unsigned long long limit) {
small_max result;
if (limit < 3) {
result.number = limit;
result.divisors = limit;
return result;
}
unsigned idx = num_primes;
small_max best = best_with(limit,0,1);
printf("Largest power of 2: %llu = 2^%llu\n", best.number, best.divisors-1);
for(idx = 1; idx < num_primes && primorials[idx] <= limit; ++idx) {
printf("Using primes to %llu:\n", primes[idx]);
unsigned long long test = limit, remaining = limit;
unsigned multiplicity = 0;
do {
++multiplicity;
test /= primorials[idx];
remaining /= primes[idx];
result = best_with(remaining, idx-1, multiplicity);
for(unsigned i = 0; i < multiplicity; ++i) {
result.number *= primes[idx];
}
result.divisors *= multiplicity + 1;
if (result.divisors > best.divisors) {
printf("New largest divisor count: %llu for\n ", result.divisors);
factor(result.number);
best = result;
} else if (result.divisors == best.divisors && result.number < best.number) {
printf("Smaller number with %llu divisors:\n ", result.divisors);
factor(result.number);
best = result;
}
}while(test >= primorials[idx]);
}
return best;
}
small_max best_with(unsigned long long limit, unsigned index, unsigned multiplicity) {
small_max result = {1, 1};
if (index == 0) {
while(limit > 1) {
result.number *= 2;
++result.divisors;
limit /= 2;
}
return result;
}
small_max best = {0,0};
unsigned long long test = limit, remaining = limit;
--multiplicity;
for(unsigned i = 0; i < multiplicity; ++i) {
test /= primorials[index];
remaining /= primes[index];
}
do {
++multiplicity;
test /= primorials[index];
remaining /= primes[index];
result = best_with(remaining, index-1, multiplicity);
for(unsigned i = 0; i < multiplicity; ++i) {
result.number *= primes[index];
}
result.divisors *= multiplicity + 1;
if (result.divisors > best.divisors) {
best = result;
} else if (result.divisors == best.divisors && result.number < best.number) {
best = result;
}
}while(test >= primorials[index]);
return best;
}
void factor(unsigned long long number) {
unsigned long long num = number;
unsigned idx, mult;
printf("%llu =", number);
for(idx = 0; num > 1 && idx < num_primes; ++idx) {
mult = 0;
while(num % primes[idx] == 0) {
num /= primes[idx];
++mult;
}
printf("%s %llu ^ %u", idx ? " *" : "", primes[idx], mult);
}
printf("\n");
}