Quantcast
Channel: roman10 » Data Structure & Algorithms
Viewing all articles
Browse latest Browse all 15

Primality Testing — Part 2. Pre-computation with Sieve of Eratosthenes

$
0
0

Previous post covers two naive methods for primality testing. This post introduces an algorithm to generate prime numbers called Sieve of Eratosthenes. With this algorithm, an optimization can be applied to speed up the primality testing.

Sieve of Eratosthenes

Suppose we want to generate prime numbers up to N. The algorithm consists of two loops, and it can be described as below,

  • Create a mask for all integers up to N.
  • In the first loop, let x be 2, mark 2x, 3x, 4x for all n such that nx < N.
  • finding an integer bigger than x and not marked, let it be the new x, and repeat the step above
  • the first loop terminates when we cannot find an integer bigger than x and not marked
  • In the second loop, starting from 2, collects the numbers that are not marked, which are the prime numbers up to N.

Below is an implementation of the Sieve of Eratosthenes algorithm,

/**

generate prime numbers

**/

#include <stdio.h>

#include <stdlib.h>

 

#define MAXV 10000000

#define ININ 1000

 

unsigned char bits[MAXV/8];

 

void setbit(int pos) {

    bits[pos/8] |= (0x01 << (pos%8));

}

 

int testbit(int pos) {

    return (bits[pos/8] & (0x01 << (pos%8)));

}

 

void genprime(int n, int **pr, int *npr) {

   int i, j, k;

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

       if (!testbit(i)) {

           for (j = 2; i*j < n; ++j) {

               setbit(i*j);

           }

       }

   } 

   k = ININ;

   *pr = malloc(sizeof(int) * k);

   *npr = 0;

   for (i = 2, j = 0; i < n; ++i) {

       if (!testbit(i)) {

           (*pr)[j++] = i;

           (*npr)++;

           if (j == k) {

               k += ININ;

               *pr = realloc(*pr, sizeof(int)*k);

           }

       }

   }

}

 

int main(int argc, char **argv) {

    int n = atoi(argv[1]);

    int *pr;

    int npr;

    int i;

    struct timeval stTime, edTime;

    if (n > MAXV) {

        printf("the max n is %d\n", MAXV);

        return 0;

    }

    gettimeofday(&stTime, NULL);

    genprime(n, &pr, &npr);

    printf("No. of prime numbers: %d\n", npr);

    for (i = 0; i < npr; ++i) {

        printf("%d  ", pr[i]);

    }

    printf("\n");

    gettimeofday(&edTime, NULL);

    printf("time: %u:%u\n", (unsigned int)(edTime.tv_sec - stTime.tv_sec), (unsigned int)(edTime.tv_usec - stTime.tv_usec)); 

    if (pr != NULL) {

        free(pr);

    }

    return 0;

}

Note that the code above use a single bit to indicate if an integer is marked or not.

Save the code to prim2.c, and then compile the code with the command below,

gcc -o prim2 prim2.c

Below is a screenshot of some simple tests,


Figure 1. Simple Tests of Implementation for Sieve of Eratosthenes

Primality Testing with Sieve of Eratosthenes for Precomputation

One way to speed up primality testing is to precompute a list of prime numbers up to N. Then we first check if an input integer n is divisible by the prime numbers generated. If the number is not divisible by any of those prime numbers, we then proceed to the test mentioned in the naive approach post.

Below is an implementation of this precomputation approach,

/**

a primality test with precomputation

**/

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

 

#define MAXV 10000000

#define ININ 1000

 

unsigned char bits[MAXV/8];

 

void setbit(int pos) {

    bits[pos/8] |= (0x01 << (pos%8));

}

 

int testbit(int pos) {

    return (bits[pos/8] & (0x01 << (pos%8)));

}

 

void genprime(int n, int **pr, int *npr) {

   int i, j, k;

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

       if (!testbit(i)) {

           for (j = 2; i*j < n; ++j) {

               setbit(i*j);

           }

       }

   } 

   k = ININ;

   *pr = malloc(sizeof(int) * k);

   *npr = 0;

   for (i = 2, j = 0; i < n; ++i) {

       if (!testbit(i)) {

           (*pr)[j++] = i;

           (*npr)++;

           if (j == k) {

               k += ININ;

               *pr = realloc(*pr, sizeof(int)*k);

           }

       }

   }

}

 

int prime(int n, int *pr, int npr, int x) {

    int i;

    int j;

    

    for (i = 0; i < npr; ++i) {

        if (n == pr[i]) {

            return 1;

        }

        if (n%pr[i] == 0) {

            printf("%d is divisble by %d\n", n, pr[i]);

            return 0;

        }

    }

    if (n == 2) {

        return 1;

    }

    if (!(n&1)) {

        printf("%d is divisible by 2\n", n);

        return 0;

    }

    j = sqrt(n);

    //to be safe, run to j+1

    i = (x+1) > 3 ? (x+1):3;

    if (!(i&1)) {

        ++i;   //make sure i starts as odd number

    }

    for (; i <= j + 1; i+=2) {

        if (n%i == 0) {

            printf("%d is divisible by %d\n", n, i);

            return 0;

        }

    }

    return 1;

}

 

int main(int argc, char **argv) {

    int m, n;

    int *pr;

    int npr;

    int i;

    struct timeval stTime, stTime2, edTime;

    if (argc < 3) {

        printf("./prim3 <primality test number> <prime number generation limit>\n");

        return 0;

    }

    m = atoi(argv[1]);

    n = atoi(argv[2]);

    if (n > MAXV) {

        printf("the max n is %d\n", MAXV);

        return 0;

    }

    gettimeofday(&stTime, NULL);

    genprime(n, &pr, &npr);

    gettimeofday(&stTime2, NULL);

    //printf("No. of prime numbers: %d\n", npr);

/*    for (i = 0; i < npr; ++i) {

        printf("%d\n", pr[i]);

    }*/

    if (prime(m, pr, npr, n)) {

        printf("%d is prime number\n", m);

    } else {

        printf("%d is not prime number\n", m);

    }

    gettimeofday(&edTime, NULL);

    printf("total time: %u:%u\n", (unsigned int)(edTime.tv_sec - stTime.tv_sec), (unsigned int)(edTime.tv_usec - stTime.tv_usec)); 

    printf("test prime time: %u:%u\n", (unsigned int)(edTime.tv_sec - stTime2.tv_sec), (unsigned int)(edTime.tv_usec - stTime2.tv_usec)); 

    if (pr != NULL) {

        free(pr);

    }

    return 0;

}

Save the source code to prim3.c, and then compile the code with the command below,

gcc -o prim3 prim3.c –lm

Below are some simple tests for the compiled program,

roman10@ra-ubuntu-1:~/Desktop/integer/prim$ ./prim3 2 10

2 is prime number

total time: 0:144

test prime time: 0:48

 

roman10@ra-ubuntu-1:~/Desktop/integer/prim$ ./prim3 3 10

3 is prime number

total time: 0:60

test prime time: 0:22

 

roman10@ra-ubuntu-1:~/Desktop/integer/prim$ ./prim3 2147483647 1000

2147483647 is prime number

total time: 0:469

test prime time: 0:302

 

roman10@ra-ubuntu-1:~/Desktop/integer/prim$ ./prim3 2147483643 1000

2147483643 is divisble by 3

2147483643 is not prime number

total time: 0:223

test prime time: 0:57

 

roman10@ra-ubuntu-1:~/Desktop/integer/prim$ ./prim3 2147483641 1000

2147483641 is divisible by 2699

2147483641 is not prime number

total time: 0:250

test prime time: 0:80

References:
1. Wikipedia: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes


Viewing all articles
Browse latest Browse all 15

Trending Articles