难以从大范围中筛选素数

Trouble sieving primes from a large range

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;

int main() {
    int t,m,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&m,&n);
        int rootn=sqrt(double(n));
        bool p[10000];      //finding prime numbers from 1 to square_root(n)
        for(int j=0;j<=rootn;j++)
            p[j]=true;
        p[0]=false;
        p[1]=false;
        int i=rootn;
        while(i--)
        {
            if(p[i]==true)
            {
                int c=i;
                do
                {
                    c=c+i;
                    p[c]=false;
                }while(c+p[i]<=rootn);
            }
        };
        i=0;
        bool rangep[10000]; //used for finding prime numbers between m and n by eliminating multiple of primes in between 1 and squareroot(n)
        for(int j=0;j<=n-m+1;j++)
            rangep[j]=true;
        i=rootn;
        do
        {
            if(p[i]==true)
            {
                for(int j=m;j<=n;j++)
                {
                    if(j%i==0&&j!=i)
                        rangep[j-m]=false;
                }
            }
        }while(i--);
        i=n-m;
        do
        {
            if(rangep[i]==true)
                printf("%d\n",i+m);
        }while(i--);
        printf("\n");
    }
    return 0;
    system("PAUSE");
}

您好,我正在尝试使用埃拉托色尼筛法在 m 到 n 之间的范围内查找素数,其中 m>=1 且 n<=100000000。当我输入 1 到 10000 时,结果是正确的。但是对于更大的范围,即使我增加数组大小,堆栈也会溢出。

出现错误的原因

你将一个巨大的数组声明为局部变量。这就是为什么当main的堆栈帧被压入时需要如此多的内存以致于产生堆栈溢出异常。 Visual studio 足以分析预计 运行 时间堆栈使用的代码并在需要时生成异常。

使用这个紧凑的实现。此外,如果需要,您可以在函数中声明 bs 。不要使实现变得复杂。

实施

typedef long long ll;
typedef vector<int> vi;
vi primes;
bitset<100000000> bs;
void sieve(ll upperbound) {         
  _sieve_size = upperbound + 1;                   
  bs.set();                                                 
  bs[0] = bs[1] = 0;                                     
  for (ll i = 2; i <= _sieve_size; i++) 
  if (bs[i]) {  //if not marked  

    for (ll j = i * i; j <= _sieve_size; j += i) //check all the multiples 
         bs[j] = 0; // they are surely not prime :-)

    primes.push_back((int)i); // this is prime 
} } 

从 main() 调用 sieve(10000);。您在向量 primes.

中有素数列表

注意: 正如评论中提到的——Whosebug 在这里是非常意外的错误。您正在实施筛子,但如果您使用 bistet 而不是 bool.

会更有效率

很少有类似 if n=10^8 then sqrt(n)=10^4 这样的事情。你的 bool 数组是 p[10000]。所以有机会越界访问数组。

一个简单易读的实现

void Sieve(int n) {
    int sqrtn = (int)sqrt((double)n);
    std::vector<bool> sieve(n + 1, false);
    for (int m = 2; m <= sqrtn; ++m) {
        if (!sieve[m]) {
            cout << m << " ";
            for (int k = m * m; k <= n; k += m)
                sieve[k] = true;
        }
    }
    for (int m = sqrtn; m <= n; ++m)
        if (!sieve[m])
            cout << m << " ";
}

我同意其他答案, 说你基本上应该重新开始。 你甚至关心为什么你的代码不起作用? (你实际上并没有问。)

我不确定你的代码中的问题 还没有被准确识别。 首先,我将添加此评论以帮助设置上下文:

// For any int aardvark;
// p[aardvark] = false   means that aardvark is composite (i.e., not prime).
// p[aardvark] = true    means that aardvark might be prime, or maybe we just don’t know yet.

现在让我提请您注意这段代码:

        int i=rootn;
        while(i--)
        {
            if(p[i]==true)
            {
                int c=i;
                do
                {
                    c=c+i;
                    p[c]=false;
                }while(c+p[i]<=rootn);
            }
        };

n≤100000000(虽然你的代码没有检查),所以, 据推测,rootn≤10000,也就是p[]的维数(大小)。 上面的代码是说,对于每个整数 i (无论是质数还是合数), 2×i、3×i、4×i等,根据定义,都是合数。 所以,对于 c 等于 2×i, 3×i, 4×i, …, 我们设置 p[c]=false 因为我们知道 c 是复合的。

但是仔细看代码。 它设置 c=c+i 并表示 p[c]=false before 检查 c 是否仍在范围内 成为 p[] 的有效索引。 现在,如果 n≤25000000,则 rootn≤5000。 若irootn,则i≤5000,只要c≤5000,则c+i≤10000。 但是,如果 n>25000000,则 rootn>5000, 以及序列 i=rootn;c=i;c=c+i; 可以将 c 设置为大于 10000 的值。 然后您使用该值索引到 p[]。 这可能是发生堆栈溢出的地方。

哦,顺便说一句;你不需要说 if(p[i]==true)if(p[i]) 够用了。

雪上加霜的是,同一块中还有第二个错误: while(c+p[i]<=rootn)ciints, pbools 的数组,所以 p[i]bool — 然而你添加了c<sub> </sub>+<sub> </sub>p[i]。 我们从 if 知道 p[i]true, 在数值上等于 1 — 所以你的循环终止条件是 while (c+1<=rootn); 即,同时 crootn-1。 我想你的意思是 while(c+i<=rootn).

哦,还有,你怎么会有可执行代码 在无条件 return 语句之后立即? 不可能到达 system("PAUSE"); 语句。

(我并不是说这些是唯一的错误; 他们正是我突然想到的。)
______________
OK,劈头盖脸,n必须≥‰25010001 (即 50012)在 rootn>5000.

之前