Let pi(x)
denote the number of primes <= x
. For example, pi(100) = 25
. I would like to create a table which stores values of pi(x)
for all x <= L
. I figured the quickest way would be to use the sieve of Eratosthenes. First I mark all primes, and then I use dynamic programming, summing the count of primes and increasing each time a new prime appears. This is implemented in the Java code below:
public static int [] piTableSimple (int L)
{
int sqrtl = (int) Math.sqrt(L);
int [] piTable = new int [L + 1];
Arrays.fill(piTable, 1);
piTable[0] = 0;
piTable[1] = 0;
for (int i = 2 ; i <= sqrtl ; i++)
if (piTable[i] == 1)
for (int j = i * i ; j <= L ; j += i)
piTable[j] = 0;
for (int i = 1 ; i < piTable.length ; i++)
piTable[i] += piTable[i - 1];
return piTable;
}
There are 2 problems with this implementation:
It uses large amounts of memory, as the space complexity is
O(n)
Because Java arrays are "int"-indexed, the bound for
L
is2^31 - 1
I can "cheat" a little though. Because for even values of x
, pi(x) = pi(x - 1)
, enabling me to both reduce memory usage by a factor of 2, and increase the bound for L
by a factor of 2 (Lmax <= 2^32
).
This is implemented with a simple modification to the above code:
public static long [] piTableSmart (long L)
{
long sqrtl = (long) Math.sqrt(L);
long [] piTable = new long [(int) (L/2 + 1)];
Arrays.fill(piTable, 1);
piTable[0] = 0;
piTable[1] = 0;
piTable[2] = 1;
for (int i = 3 ; i <= sqrtl ; i += 2)
if (piTable[(i + 1) / 2] == 1)
{
long li = (long) i;
long inc = li * 2L;
for (long j = li * li ; j <= L ; j += inc)
piTable[(int) ((j + 1) / 2)] = 0;
}
piTable[2] = 2;
for (int i = 1 ; i < piTable.length ; i++)
piTable[i] += piTable[i - 1];
return piTable;
}
Note that the value of pi(2) = 1
is not directly represnted in this array. But this has simple workarounds and checks that solve it. This implementation comes with a small cost, that the actual value of pi(x)
is not accessed in a straight-forward way, but rather to access the value of pi(x)
, one has to use
piTable[(x + 1) / 2]
And this works for both even and odd values of x
of course. The latter completes creating a pi(x)
table for x <= L = 10^9
in 10s on my rather slowish laptop.
I would like to further reduce the space required and also increase the bound for L
for my purposes, without severly deteriorating performance (for example, the cost of slightly more arithmetic operations to access the value of pi(x)
as in the latter code barely deteriorates performance). Can it be done in an efficient and smart way?