找出所有低于 40 亿的素数的最快方法
Posted
技术标签:
【中文标题】找出所有低于 40 亿的素数的最快方法【英文标题】:Fastest way to find all primes under 4 billion 【发布时间】:2013-09-26 12:14:52 【问题描述】:我正在尝试打印 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;
【问题讨论】:
打印出前十亿个素数需要 waaaaay 超过 4 分钟。 我认为最快的方法是跳过所有你知道不是素数的数字,例如,以2
、4
、5
(5 之后)、6
结尾的数字, 8
, 0
如果需要 4 分钟。对于 10 亿,应该需要 16 分钟。 40 亿美元,与等待 SO 的答案相比,这还不算太糟糕。一旦你计算了它们,你就不需要再次计算它们了。哎呀,只是让他们离开网络并完成它!
为了减少存储需求,关于素数或非素数的信息在每个字节中存储了 30 个整数。只需要一位来存储整数的素数或非素数。整数的值由位的位置已知。在每 30 个整数中,对于 N >= 1,可能是素数的数是 N*30+1、N*30+7、N*30+11、N*30+13、N*30+17、N* 30+19, N*30+23, N*30+29 rsok.com/~jrm/printprimes.html
这是一场谁可以做最好的谷歌搜索的比赛吗? ;-)
【参考方案1】:
我在blog 讨论生成大量素数的问题,我发现前十亿素数的总和是 11138479445180240497。我描述了四种不同的方法:
蛮力,使用试除法从 2 开始测试每个数字。
使用 2、3、5、7 轮生成候选,然后使用强伪素数测试对基数 2、7 和 61 进行素数测试;这种方法最多只能工作到 2^32,这对我来说不足以对前十亿个素数求和,但对你来说就足够了。
Melissa O'Neill 提出的一种算法,它使用嵌入优先级队列的筛子,速度非常慢。
Eratosthenes 分段筛,速度非常快,但需要空间来存储筛分素数和筛子本身。
【讨论】:
【参考方案2】:这可能会加快一点:
#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 开始并忽略直到限制的倍数)。
【讨论】:
是的。它在 ideone 上编译并运行(但超时)。在我的电脑上用VS2010和VS2012编译运行大概需要2分钟。【参考方案3】:最快的方法可能是采用预先生成的列表。
http://www.bigprimes.net/ 有前 14 亿个质数可供下载,其中应该包括每个低于 300 亿个左右的质数。
我认为加载二进制文件可能需要很长时间,因为它的大小只有几 GB。
【讨论】:
在线竞赛网站通常只接受源代码,并且有源代码大小限制以防止这种情况发生。 经常处理素数的程序员普遍认为生成素数比从磁盘中读取要快。【参考方案4】:您是否对花费最多时间的内容进行了基准测试?是筛子本身,还是输出的写法?
加快筛子速度的一种快速方法是不要担心所有偶数。只有一个偶数是质数,您可以对其进行硬编码。这会将您的数组大小减半,如果您遇到物理内存的限制,这将非常有帮助。
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
输出它。你甚至可以去老学校,使用fwrite
和stdout
。
【讨论】:
【参考方案5】:我用 C 语言编写了一种快速方法,在我的 Ryzen 9 3900x 上使用单线程在三分钟内输出多达 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;
【讨论】:
以上是关于找出所有低于 40 亿的素数的最快方法的主要内容,如果未能解决你的问题,请参考以下文章