生成从1到n的素数,崩溃为n> 3亿

时间:2014-10-06 07:01:21

标签: c primes sieve-of-eratosthenes

关于如何让这个程序工作n = 1万亿(除了升级/购买新计算机)之外的任何建议?

错误如下:构建后,正在执行的程序(弹出命令行样式输出窗口)然后快速关闭,我收到以下错误" ProjectPrimes.exe已停止工作(Windows正在寻找这个问题的解决方案。"我怀疑这与内存问题有关,因为我第一次遇到n = 2000万,但那是在我选择malloc / free' sieve&#39之前;数组(即,我的筛选数组是维度nx 1的大数组,每个元素由1或0组成)。

该程序需要大约35秒才能完成前3亿个整数(16,252,325个素数),所以没关系,但没什么了不起的。正如我所提到的,目标是能够产生低于1万亿的质数,所以还有很长的路要走......

如果相关,这是我的机器规格(如果在这台机器上目标恰好是不合理的):2.40ghz i5,4GB RAM,64位Windows 7。

方法概述,对于那些不熟悉的人:我们使用Sienda of Sundaram方法。没有进入证明,我们首先消除一个整数以下的所有奇数非素数" n"使用筛选函数:[2 *(i + j + 2 * i * j)+1 | i< - [1..n / 2],j< - [i..an优化上限]]。然后我们交掉偶数(当然不包括两个)。这让我们留下了素数。

为什么prime函数返回(指向包含数组的指针)n下面的完整素数集?那么,目标是能够识别(i)n以下的素数以及(ii)列出n以下的素数。这也是为什么我选择将n以下的素数计数作为参数传递的原因。

这里不那么令人兴奋的主要'功能:

int main() {
    long ceiling = 300*1000*1000;
    long *numPrimes;
    long *primes;

    primes = primesToSS(ceiling+1, numPrimes);
    printf("\n\nThere are %d primes below %d.\n\n",*numPrimes,ceiling);

    free(primes);
    return 0;
}

这就是肉:

//n represents the ceiling, i.e., the integer below which we will generate primes
//cnt* is a pointer which will point the number of primes below n

long* primesToSS( long n, long* cnt ) {

    //initialize sieve by setting all elements equal to 1 (except for 0 and 1)
    long *sieve = malloc(n*sizeof(long));
    initArray(sieve, n, 1);
    sieve[0] = 0; sieve[1] = 0;

    //eliminate all odd composite numbers
    for (int i = 1; i <= n/2; ++i)
        for (int j = i; j <= (n-2*i)/(2*(2*i+1)); ++j)
            sieve[ 2*(i+j+2*i*j)+1 ] = 0;

    //eliminate all even numbers greater than two 
    //and count total number of primes below n
    long numPrimes = 1;
    for (int i = 3; i < n; ++i) {
        if (i % 2 == 0) sieve[i] = 0;
        numPrimes += sieve[i];
    }
    *cnt = numPrimes;

    //create array of primes
    long *primes = malloc(numPrimes*sizeof(int));
    long counter = 0;
    for (int i = 0; i < n; ++i) {
        if (sieve[i] == 1) {
            primes[counter] = i;
            counter++; } 
    }
    free(sieve);
    return primes;
}

void initArray( int* arr, int len, int n ) {
    for( int i = 0; i < len; ++i) arr[i] = n; }

3 个答案:

答案 0 :(得分:5)

让我们做一些观察:

  • 正如人们在评论和答案中所提到的,你只需要1位来存储数字是否是素数。因此,您可以将8个数字的数据打包为1个字节。实现自己的bitset数据结构,使代码更清晰。
  • 在评论中也提到,因为大于2的所有素数都是奇数,所以不需要存储偶数的数据。这允许您将16个数字打包成1个字节。
  • 按照wheel factorization的想法,你可以进一步将24个数字打包成1个字节,如果你只存储数字的位数为1或5模数6.更高的模数将节省更多的空间(收益递减) ),但访问bitset数据结构的逻辑可能会降低算法速度。
  • 对于使用1万亿(10 12 )数字的程序,您可以只收集少于100万的素数列表(10 6 )。更大的数字是不必要的,因为列表中已经涵盖了化合物数量小于1万亿的其他因素。
  • 以16个数字/字节打包数字(你应该做的最基本的设置),你需要~58 GiB的RAM。但是,没有必要为整个范围分配。只需分配几亿到几十亿的较小范围(尝试改变最高性能的数量),最多可以转化为几百MiB,然后使用不到100万的素数列表筛选范围。

    此步骤可以并行完成 - 您只需要共享少于100万的素数列表。

    顺便说一句,这种技术称为分段筛。

答案 1 :(得分:3)

long *numPrimes;
...
primes = primesToSS(ceiling+1, numPrimes);
printf("\n\nThere are %d primes below %d.\n\n",*numPrimes,ceiling);

正如Blastfurnace所说,你正在解除引用(并存储通过)一个未初始化的指针。这应该是

long numPrimes;
...
primes = primesToSS(ceiling+1, &numPrimes);
printf("\n\nThere are %d primes below %d.\n\n", numPrimes, ceiling);

可能还有其他错误......特别是没有检查malloc失败。哦,这是一个:

long *sieve;
initArray(sieve, n, 1);

...

void initArray( int* arr, int len, int n ) {
    for( int i = 0; i < len; ++i) arr[i] = n; }

intlong是不同的类型。在这里,你再次这样做:

long *primes = malloc(numPrimes*sizeof(int));

最佳做法是使用sizeof元素,而不是类型,这可以避免这种情况:

long *primes = malloc(numPrimes * sizeof *primes);

检查代码是否存在其他明显错误。

样式注释:将}放在一行的末尾是不好的做法,并在以后添加另一行时请求错误。

答案 2 :(得分:2)

一些内存注意事项:对于前1万亿个素数,如果用一个比特代表每个数字,则需要1万亿/ 8个字节。那是125000000000 = 119209 MB = 116 GB的RAM。

虽然您可以要求您的操作系统创建一个200GB的SWAP分区,但性能可能会很差。如果您想保持相同的算法,那么您需要查看memory mapped files

那仍然会很慢,但至少你可以分配一个足够大的数组。

接下来的步骤将是查看不同的素数算法,或者您需要研究内存压缩算法。在您的情况下,sparse matrices将无效,因为您的起始矩阵全部为1,并且您最终删除了几乎所有元素。