Java 8: 1.8e8 2.4e8
Questa voce non è paragonabile a molte altre già presenti, ma ho voluto pubblicare la mia risposta poiché mi sono divertito a lavorarci su.
Le principali ottimizzazioni del mio approccio sono le seguenti:
- Ogni numero pari ha un fattore minimo di 2, quindi questi possono essere aggiunti gratuitamente dopo l'elaborazione di ogni numero dispari. Fondamentalmente, se hai fatto il lavoro per calcolare
T(N)
quando N % 2 == 1
, lo sai T(N + 1) == T(N) + 2
. Questo mi permette di iniziare il mio conteggio da tre e di aumentare di iterazione per due.
- Conservo i miei numeri primi in un array anziché in un
Collection
tipo. Questo è più che raddoppiato N
che posso raggiungere.
- Uso i numeri primi per fattorizzare un numero anziché eseguire il setaccio di Eratostene. Ciò significa che la mia memoria è limitata quasi completamente al mio array di numeri primi.
- Conservo la radice quadrata del numero per il quale sto cercando di trovare il fattore più piccolo. Ho provato l'approccio di @ user1354678 di quadrare un fattore primo ogni volta, ma questo mi è costato circa 1e7 dal mio punteggio.
Questo è tutto quello che c'è da fare. Il mio codice scorre da 3 in poi fino a quando non rileva che ha raggiunto o superato il limite di tempo, a quel punto sputa la risposta.
package sum_of_smallest_factors;
public final class SumOfSmallestFactors {
private static class Result {
private final int number;
int getNumber() {
return number;
}
private final long sum;
long getSum() {
return sum;
}
Result(int number, long sum) {
this.number = number;
this.sum = sum;
}
}
private static final long TIME_LIMIT = 60_000_000_000L; // 60 seconds x 1e9 nanoseconds / second
public static void main(String[] args) {
SumOfSmallestFactors main = new SumOfSmallestFactors();
Result result = main.run();
int number = result.getNumber();
long sum = result.getSum();
System.out.format("T(%,d) = %,d\n", number, sum);
}
private int[] primes = new int[16_777_216];
private int primeCount = 0;
private long startTime;
private SumOfSmallestFactors() {}
private Result run() {
startClock();
int number;
long sumOfSmallestFactors = 2;
for (number = 3; mayContinue(); number += 2) {
int smallestFactor = getSmallestFactor(number);
if (smallestFactor == number) {
addPrime(number);
}
sumOfSmallestFactors += smallestFactor + 2;
}
--number;
Result result = new Result(number, sumOfSmallestFactors);
return result;
}
private void startClock() {
startTime = System.nanoTime();
}
private boolean mayContinue() {
long currentTime = System.nanoTime();
long elapsedTime = currentTime - startTime;
boolean result = (elapsedTime < TIME_LIMIT);
return result;
}
private int getSmallestFactor(int number) {
int smallestFactor = number;
int squareRoot = (int) Math.ceil(Math.sqrt(number));
int index;
int prime = 3;
for (index = 0; index < primeCount; ++index) {
prime = primes[index];
if (prime > squareRoot) {
break;
}
int remainder = number % prime;
if (remainder == 0) {
smallestFactor = prime;
break;
}
}
return smallestFactor;
}
private void addPrime(int prime) {
primes[primeCount] = prime;
++primeCount;
}
}
L'esecuzione su un altro sistema (Windows 8.1, Intel Core i7 a 2,5 GHz, 8 GB RAM) con l'ultima versione di Java 8 ha risultati notevolmente migliori senza modifiche al codice:
T(240,308,208) = 1,537,216,753,010,879