找到 40 亿以下所有素数的最快方法

Fastest way to find all primes under 4 billion

本文关键字:方法 找到      更新时间:2023-10-16

我正在尝试打印 2**32 以下的每个质数。 现在我正在使用布尔向量来构建筛子,然后在制作筛子后打印出素数。 打印出高达 10 亿的素数需要 4 分钟。 有没有更快的方法来做到这一点??这是我的代码

#include <iostream>
#include <cstdlib>
#include <vector>
#include <math.h>
using namespace std;
int main(int argc, char **argv){
  long long limit = atoll(argv[1]);
  //cin >> limit;
  long long sqrtlimit = sqrt(limit);
  vector<bool> sieve(limit+1, false);
  for(long long n = 4; n <= limit; n += 2)
    sieve[n] = true;
  for(long long n=3; n <= sqrtlimit; n = n+2){
    if(!sieve[n]){
      for(long long m = n*n; m<=limit; m=m+(2*n))
        sieve[m] = true;
    }
  }
  long long last;
  for(long long i=limit; i >= 0; i--){
    if(sieve[i] == false){
      last = i;
      break;
    }
  }
  cout << last << endl;
  for(long long i=2;i<=limit;i++)
  {
    if(!sieve[i])
      if(i != last)
        cout<<i<<",";
      else
        cout<<i;
  }
  cout<<endl;

我在我的博客上讨论了生成大量素数的问题,我发现前十亿素数的总和是11138479445180240497。我描述了四种不同的方法:

  1. 蛮力,使用试验除法测试从 2 开始的每个数字。

  2. 使用 2,3,5,7 轮生成候选者,然后用强伪素数检验以 2、7 和 61 为底测试素数; 这种方法最多只能有效 2^32,这不足以让我对前十亿个素数求和,但对你来说已经足够了。

  3. 由于Melissa O'Neill使用嵌入在优先级队列中的筛子的算法,这非常慢。

  4. 埃拉托色尼的分段筛,速度非常快,但需要空间来存储筛子和筛子本身。

这可能会加快速度:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main()
{
    std::vector<unsigned long long> numbers;
    unsigned long long maximum = 4294967296;
    for (unsigned long long i = 2; i <= maximum; ++i)
    {
        if (numbers.empty())
        {
            numbers.push_back(i);
            continue;
        }
        if (std::none_of(numbers.begin(), numbers.end(), [&](unsigned long long p)
        {
            return i % p == 0;
        }))
        {
            numbers.push_back(i);
        }
    }
    std::cout << "Primes:  " << std::endl;
    std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<int>(std::cout, " "));
    return 0;
}

它有点像埃拉托色尼筛子的倒数(不是从限制下的每个数字开始并消除倍数,而是从 2 开始并忽略倍数直到极限)。

最快的方法可能是获取预生成的列表。

http://www.bigprimes.net/有前 14 亿个素数可供下载,其中应包括 300 亿以下的每个素数。

我想加载二进制文件在几千兆字节时可能需要很长时间。

您是否对花费最多时间的内容进行了基准测试?是筛子本身,还是输出的写入?

加快筛子速度的一种快速方法是停止担心所有偶数。只有一个偶数是素数,您可以对其进行硬编码。这将数组的大小减少一半,如果您遇到物理内存的限制,这将非常有帮助。

vector<bool> sieve((limit+1)/2, false);
...
  for(long long m = n*n/2; m<=limit/2; m=m+n)
    sieve[m] = true;

至于输出本身,cout是出了名的低效。自己调用itoa或等效项,然后使用 cout.write 输出它可能更有效。您甚至可以走老派,将fwritestdout一起使用。

我在 Ryzen 9 3900x 上使用单个线程在 C 语言中编写了一种快速方法,可以在三分钟内输出多达 40 亿的素数。如果将其输出到文件,它最终是 2.298GB,我认为它使用大约 20GB 的内存来完成。

#include <stdlib.h>
#include <stdio.h>
#define ARRSIZE 4000000000
#define MAXCALC ARRSIZE/2
int main() {
    long int z;
    long int *arr = (long int*) malloc((ARRSIZE) * sizeof(long int));
    for (long int x=3;x <= MAXCALC; x++) {
        if (x % 10 == 3 || x % 10 == 7 || x % 10 == 9) {
            for (long int y=3; y < MAXCALC; y++){
                    z = x * y;
                    if (z < ARRSIZE)
                        arr[z] = 1;
                    else
                        break;
            }
        }
    }
    printf("2 3 5 ");
    for (long int x=7; x < ARRSIZE; x++) {
        if (x % 2 != 0 && x % 10 != 5)
            if (arr[x] != 1)
                printf("%ld ", x);
    }
    printf("n");
    free(arr);
    return EXIT_SUCCESS;
}

我编写了代码 i python,在 8.7 秒内输出所有小于十亿的素数。但我不确定你是前40亿个素数还是艾伦素数比这少。无论如何,这是我的解决方案:

import numpy as np
from math import isqrt
def all_primes_less_than(n):
    is_prime = np.full(n,True)
    is_prime[:2] = False
    is_prime[4::2] = False
    for p in range(3,isqrt(n),2):
        if is_prime[p]: is_prime[p*p::p] = False
    return is_prime.nonzero()[0]
相关文章: