Segmented Sieve

Hello Everyone,

Given a number n, print all primes smaller than n. For example, if the given number is 10, output 2, 3, 5, 7.
A Naive approach is to run a loop from 0 to n-1 and check each number for primeness. A Better Approach is to use Simple Sieve of Eratosthenes.

// This functions finds all primes smaller than 'limit'

// using simple sieve of eratosthenes.

void simpleSieve( int limit)

{

// Create a boolean array "mark[0..limit-1]" and

// initialize all entries of it as true. A value

// in mark[p] will finally be false if 'p' is Not

// a prime, else true.

bool mark[limit];

for ( int i = 0; i<limit; i++) {

mark[i] = true ;

}

// One by one traverse all numbers so that their

// multiples can be marked as composite.

for ( int p=2; p*p<limit; p++)

{

// If p is not changed, then it is a prime

if (mark[p] == true )

{

// Update all multiples of p

for ( int i=p*p; i<limit; i+=p)

mark[i] = false ;

}

}

// Print all prime numbers and store them in prime

for ( int p=2; p<limit; p++)

if (mark[p] == true )

cout << p << " " ;

}

Problems with Simple Sieve:
The Sieve of Eratosthenes looks good, but consider the situation when n is large, the Simple Sieve faces the following issues.

  • An array of size Θ(n) may not fit in memory
  • The simple Sieve is not cached friendly even for slightly bigger n. The algorithm traverses the array without locality of reference

Segmented Sieve
The idea of a segmented sieve is to divide the range [0…n-1] in different segments and compute primes in all segments one by one. This algorithm first uses Simple Sieve to find primes smaller than or equal to √(n). Below are steps used in Segmented Sieve.

  1. Use Simple Sieve to find all primes up to the square root of ‘n’ and store these primes in an array “prime[]”. Store the found primes in an array ‘prime[]’.
  2. We need all primes in the range [0…n-1]. We divide this range into different segments such that the size of every segment is at-most √n
  3. Do following for every segment [low…high]
  • Create an array mark[high-low+1]. Here we need only O(x) space where x is a number of elements in a given range.
  • Iterate through all primes found in step 1. For every prime, mark its multiples in the given range [low…high].

In Simple Sieve, we needed O(n) space which may not be feasible for large n. Here we need O(√n) space and we process smaller ranges at a time (locality of reference)

Below is the implementation of the above idea.

// C# program to print print

// all primes smaller than

// n using segmented sieve

using System;

using System.Collections;

class GFG

{

// This methid finds all primes

// smaller than 'limit' using simple

// sieve of eratosthenes. It also stores

// found primes in vector prime[]

static void simpleSieve( int limit,

ArrayList prime)

{

// Create a boolean array "mark[0..n-1]"

// and initialize all entries of it as

// true. A value in mark[p] will finally be

// false if 'p' is Not a prime, else true.

bool [] mark = new bool [limit + 1];

for ( int i = 0; i < mark.Length; i++)

mark[i] = true ;

for ( int p = 2; p * p < limit; p++)

{

// If p is not changed, then it is a prime

if (mark[p] == true )

{

// Update all multiples of p

for ( int i = p * p; i < limit; i += p)

mark[i] = false ;

}

}

// Print all prime numbers and store them in prime

for ( int p = 2; p < limit; p++)

{

if (mark[p] == true )

{

prime.Add(p);

Console.Write(p + " " );

}

}

}

// Prints all prime numbers smaller than 'n'

static void segmentedSieve( int n)

{

// Compute all primes smaller than or equal

// to square root of n using simple sieve

int limit = ( int ) (Math.Floor(Math.Sqrt(n)) + 1);

ArrayList prime = new ArrayList();

simpleSieve(limit, prime);

// Divide the range [0..n-1] in

// different segments We have chosen

// segment size as sqrt(n).

int low = limit;

int high = 2*limit;

// While all segments of range

// [0..n-1] are not processed,

// process one segment at a time

while (low < n)

{

if (high >= n)

high = n;

// To mark primes in current range.

// A value in mark[i] will finally

// be false if 'i-low' is Not a prime,

// else true.

bool [] mark = new bool [limit + 1];

for ( int i = 0; i < mark.Length; i++)

mark[i] = true ;

// Use the found primes by

// simpleSieve() to find

// primes in current range

for ( int i = 0; i < prime.Count; i++)

{

// Find the minimum number in

// [low..high] that is a multiple

// of prime.get(i) (divisible by

// prime.get(i)) For example,

// if low is 31 and prime.get(i)

// is 3, we start with 33.

int loLim = (( int )Math.Floor(( double )(low /

( int )prime[i])) * ( int )prime[i]);

if (loLim < low)

loLim += ( int )prime[i];

/* Mark multiples of prime.get(i) in [low..high]:

We are marking j - low for j, i.e. each number

in range [low, high] is mapped to [0, high-low]

so if range is [50, 100] marking 50 corresponds

to marking 0, marking 51 corresponds to 1 and

so on. In this way we need to allocate space only

for range */

for ( int j = loLim; j < high; j += ( int )prime[i])

mark[j-low] = false ;

}

// Numbers which are not marked as false are prime

for ( int i = low; i < high; i++)

if (mark[i - low] == true )

Console.Write(i + " " );

// Update low and high for next segment

low = low + limit;

high = high + limit;

}

}

// Driver code

static void Main()

{

int n = 100;

Console.WriteLine( "Primes smaller than " + n + ":" );

segmentedSieve(n);

}

}

Output:

Primes smaller than 100: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97