纯粹为了好玩,我决定编写一个简单的算法,找到2和x之间的所有素数,其中x是你想要的。我使用clock_t
来计算算法完成变化x值所需的时间。 (我去x = 0,然后是25000,然后是50000,然后是75000,......,最多1,000,000)。例如,当x = 25000时,我进入for循环,i
从2到25000,并且对于i
的每个值,我通过划分来检查它是否是素数它与两个和它自身之间的每个数字,寻找剩余的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轴是获得所有素数的时间(以秒为单位)素数。
有人可以解释为什么理论上应该取出许多计算的第二种算法实际上更慢?
答案 0 :(得分:5)
预检的实施速度稍慢的原因是它需要做更多的工作来消除在内循环的第一步之后消除的许多数字。
以数字8
为例:预先检查需要找到除法余数并在消除之前执行五次比较,而没有预先检查的程序也会消除8
,但是单一除以二,比较为零。
你可能会看到一点胜利的唯一数字是5
,但这些数字并不像你的程序失去CPU周期的偶数那么常见。
加快这一点的一个更好的方法是完全避免数字:回忆all prime numbers after 3
are either of the form 6*k+1
or 6*k-1
。现在你可以快速迭代几乎三倍!
另一件事是你不需要检查候选素数的平方根之后的除数(你能证明为什么会这样吗?)这种改变本身就会给你带来巨大的改善。
最后,一个非常有用的技巧是存储你到目前为止发现的所有素数,并将它们用于你的试验除数。这将大大提高内循环的速度。
答案 1 :(得分:4)
因为它没有取出许多计算。如果数字是偶数,那么在检查它是否可被2整除时(这是你在循环中检查的第一个数字)会立即发现。这比你在这里做的要快得多:
int lastDig = i%10;
if(lastDig == 0
|| lastDig == 2
|| lastDig == 4
|| lastDig == 6
|| lastDig == 8
|| lastDig == 5)