这个素数测试如何使代码快 5000 倍
How does this primality test make the code 5000x faster?
警告:此代码是欧拉项目问题 50 的解决方案。如果你不想让它变质,不要看这里。
在这里,我有搜索一长串连续素数的代码,这些素数加在一起也是一个素数。有一次,我需要测试一个总和是否是素数。
我有两个测试,它们在函数 computeMaxPrime 中是 ifdef'd。第一个检查总和与 std::一组质数。第二个使用GMP实施的米勒-拉宾测试。该函数仅被调用 6 次。当我使用第一个测试时,函数 computeMaxPrime 需要 .12 秒。当我使用第二个测试时,它只需要~.00002秒。有人可以解释一下这怎么可能吗?我不认为 6 次调用来检查一个号码是否在一个集合中需要 100 毫秒。我也尝试使用unordered_set,它的性能相同。
我认为这可能是一个计时问题,但我已经通过从终端(在 OSX 上)对整个程序执行进行计时来验证它。我还验证了,如果我将测试更改为首先使用 Miller-Rabin 测试,然后确认使用该集合,它会对该集合进行一次调用,时钟报告 .02 秒,这正是我所期望的(仅使用集合测试的总时间的 1/6)。
#include "PrimeGenerator2.h"
#include <set>
#include <stdio.h>
#include <time.h>
#include <gmp.h>
typedef std::set<u_int64t> intSet;
bool isInIntSet (intSet set,
u_int64t key)
{
return (set.count(key) > 0);
}
bool isPrime (u_int64t key)
{
mpz_t integ;
mpz_init (integ);
mpz_set_ui (integ, key);
return (mpz_probab_prime_p (integ, 25) > 0);
}
void computeInitialData (const u_int64t limit,
intSet *primeSet,
intList *sumList,
u_int64t *maxCountUpperBound)
{
PrimeSieve sieve;
u_int64t cumSum = 0;
u_int64t pastUpperBound = 0;
*maxCountUpperBound = 0;
for (u_int64t prime = sieve.NextPrime(); prime < limit; prime = sieve.NextPrime()) {
primeSet->insert(prime);
cumSum += prime;
sumList->push_back(cumSum);
if (cumSum < limit)
(*maxCountUpperBound)++;
else
pastUpperBound++;
}
}
u_int64t computeMaxPrime (const u_int64t limit,
const intSet &primeSet,
const intList &sumList,
const u_int64t maxCountUpperBound)
{
for (int maxCount = maxCountUpperBound; ; maxCount--) {
for (int i = 0; i + maxCount < sumList.size(); i++) {
u_int64t sum;
sum = sumList[maxCount + i] - sumList[i];
if (sum > limit)
break;
#if 0
if (isInIntSet (primeSet, sum))
return sum;
#else
if (isPrime (sum))
return sum;
#endif
}
}
return 0; // This should never happen
}
u_int64t findMaxCount (const u_int64t limit)
{
intSet primeSet; // Contains the set of all primes < limit
intList sumList; // Array of cumulative sums of primes
u_int64t maxCountUpperBound = 0; // Used an initial guess for the maximum count
u_int64t maxPrime; // Final return value
clock_t time0, time1, time2;
time0 = clock();
computeInitialData (limit, &primeSet, &sumList, &maxCountUpperBound);
time1 = clock();
maxPrime = computeMaxPrime (limit, primeSet, sumList, maxCountUpperBound);
time2 = clock();
printf ("%f seconds for primes n" , (double)(time1 - time0)/CLOCKS_PER_SEC);
printf ("%f seconds for search n" , (double)(time2 - time1)/CLOCKS_PER_SEC);
return maxPrime;
}
int main(void)
{
printf ("%lldn", findMaxCount(1000000));
}
编辑:哦,这更奇怪。似乎与 STL 集无关。如果我做一个黑客来制作isInIntSet,只是检查它被调用了多少次,与GMP测试相比,它同样慢。这让我觉得我可能只是遇到了一个编译器错误(EDIT2:永远不要责怪编译器!
bool isInIntSet (intSet set, u_int64t key)
{
static int counter = 0;
counter++;
return (counter == 6);
}
Duh.函数 isInIntSet 直接将 intSet 作为参数,因此正在复制整个集合。我的意思是通过引用(intSet 和 set)传递。这使得搜索时间缩短到0.000003秒,unordered_set。
相关文章:
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 此代码是否违反一个定义规则
- 为什么我的代码在输出中增加了93天
- 我的简单if-else语句是如何无法访问的代码
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 为什么在这个代码结束循环中没有得到结束
- 在c代码之间共享数据的最佳方式
- 这个素数测试如何使代码快 5000 倍