Java 8: 1.8e8 2.4e8
Esta entrada no se compara con varias de las otras ya publicadas, pero quería publicar mi respuesta ya que me divertí trabajando en esto.
Las principales optimizaciones de mi enfoque son las siguientes:
- Cada número par tiene un factor mínimo de 2, por lo que se pueden agregar de forma gratuita después de que se procese cada número impar. Básicamente, si has hecho el trabajo para calcular
T(N)
cuándo N % 2 == 1
, lo sabes T(N + 1) == T(N) + 2
. Esto me permite comenzar mi conteo a las tres y aumentar por iteración de dos en dos.
- Almaceno mis números primos en una matriz en lugar de un
Collection
tipo. Esto más que duplicó el N
que puedo alcanzar.
- Utilizo los números primos para factorizar un número en lugar de realizar el Tamiz de Eratóstenes. Esto significa que mi almacenamiento de memoria está restringido casi por completo a mi matriz de primos.
- Guardo la raíz cuadrada del número para el que intento encontrar el factor más pequeño. Probé el enfoque de @ user1354678 de cuadrar un factor primo cada vez, pero esto me costó alrededor de 1e7 de mi puntaje.
Eso es todo lo que hay que hacer. Mi código itera de 3 en adelante por dos hasta que detecta que ha alcanzado o excedido el límite de tiempo, momento en el que escupe la respuesta.
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;
}
}
Ejecutar en un sistema diferente (Windows 8.1, Intel Core i7 @ 2.5 GHz, 8 GB de RAM) con la última versión de Java 8 tiene resultados notablemente mejores sin cambios de código:
T(240,308,208) = 1,537,216,753,010,879