Sieve of Eratosthenes c++ 实现错误

Sieve of Eratosthenes c++ implementation error

提问人:Ernest3.14 提问时间:3/27/2012 最后编辑:Ernest3.14 更新时间:1/23/2015 访问量:736

问:

我用 c++ 编写了这个 Sieve of Eratosthenes 的实现,但每当代码达到 59(第 16 个素数)时,它就会停止工作。在我的旧机器上,它只能达到 37。当我调试程序时,所有变量似乎都工作正常;程序只是崩溃了。这里是:(我知道它有很多评论,很多都是不必要的。

// Problem 7:What is the 10 001st prime number?

/*This program generates a list of prime numbers and uses the Sieve of Eratosthenes to find them.
 *This is done by crossing out multiples of the first known prime (2), taking the first number
 *not yet crossed out, and setting that as the next prime to "sieve" with.
 */

#include "stdafx.h"
#include <iostream>

using namespace std;

int main()
{
    int placeholder;                    //added at the end to prevent window from closing
    const int sieve_size = 10000;       //amount of #s to sieve through
    const int solution_number = 10001;  //# of primes to generate
    long long solution;                 //the 10 001st prime #
    long long current_sieve;            //current # sieving with
    int number_of_primes = 1;           //we know the first prime -- 2
    long long *primes = new long long [number_of_primes];
    primes[0] = 2;                  //2 is the first prime
    bool flag_havePrime = 0;        //whether we have our next prime yet; used when saving a prime
    bool sieve[sieve_size] = {0};   //0 is "could-be-prime" (not composite), 1 is composite.
    sieve[0] = 1;   //0 and 1 are not prime #s
    sieve[1] = 1;

    for (int i = 0; number_of_primes <= solution_number; i++)   //each loop sieves with a different prime
    {
        current_sieve = primes[i];

        //This next loop sieves through the array starting with the square of the number we will sieve with,
        //this optimizes the run time of the program.
        for (long long j=current_sieve*current_sieve; j <= sieve_size; j++)
            if (j%current_sieve == 0) sieve[j] = 1;

        /*This loop gets our next prime by looking through the array until
         *it encounters a number not crossed out yet. If it encounters a prime,
         *it increments the number of primes, then saves the new prime into
         *primes[]. It also prints that prime (for debugging purposes).
         *The "for" loop ends when it finds a prime, which it knows by encountering
         *the "havePrime" flag. This needs to be reset before every run.
         */

        for (long long j = primes[number_of_primes-1]+1; flag_havePrime == 0; j++)
        {
            if (sieve[j] == 0)
            {
                number_of_primes++;
                primes[number_of_primes-1] = j;             //because array counting starts @ 0
                cout << primes[number_of_primes-1] << endl; //because array counting starts @ 0
                flag_havePrime = 1;
            }
        }
        flag_havePrime = 0; //resetting this flag
    }
    solution = primes[number_of_primes-1];  //because array counting starts @ 0
    delete[] primes;
    primes = 0;

    cout << "The 10 001st prime number is:\n";
    cout << solution << endl;
    cin >> placeholder;
    return 0;
}

我认为这可能是一个溢出问题?


下面是一个更新的代码片段,仅包含更改:

const int sieve_size = 500000;
long long *primes = new long long [solution_number];

调试会返回(喘息)堆溢出,但运行编译版本不会。编译版本在 104759 处停止,超过 1。这可能很容易解决。但是该程序不会打印最后一位,它为您提供了解决方案。奇怪。

C++ 可视化工作室-2010

评论

0赞 Daniel Fischer 3/27/2012
请注意:这不是埃拉托色尼的筛子。您正在检查从 p² 开始的每个数字是否可被 p: 整除,这就是试验除法。if (j%current_sieve == 0) sieve[j] = 1;
0赞 Ernest3.14 3/27/2012
@DanielFischer 那好吧。成功了。:P你能用任何其他方式检查 c++ 中的可除性吗?我知道像 9 或 11 这样的数字有可除性技巧,但对于更大的素数......
1赞 Daniel Fischer 3/27/2012
当然可以,只是速度较慢。埃拉托色尼筛子的诀窍是根本不检查可分割性。你交叉了 的倍数,所以当你在筛子里找到一个素数时,你通过将索引递增 , 来划掉倍数。我在这里这里和这里都有不同程度的阐述,如果你喜欢长篇大论,请在这里pppfor(mult = p*p; mult <= limit; mult += p)

答:

2赞 paxdiablo 3/27/2012 #1
int number_of_primes = 1;           //we know the first prime -- 2
long long *primes = new long long [number_of_primes];

这将创建一个单元素数组。我很确定你需要比这更大的东西来存储素数。

具体来说,一旦你开始设置诸如(例如)这样的值,你就进入了未定义行为的领域。primes[11]

也许您可能要考虑使用一个不同的变量来表示语句中的大小,轻推,轻推,眨眼,眨眼,点头就像对盲马眨眼一样好:-)new


