找到最多 100 万个“相当不错”的数字的最佳方法?

我正在处理一项涉及“相当不错”的数字的作业。该任务将它们描述为:

“一个“相当不错”的数字是一个整数,它的“坏度”——它的除数之和与数字本身之差的大小——不大于一个指定的值。例如,如果最大坏度是设置为 3,有 12 个“相当不错”的数字小于 100:2、3、4、6、8、10、16、18、20、28、32 和 64;你的任务是编写一个 C++ 程序,非常好,它确定小于指定值的指定最大 badness 的数量。在执行程序时,将限制值和最大 badness 指定为命令行参数。”

该任务要求我编写一个程序,该程序可以打印具有高达一百万的指定坏度限制的完美数字。所以,相当好的 1000000 的命令行参数 1 应该打印 2 4 6 8 16 28 32 64 128 256 496 512 1024 2048 4096 8128 8192 16384 32768 65536 131072 262144 524288

我已经得到了这个与以下代码一起工作

#include <iostream>

using namespace std;

int main(int argc,char *argv[]) {

    const int limit = argc > 1 ? atoi(argv[1]) : 1000000;
    const int badness = argc > 2 ? atoi(argv[2]) : 10;

    for(int number = 2; number < limit; number++) {
        int sum = 1;
        for (int factor = 2; factor < number; factor++){
            if (number % factor == 0) {
                sum += factor;
            }
        }

        if (number >= (sum - badness) && number <= (sum + badness)) {
            cout << number << " ";
        }
    }

    return 0;
}

唯一的问题是,这段代码在找到高达 100 万的“相当不错”的数字时太慢了。有没有办法优化这个?

谢谢

wyq_1234 回答:找到最多 100 万个“相当不错”的数字的最佳方法?

如果 f 是 n 的因数,那么 n/f 也是如此(尽管当 f 是 n 的平方根时,f 和 n/f 是相同的因数)。因此,您可以通过仅将因子计数到 sqrt(number) 来使代码更快,然后当您找到一个时还包括匹配的因子数/因子(平方根情况除外)。

for (int factor = 2; factor * factor <= number; factor++){
    if (number % factor == 0) {
        sum += factor;
        if (factor * factor != number) {
            sum += number / factor;
        }
    }
}

此代码在我的机器上运行时间为 1.554 秒,limit 为 100 万,badness 为 1。等待原始代码完成几分钟后,我感到很无聊。

为了使代码更快,您可以找到数字的质因数分解,并使用 formula for the sum of the divisors based on the prime factorization

即使没有预先计算素数,使用这种方法在我的机器上运行时间为 0.713 秒。这是我从 sum 计算 number 的代码:

int n = number;
int i = 2;
while (n > 1) {
    if (i * i > n) {
        sum *= (n + 1);
        break;
    }
    int pp = i;
    while (n % i == 0) {
        pp *= i;
        n /= i;
    }
    sum *= (pp - 1) / (i - 1);
    i += 1;
}
sum -= number;

它找到所有除 number 的素数幂,并且对于每个 p^msum 乘以 (p^(m+1) - 1) / (p - 1)。与第一个解决方案一样,它在 i*i > n 时提前停止,这意味着 n 是素数。

在一般情况下,它比第一个解决方案快得多,因为尽管我们仍在进行试除,但随着找到质因子,n 会变小。

如果您预先计算了一个足够大的素数列表(也就是说,它至少包含一个大于 limit 的平方根的素数),您在计算 sum 时可以再次高效一点:

int n = number;
for (int i = 0; primes[i] * primes[i] <= n; ++i) {
    int pp = primes[i];
    while (n % primes[i] == 0) {
        pp *= primes[i];
        n /= primes[i];
    }
    sum *= (pp - 1) / (primes[i] - 1);
}
if (n > 1) sum *= (n + 1);
sum -= number;

以这种方式计算 sum 的代码在我的机器上运行时间为 0.189 秒。

本文链接:https://www.f2er.com/6372.html

大家都在问