更快地循环

时间:2016-06-25 16:04:22

标签: c++ loops optimization

我想计算一个数字是否是一个完美的数字(总和(适当的除数)==数字)。因此,我所要做的就是得到适当的除数,将它们加起来并查看它是否为数字。为此我使用for循环:

cin >> number;
sum = 1;
for (int i = number/2; i > 1; --i) {
  if (number % i == 0) {
    sum = sum + i;
  }
  if (sum > number) {break;}
}
if (sum == number) {cout << "perfect!" << endl;}

这个循环太慢了。我已经做过的就像你可以看到的那样,如果总和已经大于数字,就会突破循环,我从更高的数字开始(所以如果总和更大,它会更快到达),并且因为1总是一个适当的除数,我不需要循环它。

现在我有点想法,并且非常感谢有关如何进一步改进这种循环的一些提示(甚至是完全不同的方法?)

4 个答案:

答案 0 :(得分:4)

您可以通过以下方式获得非常大的改进:

cin >> number;
sum = 1;
for (int i = sqrt(number); i > 1; --i) {
  if (number % i == 0) {
    sum += i + (number / i);
  }
  if (sum > number) {break;}
}
if (sum == number) {cout << "perfect!" << endl;}

如您所见,此循环从输入的平方根开始,而不是输入的一半。这在最坏的情况下给出O(sqrt(N))改进。对于任何一对数的除数,一个必须位于平方根之上,一个最大位于下方。另一个需要注意的重要事项是整数除法/模数非常昂贵,但计算它们时,两者都是同时计算的。这意味着,一旦计算出number % inumber / i基本上是免费的。因此,我的代码中每次迭代的成本基本上与您的迭代成本相同,但迭代次数要少得多。

如果你这样做,你可能还想考虑计数而不是减少,如果你的目标是提前退出,那么一般来说你会更好地从小数字开始,因为更多极值除数的总和(一个非常高) ,一个非常低)更大。数量越小,除数密度越高。

请注意,我的代码并不完全正确,需要考虑一些边缘情况,完美的正方形就是一个例子。

答案 1 :(得分:1)

如果你真的想缩短这个循环的时间并测试大数字,首先你可以尝试Miller-Rabin test来消除素数。然后使用Fermat factorisation method查找数字的除数。

如果您测试小数字,您应该从1开始迭代并仅测试数字,直到数字的平方根(reference)。

答案 2 :(得分:0)

您可以实施一些简单的优化:

  1. 仅检查素数。通过简单的sieve,您可以快速,高效地找到它们。您也可以找到它们并保存在文件中。 UPD:请注意,在此类实施中很容易引入错误。一种可能的正确方法是找到所有除数,将它们保存在数组中,然后迭代所有可能的组合来计算实际总和。它可以递归完成。
  2. 从2开始,然后转到数字的平方根,如果找到了除数,请sum += i + number/i。自然顺序而不是反向通常会更快,因为它会立即找到小的除数,这会显着增加总和。
  3. 如果你要为非常大的数字解决这个问题,你就不能很快地做到这一点,因为它是Integer factorization问题的一个例子,已知这个问题很难有效地解决(即,没有人知道多项式算法)。因此,没有办法使这个循环比提议的实现更快(渐近)。

    但另一方面,整数分解是一个古老且非常重要的问题,因此有些库可以通过高级启发式和汇编级优化来快速解决它。

答案 3 :(得分:0)

有一些方法可以优化它。

  1. 循环sqrt(N)次,而不是N / 2次。 (O(SQRT(N))
  2. 使用预生成的数组。 (O(0)?)
  3. 使用thread s的神奇力量。 (视乎)
  4. 用筛子消除素数。 (O(N)~O(N ^ 2))
相关问题