在该代码中还存在一些其他问题。最主要的是您的筛子本身只有 10,000 个元素长。筛子的想法是你拿出大量的东西并过滤掉那些不匹配的东西。就其价值而言,10,001st 低于 105,000,因此您的筛子至少应该那么大。

其次,我看到人们使用数字的平方来优化因子的发现,但不是这样:

for (long long j=current_sieve*current_sieve; j <= sieve_size; j++)
    if (j%current_sieve == 0) sieve[j] = 1;

您想要的是从当前筛子的两倍开始,然后每次添加它,例如:

for (long long j = current_sieve * 2; j < sieve_size; j += current_sieve)
    sieve[j] = 1;

评论

0赞 Ernest3.14 3/27/2012
是的,那也是。我更改了筛子尺寸以更轻松地调试,并立即忘记了它。而且我认为我的优化仍然有效,但效率不高......无论如何,我用我的新代码更新了我的帖子。
0赞 paxdiablo 3/27/2012
@Ernest3.14,无论您从 n^2 而不是 2n 开始可能会获得任何改进(我还不相信这会起作用,但我还没有找到反例,所以你可能是对的)将被您的循环加 1 并检查每个数字的事实完全抹去。您可以只添加curr_sv而不选中:。(a+1)n = an + n
0赞 Ernest3.14 3/27/2012
咄。这确实是有道理的。还有其他优化吗(以防我需要找到很多素数)?
0赞 paxdiablo 3/27/2012
对于很多素数,是的,但我说的是很多。电子筛可以轻松处理大量数据。除此之外,a-sieve(阿特金)可能更好。除此之外我最喜欢的是 pax-sieve:stackoverflow.com/questions/9715256/... :-)
0赞 Daniel Fischer 3/27/2012
@pax,我预计我误解了,但如果您怀疑从 p^2 而不是 2p 开始交叉是否保证是正确的:是的,因为如果 n 是复合的,并且 d 是 n 的除数(介于 1 和 n 之间),那么 d 和 n/d 中的一个至少不大于 .在不失去普遍性的情况下,让.的任何质因数 是 ,所以 n 将被划掉为 的倍数。sqrt(n)d <= sqrt(n)qd<= sqrt(n)q
0赞 Andrew Tomazos 3/27/2012 #2

下面是一个固定版本进行比较:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    const int sieve_size = 1 << 20;
    const int solution_number = 10001;
    int number_of_primes = 0;
    vector<bool> sieve(sieve_size, true);

    for (int i = 2; i < sieve_size; i++)
    {
        if (!sieve[i])
            continue;

        if (++number_of_primes == solution_number)
        {
            cout << "The 10 001st prime number is:" << i << endl;
            return 0;
        }

        for (long long j = i*2; j < sieve_size; j += i)
            sieve[j] = false;
    }
}

输出:

The 10 001st prime number is:104743

评论

1赞 paxdiablo 3/27/2012
发布家庭作业或欧拉问题的完整解决方案不是很好的形式,重点是在这些情况下提供指导,而不是为他们做工作:-)
0赞 Andrew Tomazos 3/27/2012
@paxdiablo:说实话,给他一个工作版本供他比较比对他的解决方案进行逆向工程要快得多。有总比没有好。如果他在不理解的情况下逐字逐句地交出它,他就不会走得很远。
0赞 Daniel Fischer 3/27/2012
@user1131467 由于你所要提交的 Project Euler 问题只是答案,因此即使仅凭答案的最后一行,OP 也足以让 OP 走得足够远。
0赞 Ernest3.14 3/27/2012
不过,我真的很喜欢弄清楚它们,;)