使用C计算给定long int范围内的素数

时间:2015-06-07 09:34:32

标签: c algorithm

嗯,SO和其他论坛都有很多这样的问题。但是,这些都没有帮助。

我在" C"查找范围内的素数。我在long int中的范围。我正在使用Sieve of Eratosthenes"算法。我正在使用一长串整数来存储从1到极限的所有数字。如果不使用数组,我想不出更好的方法。代码工作正常,直到10000000.但在那之后,它耗尽内存并退出。以下是我的代码。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef unsigned long uint_32;

int main() {

    uint_32 i, N, *list, cross=0, j=4, k, primes_cnt = 0;
    clock_t start, end;
    double exec_time;

    system("cls");
    printf("Enter N\n");
    scanf("%lu", &N);
    list = (uint_32 *) malloc( (N+1) * sizeof(uint_32));
    start = clock();
    for(i=0; i<=N+1; i++) {
        list[i] = i;
    }

    for(i=0; cross<=N/2; i++) { 
        if(i == 0)
            cross = 2;
        else if(i == 1)
            cross = 3;
        else {

            for(j=cross+1; j<=N; j++) {
                if(list[j] != 0){

                    cross = list[j];
                    break;
                }
            }
        }

        for(k=cross*2; k<=N; k+=cross) {
            if(k <= N)
                list[k] = 0;
        }
    }

    for(i=2; i<=N; i++) {

        if(list[i] == 0)
            continue;
        else
            primes_cnt++;
    }

    printf("%lu", primes_cnt);
    end = clock();
    exec_time = (double) (end-start);
    printf("\n%f", exec_time);
    return 0;
}

我陷入困境,无法想出更好的方法来实现这一目标。任何帮助将非常感激。感谢。

修改

我的目标是生成并打印低于该范围的所有素数。由于打印耗费了大量时间,我想到了第一步。

4 个答案:

答案 0 :(得分:2)

还有其他算法不需要您生成高达N的素数来计算低于N的素数。最简单的算法是勒让德素数计数。该算法要求您仅生成sqrt(N)素数以确定N以下的素数。

算法背后的想法是

pi(n) = phi(n, sqrt(n)) + pi(sqrt(n)) - 1

where
   pi(n) = number of prime below N
   phi(n, m) = number of number below N that is not divisible by any prime below m.

平均值phi(n,sqrt(n))= sqrt(n)到n之间的素数。有关如何计算phi,您可以转到以下链接(Feasible implementation of a Prime Counting Function

它更有效的原因是因为计算phi(n,m)比计算pi(n)最容易。假设我想计算phi(100,3)表示低于或等于100的数字不能被2和3整除。您可以执行以下操作。 phi(100,3)= 100 - 100/2 - 100/3 + 100/6。

答案 1 :(得分:1)

您的代码使用的内存大约是其所需内存的32倍。请注意,自您初始化list[i] = i以来,作业cross = list[j]可以替换为cross = j,因此可以使用位向量替换list

然而,这还不足以使范围达到2 64 ,因为你的实现需要2个 61 字节(2 exbibytes)的内存,所以你需要优化一些。

接下来需要注意的是,当&#34;穿越&#34;时,您不需要前往N/2。数字:√N就足够了(你应该能够通过考虑将合数除以高于√N的除数的结果来证明这一点)。这会带来您可以达到的内存要求,因为您可以通过&#34;穿越&#34;素数将适合大约4 GB的内存。

一旦你有一个交叉素数阵列,你就可以为任何范围构建一个部分筛,而不会在它之前保留所有范围内的所有范围。这称为分段筛。您可以在primesieve生成器的页面上找到有关它的详细信息以及一个简单的实现。这种方法的另一个优点是你可以将它并行化,从而进一步缩短时间。

答案 2 :(得分:1)

您可以稍微调整算法以计算块中的素数。

加载数组的一部分(尽可能多地适合内存),另外还要保存所有已知素数的列表。
每当你加载一个块时,首先通过已知的素数,并且类似于常规筛,设置所有非素数。
然后,再次检查数组,标记所有可能的数据,并将列出的新素数添加到列表中。

完成后,您将拥有一个包含所有素数的列表。

答案 3 :(得分:0)

我可以看到你正在使用的方法是 Eratosthenes 的基本实现,它首先显示所有2个多重,然后是3个多重,依此类推。

但我有一个更好的解决方案。实际上,关于spoj PRINT存在疑问。请仔细阅读并检查后面的约束。以下是此问题的代码段:

#include<stdio.h>
#include<math.h>
#include<cstdlib>

int num[46500] = {0},prime[5000],prime_index = -1;

int main() {
    /* First, calculate the prime up-to the sqrt(N) (preferably greater than, but near to 
       sqrt(N) */   
    prime[++prime_index] = 2;    int i,j,k;

    for(i=3;    i<216;     i += 2) {
        if(num[i] == 0) {
            prime[++prime_index] = i;
            for(j = i*i, k = 2*i;    j<=46500;   j += k) {
                num[j] = 1;
            }
        }
    }

    for(;   i<=46500;   i+= 2) {
        if(num[i] == 0) {
            prime[++prime_index] = i;
        }
    }

    int t;            // Stands for number of test cases  

    scanf("%i",&t);
    while(t--) {

        bool arr[1000005] = {0};   int m,n,j,k;

        scanf("%i%i",&m,&n);

        if(m == 1)
            m++;

        if(m == 2 && m <= n) {
            printf("2\n");
        }

        int sqt = sqrt(n) + 1;

        for(i=0;    i<=prime_index; i++) {
            if(prime[i] > sqt) {
                sqt = i;
                break;
            }
        }

        for(;   m<=n && m <= prime[prime_index];   m++) {
            if(m&1 && num[m] == 0) {
                printf("%i\n",m);
            }
        }

        if(m%2 == 0) {
            m++;
        }

        for(i=1;    i<=sqt;     i++) {
            j = (m%prime[i]) ? (m + prime[i] - m%prime[i]) : (m);
            for(k=j;    k<=n;   k += prime[i]) {
                arr[k-m] = 1;
            }
        }

        for(i=0;    i<=n-m; i += 2) {
            if(!arr[i]) {
                printf("%i\n",m+i);
            }
        }
        printf("\n");
    }
    return 0;
}

我希望你明白这一点:

而且,正如你所提到的那样,你的程序在10 ^ 7之间工作正常,但是在它失败之后,它必定是因为你必须耗尽内存。

注意:我只是出于知识目的而共享我的代码。请不要复制并粘贴它,直到你明白为止。