Count Primes with a Thread Pool

Problem

Print how many prime numbers are in the range 1 to n. Split the range 2..n into 4 equal parts, count the primes of each part with trial division in a worker task of a thread pool, and add the four counts. The answer must not depend on the number of threads.

Constraints

  • 0 <= n <= 200000

Examples

Input

10

Output

4

Solutions

Best approachTime O(n sqrt n / 4)Space O(1)

The parts do not share data, so the tasks need no locks. The pool reuses its four threads. Python threads do not speed up pure computing because of the GIL, but the structure is the same as in Java.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Count primes.
 * Input   : n.
 * Output  : The count.
 * Approach: Thread pool.
 * Time    : O(n sqrt n / 4)
 * Space   : O(1)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Count the primes from low to high (both included) with trial division.
        private static long countPrimes(long low, long high) {
            long count = 0;
            for (long x = Math.max(low, 2); x <= high; x++) {
                boolean prime = true;
                for (long d = 2; d * d <= x; d++) {
                    if (x % d == 0) {
                        prime = false;
                        break;
                    }
                }
                if (prime) {
                    count++;
                }
            }
            return count;
        }

    // This method solves the task. It receives the input and returns the answer.
    static long solve(long n) {
        // Step 1: A pool of 4 worker threads. Each part of the range becomes one task.
        int parts = 4;
        java.util.concurrent.ExecutorService pool = java.util.concurrent.Executors.newFixedThreadPool(parts);
        try {
            java.util.List<java.util.concurrent.Future<Long>> futures = new java.util.ArrayList<java.util.concurrent.Future<Long>>();
            for (int p = 0; p < parts; p++) {
                // Step 2: Part p covers the numbers from low to high. Together the four parts cover 2..n once.
                final long low = 2 + (n - 1) * p / parts;
                final long high = 2 + (n - 1) * (p + 1) / parts - 1;
                futures.add(pool.submit(() -> countPrimes(low, high)));
            }
            // Step 3: get() waits for each task. We add the counts of the four parts.
            long total = 0;
            for (java.util.concurrent.Future<Long> future : futures) {
                total += future.get();
            }
            return total;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            pool.shutdown();
        }
    }

    public static void main(String[] args) {
        long n = new Scanner(System.in).nextLong();
        System.out.println(solve(n));
    }
}
Hints (2)
  1. Why do the tasks need no locks?
  2. What does get() do when a task is not finished?