Não entendi, o seu código já estava funcionando (já verificava corretamente se o número é primo), qual era o problema exatamente?
Se estava procurando por um algoritmo melhor, sinto dizer que o que foi postado aí em cima não é a melhor opção. Usar o operador %
para verificar se um número é divisível por outro, como você já estava fazendo, é melhor do que a outra opção dada (que faz uma divisão e uma multiplicação, ou seja, 2 operações em vez de apenas uma), conforme veremos no final.
De qualquer forma, se quiser melhorar um pouco:
private static boolean ehPrimo(int numero) {
if (numero <= 1) {
return false;
} else if (numero == 2 || numero == 3) {
return true;
} else if (numero % 2 == 0 || numero % 3 == 0) {
return false;
} else {
for (int i = 5; i < Math.sqrt(numero); i += 6) {
if (numero % i == 0 || numero % (i + 2) == 0) {
return false;
}
}
}
return true;
}
Alterações feitas:
Eu testo se o número é menor ou igual a 1 (não é primo). Talvez seja preciosismo, mas enfim…
O 2 é o único número par que é primo, então se não for 2, eu preciso testar se é par (pois todo número par é divisível por 2 e portanto não é primo).
Se o número for ímpar, eu testo se ele é divisível por algum número ímpar (eu não preciso testar a divisão por números pares, pois se ele fosse divisível por um número par, então ele também seria par, mas eu já verifiquei isso antes).
E também não preciso fazer o loop até o próprio número, eu posso ir até a raiz quadrada dele, que já é o suficiente.
E com exceção do 2 e 3, todos os outros números primos são da forma 6k - 1
ou 6k + 1
(ou seja, são antecessores ou sucessores de um múltiplo de 6), então eu posso fazer um loop que só testa esses casos.
Pode parecer que ficou “pior” só porque “tem mais linhas”, mas na verdade ficou melhor, mais otimizado, sem fazer operações desnecessárias (veja o teste abaixo).
Claro que ainda não é o “primor da matemática”, com certeza existem muitos outros algoritmos mais eficientes e “espertos”, principalmente para verificar números muito grandes, mas já é melhor do que fazer um loop simples de 1 em 1
Só como curiosidade, fiz um teste usando o Java Microbenchmark Harness (JMH), que é uma ferramenta para medir a execução de programas. O código do teste:
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
@State(Scope.Benchmark)
public class PrimeNumbersBenchmark {
@Param({ "1000", "10000" })
int size; // testa todos os números de 0 até 1000, depois de 0 a 10000
int[] valores;
@Setup
public void setup() {
valores = new int[size + 1];
for (int i = 0; i < valores.length; i++) {
valores[i] = i;
}
}
@Benchmark
public void testLoopComMod(Blackhole bh) {
for (int i : valores) {
bh.consume(loopComMod(i));
}
}
@Benchmark
public void testLoopComMultDiv(Blackhole bh) {
for (int i : valores) {
bh.consume(loopComMultDiv(i));
}
}
@Benchmark
public void testOtimizado(Blackhole bh) {
for (int i : valores) {
bh.consume(otimizado(i));
}
}
private boolean loopComMod(int numero) {
for (int j = 2; j < numero; j++) {
if (numero % j == 0)
return false;
}
return true;
}
private boolean loopComMultDiv(int number) {
for (int i = 2; i < number; i++) {
if ((number / i) * i == number)
return false;
}
return true;
}
private boolean otimizado(int numero) {
if (numero <= 1) {
return false;
} else if (numero == 2 || numero == 3) {
return true;
} else if (numero % 2 == 0 || numero % 3 == 0) {
return false;
} else {
for (int i = 5; i < Math.sqrt(numero); i += 6) {
if (numero % i == 0 || numero % (i + 2) == 0) {
return false;
}
}
}
return true;
}
}
Basicamente, testei todos os números de 0 a 1000 (e depois com todos de 0 a 10000) usando os 3 algoritmos (o seu, que usa %
; o sugerido acima, com a divisão e multiplicação; e o meu). Os loops foram feitos da forma indicada neste exemplo da documentação.
O resultado:
Benchmark (size) Mode Samples Score Score error Units
o.s.PrimeNumbersBenchmark.testLoopComMod 1000 thrpt 200 5439,735 50,771 ops/s
o.s.PrimeNumbersBenchmark.testLoopComMod 10000 thrpt 200 73,930 0,504 ops/s
o.s.PrimeNumbersBenchmark.testLoopComMultDiv 1000 thrpt 200 5037,663 44,648 ops/s
o.s.PrimeNumbersBenchmark.testLoopComMultDiv 10000 thrpt 200 67,537 1,125 ops/s
o.s.PrimeNumbersBenchmark.testOtimizado 1000 thrpt 200 161313,809 1888,790 ops/s
o.s.PrimeNumbersBenchmark.testOtimizado 10000 thrpt 200 6413,778 66,998 ops/s
No modo “thrpt” (throughput) o score indica “operações por segundo” (ops/s
), ou seja, quanto maior o score, melhor.
O “size” indica se o teste foi com 1000 ou 10000 números, e o “score error” indica o desvio máximo ocorrido durante os testes (já que o tempo exato depende de vários fatores e nem sempre será exatamente igual). No caso acima, os desvios variam entre 0,6% e 1,6% do score total, o que eu acho bem aceitável.
Enfim, veja como o seu código (com %
) é um pouco melhor que a alternativa sugerida (com divisão e multiplicação), e como a opção otimizada que eu sugeri é muito melhor que ambas (ou seja, mesmo tendo “mais linhas”, ficou um algoritmo muito melhor).