Skip to main content
Added a worked example, showing a solution based on boolean arrays, after testing BitSet and finding that boolean array was quicker.
Source Link
Mark Bluemel
  • 1.6k
  • 1
  • 5
  • 10

Update: I've tried, and the BitSet is fairly fast, but a simple boolean array seems better from my timings.

I note that some of the versions posted have some subtle bugs (and claim 169 and 301 as prime), but I'm fairly confident my solution is accurate.

I've avoided Wrapper classes, to avoid the boxing/unboxing costs, and used a boolean to mark a candidate number as known to be non-prime.

I thought that the numbering fell more comfortably without subtracting some offset, so made my array one entry larger than absolutely necessary.

import java.util.Arrays;

public class Sieve {

  private static final int NANOS_PER_SECOND = 1000_000_000;

  /*
   * [0] : ignored - allows us to do 1-based indexing cleanly
   * [n] : set if that integer is now known to be non-prime
   */
  private static boolean[] knownNonPrimes;

  private static int max;
  private static int maxFactor;

  public static void main(String[] args) {
    max = Integer.getInteger("Max", 10000);
    maxFactor = (int) Math.ceil(Math.sqrt(max)); // only need to check to root(max)
    knownNonPrimes = new boolean[max + 1]; // initially all false
    knownNonPrimes[0] = true; // zero is special
    markMultiples(2); // Special-case, to save checking other even numbers

    long startTime = System.nanoTime();
    int[] foundPrimes = findPrimes();
    long endTime = System.nanoTime();

    System.out.format("Time taken = %f seconds%n", 
      (double) (endTime - startTime) / NANOS_PER_SECOND);
    System.out.println(foundPrimes.length);
    System.out.println(Arrays.toString(foundPrimes));
  }

  private static int[] findPrimes() {
    for (int factor = 3; factor <= maxFactor; factor += 2) {
      // If we already know this is non-prime, 
      // then its multiples have been marked already
      if (!knownNonPrimes[factor]) {
        markMultiples(factor);
      }
    }

    int resultSize = 0;
    for (boolean knownNonPrime : knownNonPrimes) {
      if (!knownNonPrime) {
        resultSize += 1;
      }
    }
    int[] result = new int[resultSize];
    int resultIndex = 0;
    for (int primeCandidate = 1; primeCandidate <= max; primeCandidate++) {
      if (!knownNonPrimes[primeCandidate]) {
        result[resultIndex++] = primeCandidate;
      }
    }
    return result;
  }

  private static void markMultiples(int factor) {
    for (int multiple = factor * 2; multiple <= max; multiple += factor) {
      knownNonPrimes[multiple] = true;
    }
  }

}

Update: I've tried, and the BitSet is fairly fast, but a simple boolean array seems better from my timings.

I note that some of the versions posted have some subtle bugs (and claim 169 and 301 as prime), but I'm fairly confident my solution is accurate.

I've avoided Wrapper classes, to avoid the boxing/unboxing costs, and used a boolean to mark a candidate number as known to be non-prime.

I thought that the numbering fell more comfortably without subtracting some offset, so made my array one entry larger than absolutely necessary.

import java.util.Arrays;

public class Sieve {

  private static final int NANOS_PER_SECOND = 1000_000_000;

  /*
   * [0] : ignored - allows us to do 1-based indexing cleanly
   * [n] : set if that integer is now known to be non-prime
   */
  private static boolean[] knownNonPrimes;

  private static int max;
  private static int maxFactor;

  public static void main(String[] args) {
    max = Integer.getInteger("Max", 10000);
    maxFactor = (int) Math.ceil(Math.sqrt(max)); // only need to check to root(max)
    knownNonPrimes = new boolean[max + 1]; // initially all false
    knownNonPrimes[0] = true; // zero is special
    markMultiples(2); // Special-case, to save checking other even numbers

    long startTime = System.nanoTime();
    int[] foundPrimes = findPrimes();
    long endTime = System.nanoTime();

    System.out.format("Time taken = %f seconds%n", 
      (double) (endTime - startTime) / NANOS_PER_SECOND);
    System.out.println(foundPrimes.length);
    System.out.println(Arrays.toString(foundPrimes));
  }

  private static int[] findPrimes() {
    for (int factor = 3; factor <= maxFactor; factor += 2) {
      // If we already know this is non-prime, 
      // then its multiples have been marked already
      if (!knownNonPrimes[factor]) {
        markMultiples(factor);
      }
    }

    int resultSize = 0;
    for (boolean knownNonPrime : knownNonPrimes) {
      if (!knownNonPrime) {
        resultSize += 1;
      }
    }
    int[] result = new int[resultSize];
    int resultIndex = 0;
    for (int primeCandidate = 1; primeCandidate <= max; primeCandidate++) {
      if (!knownNonPrimes[primeCandidate]) {
        result[resultIndex++] = primeCandidate;
      }
    }
    return result;
  }

  private static void markMultiples(int factor) {
    for (int multiple = factor * 2; multiple <= max; multiple += factor) {
      knownNonPrimes[multiple] = true;
    }
  }

}
Source Link
Mark Bluemel
  • 1.6k
  • 1
  • 5
  • 10

Anything involving Integer rather than int will have boxing and unboxing costs.

Anything involving remove() will have structure maintenance costs.

I'm not able to do a worked example right now, but I'd be surprised if anything beats an solution using https://docs.oracle.com/javase/7/docs/api/java/util/BitSet.html