【问题标题】:Sieve of Eratosthenes c++ implementation errorEratosthenes C++ 实现错误的筛
【发布时间】:2012-03-27 03:14:41
【问题描述】:

我用 C++ 编写了这个埃拉托色尼筛法的实现,但只要代码达到 59(第 16 个素数),它就会停止工作。在我的旧机器上只能达到 37。当我调试程序时,所有变量似乎都工作正常;该程序只是崩溃。这里是:(我知道它有很多 cmets,而且很多是不必要的。)

// 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;
}

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


这是一个更新的 sn-p,仅包含更改:

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

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

【问题讨论】:

  • 请注意:这不是埃拉托色尼筛。您正在检查从 p² 开始的每个数字是否可以被 p 整除:if (j%current_sieve == 0) sieve[j] = 1;,这是试除法。
  • @DanielFischer 哦,好吧。有效。 :P 你能以其他方式检查 C++ 中的可分性吗?我知道像 9 或 11 这样的数字有除法技巧,但对于更大的素数......
  • 当然可以,只是速度较慢。埃拉托色尼筛法的诀窍是根本不检查可分性。您交叉了p 的倍数,因此当您在筛子中找到素数p 时,您可以通过将索引增加pfor(mult = p*p; mult &lt;= limit; mult += p) 来消除倍数。我已经在不同程度上详细阐述了herehere,如果你喜欢长篇阅读,here

标签: c++ visual-studio-2010


【解决方案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;

【讨论】:

  • 感谢您的快速回答!我会试试的:)
  • 是的,也是。我更改了筛子大小以更轻松地调试并迅速忘记它。而且我认为我的优化仍然有效,但效率不高......无论如何,我用我的新代码更新了我的帖子。
  • @Ernest3.14,无论从 n^2 而不是 2n 开始,你可能会得到什么改进(我并不完全相信这会奏效,但我还没有找到反例,所以您可能是对的)将被您的循环添加一个并检查每个数字的事实完全抹杀。您可以只添加 curr_sv 而不检查:(a+1)n = an + n
  • 呃。这确实有道理。是否有任何其他优化(以防我需要找到 lots 的素数)?
  • 对于很多素数,是的,但我说的是 很多。 电子筛可以轻松处理大量数字。除此之外,a-sieve (Atkin) 可能会更好。除此之外, 我最喜欢的是 pax-sieve:stackoverflow.com/questions/9715256/… :-)
【解决方案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

【讨论】:

  • 不是很好的形式发布家庭作业或欧拉问题的完整解决方案,关键是在这些情况下指导,而不是为他们做工作:-)
  • @paxdiablo:说实话,给他一个工作版本供他比较比对他的解决方案进行逆向工程要快。有总比没有好。如果他在不理解的情况下逐字传递,他不会走得太远。
  • @user1131467 因为你必须为 Project Euler 问题提交答案,所以即使是你答案的最后一行也足以让 OP 走得更远。
  • 我真的很喜欢弄清楚它们,;)
猜你喜欢
  • 2020-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多