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
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.
/*
* 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)
- Why do the tasks need no locks?
- What does get() do when a task is not finished?