我试图用希腊人算法的Sieve找到一些素数。我有一些效率问题。这是代码:
void check_if_prime(unsigned number)
{
unsigned index = 0;
while (primes[index] <= std::sqrt(number))
{
if (number % primes[index] == 0) return;
++index;
}
primes.push_back(number);
}
而且,因为我编写了巨大的2/3/5/7/11/13主轮,代码是5795行长。
for (unsigned i = 0; i < selection; ++i)
{
unsigned multiple = i * 30030;
if (i!=0) check_if_prime( multiple+1 );
check_if_prime ( multiple+17 );
check_if_prime ( multiple+19 );
check_if_prime ( multiple+23 );
// ...so on until 30029
}
优化标志:-O3,-fexpensive-optimizations,-march = pentium2
在20分钟内有2500万个素数,CPU卡在50%(不知道为什么,尝试了实时优先级,但它并没有太大变化)。输出文本文件的大小为256MB(稍后将更改为二进制文件)。我想找到最多2 ^ 32的所有素数,并且很少的优化可以节省一些时间和电力。提前谢谢!
编辑:没有寻求算法,如果可以做的话,寻求代码改进!
答案 0 :(得分:4)
以下是我对您的计划表现的看法:
可能您的主要问题是对std::sqrt()
的调用。这是一个浮点函数,专为完全精确的结果而设计,它肯定需要相当多的周期。我敢打赌,如果你使用这张支票,你会快得多:
while (primes[index]*primes[index] < number)
这样你就可以使用整数乘法,这对于现代CPU来说是微不足道的。
for()
循环开头的if语句与性能无关。它没有执行足够多次。你的内部循环是check_if_prime()
中的while循环。这是你需要优化的那个。
我看不出你是如何做输出的。有一些方法可以使输出严重减慢你的速度,但我不认为这是主要问题(如果它是一个问题)。
代码大小可能是个问题:您的CPU具有容量有限的指令缓存。如果您的6k行不适合第一级指令缓存,则惩罚可能会很严重。如果我是你,我会使用数据代替代码重新实现轮子,我。 E:
unsigned const wheel[] = {1, 17, 19, 23, ...}; //add all your 6k primes here
for (unsigned i = 0; i < selection; ++i)
{
unsigned multiple = i * 30030;
for(unsigned j = 0; j < sizeof(wheel)/sizeof(*wheel); j++) {
check_if_prime(multiple + wheel[j]);
}
}
答案 1 :(得分:0)
让它在调试器下运行,单步执行,逐个指令,并在每个点了解它在做什么,以及为什么。
这让你走在CPU的脚下,你会看到坚果程序员让你做的所有愚蠢, 你会看到你能做得更好的事情。
这是使代码尽可能快的一种方法。
程序大小本身只会影响速度,如果你的速度太快以至于缓存成为一个问题。
答案 2 :(得分:0)
这里有一些技巧可以检查数字是否为素数:
bool is_prime(unsigned int number) // negative numbers are not prime.
{
// A data store for primes already calculated.
static std::set<unsigned int> calculated_primes;
// Simple checks first:
// Primes must be >= 2.
// Primes greater than 2 are odd.
if ( (number < 2)
|| ((number > 2) && ((number & 1) == 0) )
{
return false;
}
// Initialize the set with a few prime numbers, if necessary.
if (calculated_primes.empty())
{
static const unsigned int primes[] =
{ 2, 3, 5, 7, 13, 17, 19, 23, 29};
static const unsigned int known_primes_quantity =
sizeof(primes) / sizeof(primes[0]);
calculated_primes.insert(&primes[0], &primes[known_primes_quantity]);
}
// Check if the number is a prime that is already calculated:
if (calculated_primes.find(number) != calculated_primes.end())
{
return true;
}
// Find the smallest prime to the number:
std::set<unsigned int>::iterator prime_iter =
calculated_primes.lower_bound(number);
// Use this value as the start for the sieve.
unsigned int prime_candidate = *prime_iter;
const unsigned int iteration_limit = number * number;
while (prime_candidate < iteration_limit)
{
prime_candidate += 2;
bool is_prime = true;
for (prime_iter = calculated_primes.begin();
prime_iter != calculated_primes.end();
++prime_iter)
{
if ((prime_candidate % (*prime_iter)) == 0)
{
is_prime = false;
break;
}
}
if (is_prime)
{
calculated_primes.insert(prime_candidate);
if (prime_candidate == number)
{
return true;
}
}
}
return false;
}
注意:这是未经测试的代码,但演示了一些检查数字是否为素数的技术。