Gerando números primos em Java
1. Introdução
Neste tutorial, mostraremos várias maneiras pelas quais podemos gerar números primos usando Java.
Se você deseja verificar se um número é primo, veja aquia quick guide como fazer isso.
2. Números primos
Vamos começar com a definição básica. A prime number is a natural number greater than one that has no positive divisors other than one and itself.
Por exemplo, 7 é primo porque 1 e 7 são seus únicos fatores inteiros positivos, enquanto 12 não é porque possui os divisores 3 e 2, além de 1, 4 e 6.
3. Gerando Números Primos
Nesta seção, veremos como podemos gerar números primos de forma eficiente que são menores do que um determinado valor.
3.1. Java 7 e anterior - força bruta
public static List primeNumbersBruteForce(int n) {
List primeNumbers = new LinkedList<>();
for (int i = 2; i <= n; i++) {
if (isPrimeBruteForce(i)) {
primeNumbers.add(i);
}
}
return primeNumbers;
}
public static boolean isPrimeBruteForce(int number) {
for (int i = 2; i < number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
Como você pode ver,primeNumbersBruteForce está iterando sobre os números de 2 ane simplesmente chamando o métodoisPrimeBruteForce() para verificar se um número é primo ou não.
O método verifica a divisibilidade de cada número pelos números em um intervalo de 2 anumber-1.
If at any point we encounter a number that is divisible, we return false. No final, quando descobrimos que o número não é divisível por nenhum de seus números anteriores, retornamos true indicando que é um número primo.
3.2. Eficiência e Otimização
O algoritmo anterior não é linear e possui a complexidade de tempo de O (n ^ 2). O algoritmo também não é eficiente e há claramente espaço para melhorias.
Vejamos a condição no métodoisPrimeBruteForce().
Quando um número não é primo, esse número pode ser fatorado em dois fatores, a sabera eb, ou seja, number = a * b. If both a and b were greater than the square root of n, a*b would be greater than n.
Portanto, pelo menos um desses fatores deve ser menor ou igual à raiz quadrada de um número e para verificar se um número é primo, precisamos apenas testar fatores inferiores ou iguais à raiz quadrada do número que está sendo verificado.
Os números primos nunca podem ser um número par, pois os números pares são todos divisíveis por 2.
Além disso, os números primos nunca podem ser um número par, pois os números pares são todos divisíveis por 2.
Tendo em mente as ideias acima, vamos melhorar o algoritmo:
public static List primeNumbersBruteForce(int n) {
List primeNumbers = new LinkedList<>();
if (n >= 2) {
primeNumbers.add(2);
}
for (int i = 3; i <= n; i += 2) {
if (isPrimeBruteForce(i)) {
primeNumbers.add(i);
}
}
return primeNumbers;
}
private static boolean isPrimeBruteForce(int number) {
for (int i = 2; i*i < number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
3.3. Usando Java 8
Vamos ver como podemos reescrever a solução anterior usando expressões idiomáticas Java 8:
public static List primeNumbersTill(int n) {
return IntStream.rangeClosed(2, n)
.filter(x -> isPrime(x)).boxed()
.collect(Collectors.toList());
}
private static boolean isPrime(int number) {
return IntStream.rangeClosed(2, (int) (Math.sqrt(number)))
.filter(n -> (n & 0X1) != 0)
.allMatch(n -> x % n != 0);
}
3.4. Usando a peneira de Eratóstenes
Existe ainda outro método eficiente que pode nos ajudar a gerar números primos de forma eficiente, e é chamado de peneira de Eratóstenes. Sua eficiência de tempo é O (n logn).
Vamos dar uma olhada nas etapas deste algoritmo:
-
Crie uma lista de inteiros consecutivos de 2 an: (2, 3, 4,…, n)
-
Inicialmente, sejap igual a 2, o primeiro número primo
-
Começando emp, conte em incrementos dep e marque cada um desses números maiores do quep na lista. Esses números serão 2p, 3p, 4p, etc .; note que alguns deles podem já ter sido marcados
-
Encontre o primeiro número maior quep na lista que não está marcada. Se não houver esse número, pare. Caso contrário, deixep agora igual a esse número (que é o próximo primo) e repita a partir da etapa 3
No final, quando o algoritmo termina, todos os números na lista que não estão marcados são os números primos.
Esta é a aparência do código:
public static List sieveOfEratosthenes(int n) {
boolean prime[] = new boolean[n + 1];
Arrays.fill(prime, true);
for (int p = 2; p * p <= n; p++) {
if (prime[p]) {
for (int i = p * 2; i <= n; i += p) {
prime[i] = false;
}
}
}
List primeNumbers = new LinkedList<>();
for (int i = 2; i <= n; i++) {
if (prime[i]) {
primeNumbers.add(i);
}
}
return primeNumbers;
}
3.5. Exemplo de trabalho da peneira de Eratóstenes
Vamos ver como funciona para n = 30.
Considere a imagem acima, aqui estão os passes feitos pelo algoritmo:
-
O loop começa com 2, então deixamos 2 sem marcação e marcamos todos os divisores de 2. Está marcado na imagem com a cor vermelha
-
O loop passa para 3, então deixamos 3 sem marcação e marcamos todos os divisores de 3 ainda não marcados. Está marcado na imagem com a cor verde
-
O loop vai para 4, já está marcado, então continuamos
-
O loop passa para 5, então deixamos 5 sem marcação e marcamos todos os divisores de 5 ainda não marcados. Está marcado na imagem com a cor roxa
-
Continuamos os passos acima até que o loop seja igual à raiz quadrada den
4. Conclusão
Neste tutorial rápido, ilustramos maneiras pelas quais podemos gerar números primos até o valor ‘N '.
A implementação desses exemplos pode ser encontradaover on GitHub.