项目欧拉#10总和

时间:2016-04-25 00:20:33

标签: primes

我正在研究Project Euler的项目#10,其中要求我找到2,000,000以下所有素数的总和。出于某种原因,我无法让我的代码工作。我相当肯定我不了解要使用哪种数据类型,但是int,long或long long似乎都不起作用。任何帮助或建议将不胜感激。这是我的代码。感谢。

int main(int argc, char *argv[])
{
int primes[100] = {2, 3, 5, 7};
//array of primes that have been found
int fillcell = 4;
//cell we are looking to fill
int testnum = 8;
//number being tested to see if it's a prime
int divprime;
int sum = 17;
for(testnum = 8, fillcell = 4 ; testnum < 2000000 ; ++fillcell)
{
    std::cout << "fillcell " << fillcell << std::endl;
    for( ; primes[fillcell] == 0 ; ++testnum)
    {
        std::cout << "testnum " << testnum << std::endl;
        for(divprime = 0 ; testnum % primes[divprime] != 0 ; ++divprime)
        {
            std::cout << "divprime " << divprime << std::endl;
            if(primes[divprime] >= sqrt(testnum))
            {
                primes[fillcell] = testnum;
                sum = sum + testnum;
                std::cout << "prime found " << testnum << std::endl;
                break;
            }
        }
    }
}
std::cout << "sum" << sum << std::endl;
}

2 个答案:

答案 0 :(得分:2)

在学习步行艺术之前,不要试着跑。

除非必须,否则请勿使用int primes[100]等固定大小的数组。

一个原因是,这需要程序员预先确定所需的大小 - 例如转到nth Prime Page并发现有148933个素数低于2000000.

它还要求程序员在代码中添加额外的检查,以确定数组访问不超出数组的范围(除非您使用的语言为您执行此操作,如Java或C#) 。另一个原因是它需要您添加用于簿记的代码,即跟踪当前占用的阵列的多少个单元。

最后但并非最不重要的一点是,将148933个整数数组分配为自动变量(即在堆栈上)可能会导致崩溃,因为它会导致堆栈崩溃。

如果您使用std::vector<>,那么所有这些令人头疼的问题立即消失,您的代码变得更加简单。

从一个简单的计划开始,并使用单独的代码片段实现这些步骤。如果每一段代码都有一个简单明确的责任,那么就更容易掌握事物。如果你把所有东西都纠缠在一个很糟糕的丛中,情况会变得更加困难。

例如,如果您存储在向量中找到的素数,那么这允许您查看那里的数字以查看是否一切都很好,并且可能将它们与已知的素数列表(如The First 10,000 Primes)进行比较,或primos.mat.br处的最高达1,000,000,000,000的素数。你可以看,但你没必要。如果你用输出代码散布所有内容,那么你总是要查看所有这些代码。如果您只是将它们添加到总和中,那么除非您调试程序并遵循每一步,否则您无法看到它们。

将您的计划表示为伪代码,以便您可以一目了然地完全理解它。如果您没有计划,或者您不了解计划,那么结果很可能是cr * p。

for each candidate n between 2 and 2000000
    for each known prime p up to sqrt(n)
        if p divides n
            break
    if no divisor p was found // must be a new prime
        add n to the list of found primes

显然,标准'如果没有找到除数p'要求你使用一个标志,如divisor_found,在内循环之前初始化为false。因此,第一个改进:

for each candidate n between 2 and 2000000
    divisor_found := false
    for each known prime p up to sqrt(n)
        if p divides n
            divisor_found := true
            break
    if not divisor_found // must be a new prime
        add n to the list of found primes

这可以毫不费力地实施。候选人的列举可以通过跳过一些不太可能是素数的数字来改进,例如两倍的倍数:

add 2 to the list of found primes
for each odd number between 3 and 2000000
    ...

这会立即将您的工作量减少一半,这是'wheel'的最简单示例。对于这样的问题,通过从5开始并以交替方式递增2和4来跳过3的倍数(mod 6轮)是非常实用的。

add 2 and 3 to the list of found primes
for n = 5 to 2000000 step 6  // 6 = 2 * 3
    try n
    try n + 2

在这里,在try中完成的试验分区不需要考虑2或3作为潜在的除数,因为你的枚举候选人的方法已经排除了所有的倍数。跳过5的倍数的扩展也相当简单。

如果在最内层循环的每次迭代期间执行像sqrt(n)这样的昂贵计算,那么代码会慢下来爬行。 n在内循环的生命周期内不会改变,因此在循环头中计算一次值,而不是不必要地重复计算。

尽管如此,随机尝试不同的整数数据类型将无处可寻。如果值不能变为负数 - 就像这里的情况那样 - 那么unsigned应该是您的首选。在当前系统上,这通常对应于uint32_t,这对于这个Euler任务中涉及的小数字来说已经足够了。你可以通过引入一个合适的typedef来省去一些麻烦;这样你只需要改变一个单一的定义:

typedef std::uint32_t num_t;
typedef std::uint64_t sum_t;
num_t const N = 2000000;
...

std::vector<num_t> primes;
...

for (num_t n = 3; n <= N; n += 2)
   ...

sum_t sum = 0;
for (num_t p: primes)
   sum += p;

我已经添加了一个单独的sum_t,因为对总和的球场估计远远超出uint32_t的容量。

无论如何,你应该认真考虑在这里使用Sieve of Eratosthenes。它比轮式试验分割更简单,速度提高了几个数量级 - 即使最简单的渲染也应该在几毫秒内解决这个欧拉任务。

答案 1 :(得分:1)

DarthGizka给了你一些很好的建议,包括改变算法以使用Eratosthenes的Sieve。这是我的解决方案,我将留给您用您选择的语言重写:

function sumPrimes(n) # sum of primes <= n
    sum := 0
    sieve := makeArray(2..n, True)
    for p from 2 to n step 1
        if sieve[p]
            sum := sum + p
            for i from p * p to n step p
                sieve[i] := False
    return sum

如果 n 太大而无法形成sieve数组,则需要对数组进行分段。如果您必须这样做,请参阅here。还有一种算法,勒让德计算素数的方法的一种变体,它计算素数之和,但它非常复杂。