C++质数区间

C++ prime numbers interval

本文关键字:区间 C++      更新时间:2023-10-16

我必须编写脚本来打印给定范围内的质数。 目前我正在使用以下脚本:

#include <iostream>
using namespace std;
int main()
{
    int n,p,m;
    cin>>n >> m;
    int * arr;
    arr= new  int[m+1];
    for (int i=0; i<=m; i++)
    {
      arr[i]=0;
    }
    for(int i=2;i<=m;i++){
        if(arr[i]==0)
        {   p=i;
            for (int j=2;p*j<=m;j++)
            {
                arr[p*j]=1;
            }
        }
    }
    for(int i=n;i<=m;i++){
        if(arr[i]==0)cout<<i<<endl;
    }
    delete[] arr;
    return 0;
}

它适用于小输入(它将 1 打印为素数,但这很容易修复(。 但是,当我输入像 1999998973 和 1999999973 这样的数字时,它会崩溃并显示 bad_alloc .

我知道我可能做了一个巨大的阵列,但我不知道如何修复它。 我尝试了不同的算法,但它真的很慢,我需要它在大约 2 秒左右的时间内打印数字。 有人有想法吗?

似乎适用于我从 2 到 500 查看的数字。它不会随着1999998973到1999999973范围并给出这些结果,我不知道它们是否正确:

1999999003199999901319999990491999999061199999908119999990871999999093199999909719999991171999999121199999915119999991711999999207199999921919999992711999999321199999937319999994231999999439199999949919999995531999999559199999957119999996091999999613199999962119999996431999999649199999965719999997471999999763199999977719999998111999999817199999982919999998531999999861199999987119999998731999999913199999992719999999431999999973

int n,p,m;
cout << "enter two numbers" << endl;
cin >> n >> m;
int size = (m - n) + 1; 
int * arr = new  int[size];
// initialize the array to all zeros
for (int i=0; i < size; i++)
{
  arr[i]=0;
}
// loop once for the entire set of numbers from 2 to max value to 
// multiply with (sqrt of top of range)
int max = sqrt(m);
for(int i = 2; i <= max ;i++)
{
    p = i;
    for (int j=2 ; p*j <= m ; j++)
    {
        if ( (p * j) >= n  )
        {
           arr[(p*j) - n] = 1;
        }
    }
}
for(int i = 0; i < size; i++)
{
    if ( arr[i]==0 )
        cout << (i + n) << endl;
}
delete[] arr;

问题是数组的空间。

选项 1:您可以将数组缩小到范围的大小。 你获得了空间,但你有额外的计算,因为你必须考虑你的埃拉托斯滕筛子范围之外的所有数字。

选项 2:使用 bit 而不是 int 来压缩数据。 您可以使用vector<bool>轻松做到这一点。

使用选项 2,您可以保持代码原样,只需将 arr 更改为:

vector<bool> arr(m+1);

您甚至可以通过以下方式节省初始化循环:

vector<bool>arr(m+1, false);

无论您采取什么选择,我都建议您用添加剂版本替换您的镭脂筛的内环:

for (int i = 2; i <= m; i++){    
    if (arr[i] == 0)
    {
        for (int j = i*2; j <= m; j+=i)  // no p, no multiplication, just add i at each iteration
        {
            arr[j] = 1;
        }
    }
}

时间

节省空间会增加时间。 然而,我怀疑即使你有足够的记忆,你也会在 2 秒内得到如此巨大的数字的答案。如果您必须处理 2 000 000 000 个数字,则需要在最多 1 纳秒内处理每个数字,即一条 CPU 指令以满足您的期望。

我建议使用轮子分解来更改算法。

如提供的链接的第一段所述,您可以通过除以 2,3,5 来确定一个数字是否是素数,然后除以全等到模 30 的数字,直到该数字的平方根。

下面列出了一个这样的实现。通过以这种方式检查素数,您无需创建最多 m 的数组。您唯一需要的数组是 wheelOf30[]

#include <math.h>
static const bool wheelOf30[] = {
    false,  true, false, false, false, false, false,  true, false, false,  //  1,  7,
    false,  true, false,  true, false, false, false,  true, false,  true,  // 11, 13, 17, 19,
    false, false, false,  true, false, false, false, false, false,  true   // 23, 29.
};
bool isPrime(int num)
{
    // first, modulo by 2, 3, and 5
    if ((num % 2 == 0) || (num % 3 == 0) || (num % 5 == 0))
        return false;
    int rootNum = sqrt(num);
    // and then, divide by the congruent numbers represented by wheel of 30
    for (int i = 7; i <= rootNum; i += 2)
    {
        // divide the number with the ones congruency to modulo 30 is true
        if (wheelOf30[ i % 30 ])
        {
            if (num % i == 0)
                return false;
        }
    }
    return true;
}

您的主程序如下所示:

#include <iostream>
extern bool isPrime(int num);
int main(int argc, const char * argv[])
{
    int n, m;
    cin >> n >> m;
    for (int i=n; i<=m; ++i)
        if (isPrime(i))
            cout << i << endl;
}