关于C#:使用Atkin的Sieve计算素数之和低于200万

Calculating sum of prime number below 2 million by using Sieve of Atkin

我在做项目欧拉,我遇到了这个问题。我在vs 2013中运行代码,程序因为溢出而崩溃。

这是我的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
void problem10()
{
    long long int iter = 2, sum = 0;

    //Sieve of Atkin
    bool isPrime[PRIME_LIMIT+1];

    for (long long int i = 5; i <= PRIME_LIMIT; i++)
    {
        isPrime[i] = false;
    }

    long long int lim = ceil(sqrt(PRIME_LIMIT));

    for (long long int x = 1; x <= lim; x++)
    {
        for (long long int y = 1; y <= lim; y++)
        {
            long long int n = 4 * x*x + y*y;
            if (n <= PRIME_LIMIT && (n % 12 == 1 || n % 12 == 5))
            {
                isPrime[n] = true;
            }

            n = 3 * x*x + y*y;
            if (n <= PRIME_LIMIT && (n % 12 == 7))
            {
                isPrime[n] = true;
            }

            n = 3 * x*x - y*y;
            if (x > y && n < PRIME_LIMIT && n % 12 == 11)
            {
                isPrime[n] = true;
            }
        }
    }

    // eliminate composites by seiving
    for (long long int n = 5; n <= lim; n++)
    {
        if (isPrime[n])
        {
            for (long long int k = n*n; k <= PRIME_LIMIT; k+= k*k)
            {
                isPrime[k] = false;
            }
        }
    }

    for (long long int n = 5; n <= PRIME_LIMIT; n++)
    {
        if (isPrime[n])
        {
            sum += n;
        }
    }
    sum = sum + 2 + 3;
    printf("%lld
"
, sum);
    /* //A basic approach -- too slow
    while (iter < PRIME_LIMIT)
    {
        bool isDivisible = false;
        int prime = iter;
        for (int a = 2; a < iter; a++)
        {
            if (prime%a == 0)
            {
                isDivisible = true;
                break;
            }
        }

        if (isDivisible){}
        else
        {
            //printf("prime is: %d
", prime);
            sum += prime;
        }
        iter++;
    }
    printf("Sum of prime is: %d
", sum);
    */

}

该方法包括两种方法来计算PRIME_LIMIT范围内所有素数的和。第二种方法要花太长时间才能得到结果,可能要花一整天的时间。第一种方法是使用atkin的筛子,程序崩溃!

我的密码有错误吗?请帮忙!


那么,我们来谈谈这个问题:

整数大小

与Java非常类似,C中的整数限制了它们适合的大小。在这里,您选择使用long long int。幸运的是,对于这个问题,总和将适合该数据类型。对于其他ProjectEuler问题,需要使用bigint类。

你的慢方法

如果你再加一个,这个缓慢的方法实际上是可以的。我们知道,我们需要搜索的除数列表实际上比2 ... n中的所有数字都要少。所以我们可以将其中一个循环改为:

1
2
3
4
5
int max = ciel(sqrt(iter));
for (int a = 2; a < max; a++)
    if (prime % a == 0)
        isDivisible = true;
        break;

如果我们这样做,您的代码将相对较快地完成。

你的快速方法

我还没有完全读完这段代码,因为我记得它不像是埃拉托斯滕的筛子,但至少,你的分配会溢出堆栈。

1
2
#define PRIME_LIMIT 2000000
bool isPrime[PRIME_LIMIT+1];

让我们来解决这个问题:

1
2
#define PRIME_LIMIT 2000000
static bool isPrime[PRIME_LIMIT+1]

或:

1
2
#define PRIME_LIMIT 2000000
bool *isPrime = calloc(PRIME_LIMIT + 1, sizeof(bool));

建议

我真的建议你不要从实施阿特金的筛选开始。如果我将此作为学习练习来实施,我将按以下顺序执行:

  • 你上面所做的缓慢的方法。在Python中,类似的代码需要大约1分钟来解决问题。我怀疑C能在10秒钟内完成(或更快)。

  • 埃拉托斯滕斯筛网是一种简单得多的筛网。我建议下一步使用它。

  • 然后尝试实施阿特金的筛选。不过,对于这种规模的问题,这种速度的算法是完全不必要的。