2

Well, there are lots of such questions available in SO as well as other forums. However, none of these helped.

I wrote a program in "C" to find number of primes within a range. The range i in long int. I am using Sieve of Eratosthenes" algorithm. I am using an array of long ints to store all the numbers from 1 till the limit. I could not think of a better approach to achieve without using an array. The code works fine, till 10000000. But after that, it runs out of memory and exits. Below is my code.

#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;
}

I am stuck and can't think of a better way to achieve this. Any help will be hugely appreciated. Thanks.

Edit:

My aim is to generate and print all prime numbers below the range. As printing consumed a lot of time, I thought of getting the first step right.

dr_dev
  • 492
  • 3
  • 17
  • 1
    For starters, you can use the array as a bitset (recall you actually need it for a true/false, and not for 2^32 values). It will give you X32 more elements you can store by allocating a single bit for number. (Still not enough for 2^64 values thouhg). – amit Jun 07 '15 at 09:37
  • Thanks for the reply amit. I need to print all the prime numbers below the range actually. But as printing takes a lot of time, I thought first let me count the total prime numbers. If I get this right, the next step will be easier. Any other leads? – dr_dev Jun 07 '15 at 09:53
  • Even with a bit field you can still get the numbers out. – Wai Ha Lee Jun 07 '15 at 10:37
  • you said in the question that you want to generate primes in range. while your code so far shows that you are generating primes from zero to n. right? – hasan Jun 07 '15 at 10:51
  • note that for each iteration of Sieve of Eratosthenes algorithm you start eliminating from n*n and not n*2. example: for eliminating multiples of 2 you start from 2*2=4, 6, 8 ..., for 3 you start from 3*3=9,12,15...., for 5 you start from 5*5=25,30,35,40... – hasan Jun 07 '15 at 11:01
  • so you must modify your code as: for(k=cross*cross; k<=N; k+=cross) – hasan Jun 07 '15 at 11:03
  • which also will affect the cross initialisation here: for(i=0; cross<=N/2; i++) to for(i=0; cross*cross<=N; i++) – hasan Jun 07 '15 at 11:10
  • @ hasan83, yeah good point. Thanks. – dr_dev Jun 07 '15 at 11:14
  • One can compress the bitfield considerably when storing/calculating only candidates for n % 30 in {1, 7, 11, 13, 17, 19, 23, 29 }, n > 30. That's 8 out of 30 integers. – Aki Suihkonen Jun 07 '15 at 12:18

4 Answers4

2

There are other algorithm that does not require you to generate prime number up to N to count number of prime below N. The easiest algorithm to implement is Legendre Prime Counting. The algorithm requires you to generate only sqrt(N) prime to determine the number of prime below N.

The idea behind the algorithm is that

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.

That's mean phi(n, sqrt(n)) = number of prime between sqrt(n) to n. For how to calculate the phi, you can go to the following link (Feasible implementation of a Prime Counting Function)

The reason why it is more efficient is because it is easiest to compute phi(n, m) than to compute pi(n). Let say that I want to compute phi(100, 3) means that how many number below or equal to 100 that does not divisible by 2 and 3. You can do as following. phi(100, 3) = 100 - 100/2 - 100/3 + 100/6.

Community
  • 1
  • 1
invisal
  • 11,075
  • 4
  • 33
  • 54
  • Thanks invisal for this approach. In fact, I need to generate all the prime numbers below N. Should have mentioned this in the question. WIll edit it. – dr_dev Jun 07 '15 at 10:41
1

Your code uses about 32 times as much memory as it needs. Note that since you initialized list[i] = i the assignment cross = list[j] can be replaced with cross = j, making it possible to replace list with a bit vector.

However, this is not enough to bring the range to 264, because your implementation would require 261 bytes (2 exbibytes) of memory, so you need to optimize some more.

The next thing to notice is that you do not need to go up to N/2 when "crossing" the numbers: √N is sufficient (you should be able to prove this by thinking about the result of dividing a composite number by its divisors above √N). This brings memory requirements within your reach, because your "crossing" primes would fit in about 4 GB of memory.

Once you have an array of crossing primes, you can build a partial sieve for any range without keeping in memory all ranges that precede it. This is called the Segmented sieve. You can find details on it, along with a simple implementation, on the page of primesieve generator. Another advantage of this approach is that you can parallelize it, bringing the time down even further.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

You can tweak the algorithm a bit to calculate the prime numbers in chunks.

Load a part of the array (as much as fits the memory), and in addition hold a list of all known prime numbers.
Whenever you load a chunk, first go through the already known prime numbers, and similar to the regular sieve, set all non primes as such.
Then, go over the array again, mark whatever you can, and add to the list the new prime numbers found.

When done, you'll have a list containing all your prime numbers.

amit
  • 175,853
  • 27
  • 231
  • 333
0

I could see that the approach you are using is the basic implementation of Eratosthenes, that first stick out all the 2's multiple and then 3's multiple and so on.

But I have a better solution to the question. Actually, there is question on spoj PRINT. Please go through it and do check the constraints it follows. Below is my code snippet for this problem:

#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;
}

I hope you got the point:

And, as you mentioned that your program is working fine up-to 10^7 but above it fails, it must be because you must be running out of the memory.

NOTE: I'm sharing my code only for knowledge purpose. Please, don't copy and paste it, until you get the point.

surajs1n
  • 1,493
  • 6
  • 23
  • 34