Em computação, a fatoração de números inteiros é um problema NP e não se sabe se existe um algoritmo polinomial capaz de resolver esse problema.
É claro, o fato de não sabermos da existência de um algoritmo polinomial não implica que não existem algoritmos que realizam a fatoração.
Neste artigo, apresento um algoritmo que faz a fatoração via força bruta chamado de "divisão por tentativa". O algoritmo é exato, isto é, dado um número inteiro qualquer, o algoritmo irá retornar uma lista com os fatores desse número. Entretanto, ele faz a fatoração em tempo exponencial.
Além do algoritmo, também apresento implementações do mesmo em Java, C e JavaScript.
Algoritmo de divisão por tentativa
A ideia do algoritmo é bem simples: dado um número n, verificamos se cada número menor do que n é seu divisor.
Por exemplo, para n = 8, teríamos
- 8 é divisível por 1;
- 8 é divisível por 2;
- 8 não é divisível por 3;
- 8 é divisível por 4;
- 8 não é divisível por 5;
- 8 não é divisível por 6;
- 8 não é divisível por 7;
- 8 é divisível por 8.
Percebe-se claramente que se o número for grande, o algoritmo será bem lento, por isso é conveniente realizar alguma otimização.
Na prática, não precisamos checar todos os valores até n. É suficiente verificar somente até $\sqrt{n}$. Isto é, se p é o fator analisado, então a seguinte condição deve ser satisfeita: $p^2 \leq n$.
A justificativa é que se p é divisor de n, então $n = p \times q$ e que se $q < p$, então q ou um dos seus fatores primos deveria ter sido detectado previamente como um divisor de n [2].
Além disso, se $n = p\times q$, então pelo menos um dos fatores não deve ultrapassar $\sqrt{n}$, senão o produto seria maior que n [1].
O algoritmo, em pseudocódigo, é
01. trialDivision(n) 02. | para d ← 2 até √n 03. | | enquanto(n % d = 0) 04. | | | imprima d 05. | | | n ← n / d 06. | | fim_enquanto 07. | fim_para 08. | imprima n 09. fim_trialDivision
Mas podemos melhorar. O algoritmo anterior irá imprimir apenas fatores primos de n, incluindo repetições. Sabemos que todos os primos, exceto 2, são ímpares. Então podemos verificar se 2 é um fator separadamente e depois utilizamos um laço para verificar apenas números ímpares. Isso reduz o custo pela metade. Em pseudocódigo, temos (o algoritmo a seguir é da Wikipédia, mas com algumas alterações) [2]
01. trialDivision(n) 02. | enquanto(n % 2 = 0) 03. | | imprima 2 04. | | n ← n / 2 05. | fim_enquanto 06. | d ← 3 07. | enquanto(d2 ≤ n) 08. | | se(n % d = 0) 09. | | | imprima d 10. | | | n ← n / d 11. | | senão 12. | | | d ← d + 2 13. | | fim_se 14. | fim_enquanto 15. | se(n > 1) 16. | | imprima n 17. | fim_se 18. fim_trialDivision
O laço das linhas 2-5 imprime todas as ocorrências do fator 2 em n (se houver). A variável d (linha 6) conterá os possíveis fatores ímpares de n (afinal, não haverá mais pares).
O laço das linhas 7-14 verifica exaustivamente os divisores ímpares de n, começando pelo 3, até que não haja mais divisores para verificar. Se algum número não for fator de n ou se já verificamos todas as ocorrências daquele fator, o laço pula para o próximo ímpar na linha 12.
Observe que o laço será executado enquanto d2 for menor ou igual a n, que é equivalente a verificar se d não ultrapassa a raiz quadrada de n.
A condição nas linhas 15-17 é necessária porque se n for primo, então a condição da linha 8 sempre será falsa. Além disso, uma vez que nunca ultrapassaremos √n nas iterações, então o valor da variável d nunca alcançará n.
O algoritmo irá imprimir apenas fatores primos, pois à medida que eliminamos a ocorrência de um fator, também eliminamos a ocorrência dos múltiplos daquele fator. A ideia é similar ao Crivo de Erastóstenes.
Por fim, conforme mencionei no início do artigo, o algoritmo de divisão por tentativa tem complexidade exponencial, portanto ele é inviável para números grandes.
Leia também
Códigos
Os códigos são apresentados a seguir
Código em Java
O código em Java a seguir armazena os fatores numa lista ligada, mas você pode utilizar a estrutura de dados que quiser. A lista irá conter todos os fatores primos do número fatorado, incluindo as repetições.
//GitHub: HenriqueIni
import java.util.LinkedList;
import java.util.List;
// Algoritmo de fatoração 'divisão por tentativa' (Trial Division)
public class TrialDivision {
// Retorna uma lista de fatores primos de n
// Se quiser, substitua 'int' por 'long'
public static List<Integer> trialDivision(int n){
// O fatores são armazenados numa lista ligada
// Entretanto você pode usar a estrutura que quiser
// Exemplos: Array, ArrayList, Stack (pilha)
List<Integer> factors = new LinkedList<Integer>();
// Verifica se 2 é fator
while(n % 2 == 0){
factors.add(2);
n = n / 2;
}
// Agora verifica os possíveis fatores ímpares
// Só ímpares são possíveis
int d = 3; // Possíveis fatores
int d2 = 9; // d2 = d * d
while(d2 <= n){
// Se d é fator, faz a divisão e armazena o fator
if(n % d == 0){
factors.add(d);
n = n / d;
}else{
// Se d não é fator, verifica o próximo
d = d + 2;
d2 = d * d; // d2 = d*d
}
}
// Essa condição é necessária quando n for primo
if(n > 1){
factors.add(n);
}
return factors;
}
// Testes
public static void main(String[] args) {
System.out.println("Fatores de 10 = " + trialDivision(10));
System.out.println("Fatores de 50 = " + trialDivision(50));
System.out.println("Fatores de 24 = " + trialDivision(24));
System.out.println("Fatores de 350 = " + trialDivision(350));
System.out.println("Fatores de 107 = " + trialDivision(107));
System.out.println("Fatores de 1025 = " + trialDivision(1025));
System.out.println("Fatores de 3 * 5 * 7 * 11 * 13 * 17 = " +
trialDivision(3 * 5 * 7 * 11 * 13 * 17));
}
}
Código em C
Ao contrário do código em Java, o código em C a seguir imprime os fatores ao invés de armazená-los em alguma estrutura de dados.
//GitHub: HenriqueIni
#include <stdio.h>
#include <stdlib.h>
// Imprime os fatores primos de n
// Se quiser, substitua 'int' por 'long'
void trialDivision(int n){
// Verifica se 2 é fator
while(n % 2 == 0){
printf("%d, ", 2);
n = n / 2;
}
// Agora verifica os possíveis fatores ímpares
// Só ímpares são possíveis
int d = 3; // Possíveis fatores
int d2 = 9; // d2 = d * d
while(d2 <= n){
// Se d é fator, faz a divisão e imprime o fator
if(n % d == 0){
printf("%d, ", d);
n = n / d;
}else{
// Se d não é fator, verifica o próximo
d = d + 2;
d2 = d * d; // d2 = d*d
}
}
// Essa condição é necessária quando n for primo
if(n > 1){
printf("%d, ", n);
}
printf("\n");
}
int main() {
printf("Fatores de 10 = ");
trialDivision(10);
printf("Fatores de 50 = ");
trialDivision(50);
printf("Fatores de 24 = ");
trialDivision(24);
printf("Fatores de 350 = ");
trialDivision(350);
printf("Fatores de 107 = ");
trialDivision(107);
printf("Fatores de 1025 = ");
trialDivision(1025);
printf("Fatores de 3 * 5 * 7 * 11 * 13 * 17 = ");
trialDivision(3 * 5 * 7 * 11 * 13 * 17);
return 0;
}
Código em JavaScript
O código em JavaScript a seguir contém apenas a função que realiza a fatoração.
//GitHub: HenriqueIni
// Algoritmo de fatoração 'divisão por tentativa' (Trial Division)
// Retorna uma lista de fatores primos de n
function trialDivision(n){
// O fatores são armazenados num array
var factors = [];
// Verifica se 2 é fator
while(n % 2 == 0){
factors.push(2);
n = n / 2;
}
// Agora verifica os possíveis fatores ímpares
// Só ímpares são possíveis
var d = 3; // Possíveis fatores
var d2 = 9; // d2 = d * d
while(d2 <= n){
// Se d é fator, faz a divisão e armazena o fator
if(n % d == 0){
factors.push(d);
n = n / d;
}else{
// Se d não é fator, verifica o próximo
d = d + 2;
d2 = d * d; // d2 = d*d
}
}
// Essa condição é necessária quando n for primo
if(n > 1){
factors.push(n);
}
return factors;
}
Referências
- [1] AMIT, ALON (23 de junho 2016). What is trial division method for factorization? How is it implemented? Is it fit for large numbers?. Acesso em 23 de abril de 2018.
- [2] Wikipédia. Trial division. Acesso em 23 de abril de 2018.

so faltou explicar o pq do algoritmo ser exponencial, mas a explicação sobre so precisar ir até a raiz quadrada pra achar os primos foi útil.
ResponderExcluir