After optimizing my function which I plan to use in Project Euler 95, I've run into a wall concerning further improvements. This code is destined for my library of project Euler functions, so while readability is nice, it definitely takes a backseat to performance.
I have verified that the output is correct by non-rigorous means (Pasting 10+ non consecutive random numbers for many ranges of output into Wolfram Alpha, no discrepancies), but it remains possible, (but unlikely) that there may be errors. Current run time is between 470-490ms for input size 107. Algorithmic or stylistic review is greatly appreciated.
public class sumDivisors2 {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
int[] divisors = divisors((int)Math.pow(10, 7));
}
/**
*
* @param limit
* @returns an array of integers, such that n[i] = the sum of divisors of i + 1
* That is the 0th index is 1, 1st index is 2, etc.
* All divisors of i, including i and 1, are included in the sum.
* (The second index of the array will contain the value three)
*/
public static int[] divisors(int limit){
/**
* The general idea behind the algorithm is to compute the sum of divisors for
* each successive prime, raised to a given power, and multiply them out over the
* entire sieve.
*/
//Setup
long start = System.currentTimeMillis();
int[] primes = generatePrimes(limit);
long pStop = System.currentTimeMillis();
int[] data = new int[limit];
long fStart = System.currentTimeMillis();
fill(data, 1);
long fStop = System.currentTimeMillis();
int index = 0;
//Use bitshifting to quickly multiply out all values of two
int max_pow_two = (int) Math.floor(Math.log(limit) / Math.log(2));
for (int i = 1; i <= max_pow_two; i++) {
for (int j = 1 << i; j <= limit; j = j + (1 << i + 1)) {
data[j - 1] = (data[j - 1] << i + 1) - 1;
}
}
for (index = 1; index < primes.length; index++) {
int maxPow = (int) Math.floor(Math.log(limit) / Math.log(primes[index]));
if (maxPow == 1) {
//Break when prime is greater than sqrt(n)
break;
}
int skipConstant = primes[index] - 1;
int[] powers = new int[maxPow];
for (int j = powers.length - 1; j > -1; j--) {
int skipCount = 0;
powers[j] = ((int) Math.pow(primes[index], j + 1));
if (j < powers.length - 1) {
for (int k = powers[j]; k <= limit; k += powers[j]) {
data[k - 1] *= (powers[j] * primes[index] - 1) / (primes[index] - 1);
skipCount++;
if (skipCount == skipConstant) { //Skips every nth multiple to multiplications //that were performed by an earlier iteration
k += powers[j];
skipCount = 0;
}
}
} else {
for (int k = powers[j]; k <= limit; k += powers[j]) {
data[k - 1] *= (powers[j] * primes[index] - 1) / (primes[index] - 1);
}
}
}
}
//Iterate over the rest of the primes, which will never have more than a first power
for (int i = index; i < primes.length; i++) {
for (int j = primes[i]; j <= limit; j += primes[i]) {
data[j - 1] *= primes[i] + 1;
}
}
long stop = System.currentTimeMillis();
// for (int i = 0; i < data.length; i++) {
// System.out.printf("The sum of the factors of %d is %d\n", i + 1, data[i]);
// }
System.out.println("Computation time: " + (stop - start) + " ms");
System.out.println("\tPrime generation time: " + (pStop - start) + " ms");
System.out.println("\tArray fill time: " + (fStop - fStart) + " ms");
System.out.println("Verification for 10 ^ 7: 24902280 = " + data[limit - 1]);
return data;
}
/**
* Simple implementation of the Sieve of Eratosthenes
* @param max the integer to generate primes up to
* @returns an array consisting of all the primes 2 <= n <= max
*/
public static int[] generatePrimes(int max) {
boolean[] isComposite = new boolean[max + 1];
int numPrimes = 0;
for (int i = 2; i * i <= max; i++) {
if (!isComposite[i]) {
for (int j = i; i * j <= max; j++) {
isComposite[i * j] = true;
}
}
}
for (int i = 2; i <= max; i++) {
if (!isComposite[i]) {
numPrimes++;
}
}
//System.out.println(numPrimes);
int[] primes = new int[numPrimes];
int index = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite[i]) {
primes[index++] = i;
}
}
return primes;
}
/**
* Quickly fill an int array with a given value
* @param array the int array
* @param value the value
*/
private static void fill(int[] array, int value) {
int len = array.length;
if (len > 0) {
array[0] = value;
}
for (int i = 1; i < len; i += i) {
System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i);
}
}
}