【发布时间】:2014-05-05 03:08:42
【问题描述】:
纯粹是为了好玩,我决定编写一个简单的算法来查找 2 和 x 之间的所有素数,其中 x 是你想要的任何值。我正在使用clock_t 来计算算法完成不同 x 值所需的时间。 (我去 x=0,然后是 25000,然后是 50000,然后是 75000,...,直到 1,000,000)。例如,当 x = 25000 时,我进入一个 for 循环,其中 i 从 2 变为 25000,并且对于 i 的每个值,我通过将其除以 2 和自身之间的每个数字来检查它是否是素数, 寻找余数 0。
算法如下:
vector<int> calcPrimesWithoutPrechecking(int upperLimit)
{
vector<int> res;
for(int i = 2; i <= upperLimit; i++)
{
int currentNum = i;
bool foundDivisible = false;
for(int j = 2; j < currentNum; j++)
{
if(currentNum % j == 0)
{
foundDivisible = true;
break;
}
}
if(!foundDivisible)
{
res.push_back(i);
}
}
return res;
}
我想我可以通过检查我们当前正在测试的数字的最后一位来加快速度。如果那个数字是 0、2、4、5、6 或 8,那么我什至不必检查它是否是素数,因为我知道它不是(当然 2、3 和 5 是,所以这些都被处理了在一开始的时候)。我称之为预检查。这是带有预检查的算法:
vector<int> calcPrimesWithPrechecking(int upperLimit)
{
vector<int> res;
res.push_back(2);res.push_back(3);res.push_back(5);
for(int i = 6; i <= upperLimit; i++)
{
bool foundDivisible = false;
int lastDig = i%10;
if(lastDig == 0
|| lastDig == 2
|| lastDig == 4
|| lastDig == 6
|| lastDig == 8
|| lastDig == 5)
{
foundDivisible = true;
}
int currentNum = i;
for(int j = 2; j < currentNum && !foundDivisible; j++)
{
if(currentNum % j == 0)
{
foundDivisible = true;
break;
}
}
if(!foundDivisible)
{
res.push_back(i);
}
}
return res;
}
我将结果输出到控制台,并将它们写入文本文件。然后我将时间复制到excel,并绘制它们。但是,由于某种原因,带有预检查的算法较慢。我几乎可以肯定它会更快。当我运行程序时,我会故意关闭计算机上的每个程序,并以发布模式运行它。我已经在调试中测试了每个算法,它们确实都按预期工作。
x 轴是我们要检查的素数的数量(例如,25000 表示我们正在寻找 2 到 25000 之间的所有素数),y 轴是获得所有素数的时间(以秒为单位)。
有人可以解释为什么理论上应该消除许多计算的第二种算法实际上更慢吗?
【问题讨论】:
-
只遍历奇数会更容易,并且会稍微提高效率。将外循环更改为
for(int i = 7; i <= upperLimit; i += 2)。 -
最佳的试除法是
~ n^1.35..1.45和Eratosthenes的筛子~ n~1.05..1.1,在n产生的素数。您的算法是~ n^2 ~ x^2/(log x)^2,实际上您的数据在 100 万与 400K empirically 是~ x^1.92。
标签: c++ algorithm performance primes