Escrito por Lúcio Figueiredo
Um dos problemas mais clássicos da matemática computacional consiste em encontrar maneiras eficientes para descobrir se um número é primo ou não, ou seja, realizar um teste de primalidade. Nesta aula, abordaremos um algoritmo simples para realizar testes de primalidade e encontrar divisores e fatores primos de um inteiro qualquer.
Teste de Primalidade em
Para checar se um certo número inteiro é primo ou não, podemos imaginar o seguinte simples algoritmo: Para cada número tal que , testamos se é um divisor de , ou seja, se . Se sim, é certamente um número composto. Senão, os únicos divisores de são e o próprio , e logo, é um número primo.
O problema do algoritmo acima é que a sua complexidade é , o que, dependendo da situação, pode ser muito ineficiente. Para uma otimização significativa, vamos utilizar o seguinte lema:
Lema 1: Um número inteiro é composto se, e somente se este possui um divisor próprio (ou seja, um divisor diferente de e de ) menor ou igual a .
Prova: Se possui um divisor próprio menor ou igual a , certamente será composto. Vamos agora provar a outra parte, o "somente se": Seja um divisor próprio qualquer de . Perceba que também é um divisor próprio de . Vamos analisar dois casos:
- : Este caso é trivial, pois já é um divisor que satisfaz o lema.
- : Neste caso, teremos que . Logo, o divisor já torna o lema verdadeiro.
Como em ambos os casos o lema é verdadeiro, terminamos a prova.
Utilizando o lema acima, sabemos que um número é primo se, e somente se não possui nenhum divisor entre e . Logo, podemos otimizar nosso algoritmo inicial testando apenas se existe algum inteiro tal que e divide . Assim, a complexidade do algoritmo otimizado será . Confira o código abaixo:
Divisores de um inteiro
Imagine que temos o seguinte problema: Dado um número inteiro , queremos encontrar todos os divisores positivos de . Uma possível solução seria, novamente, iterar por todos os números de a e checar quais deles dividem , salvando-os em um vetor de respostas. Dessa forma, obtemos uma complexidade de .
Para otimizar este algoritmo, perceba que se é um divisor de maior que , então o número também é um divisor de e . Isso sugere que é suficiente iterarmos apenas pelos números de a , e, assim que encontrarmos um divisor tal que , inserimos tanto quanto em nosso vetor de respostas. Dessa forma, encontraremos tanto os divisores "pequenos" (menores ou iguais a ) de quanto os divisores "grandes" (maiores que ).
Confira o código para melhor entendimento:
Fatoração de um inteiro
Imagine que agora queremos encontrar todos os divisores primos de , ou seja, queremos encontrar a fatoração de em números primos. Inicialmente, observe que o menor divisor próprio de um número composto é sempre um número primo. Além disso, pelo Lema 1, sabemos que este menor divisor é sempre menor ou igual a .
Estas duas observações indicam que podemos realizar o seguinte algoritmo:
- Encontre o menor divisor de e adicione à fatoração.
- Divida por até que o número resultante não seja mais um múltiplo de .
- Repita os passos e até que o número resultante seja primo ou igual a . Caso o número seja diferente de , inclua-o na fatoração de .
Essencialmente, iremos dividir o número sempre pelo seu menor fator, que é sempre um número primo, até que este fator não o divida mais. Desta maneira, o menor divisor do número resultante será o próximo divisor primo de , e assim sucessivamente. Como o divisor será sempre menor ou igual a , a complexidade do algoritmo será (também!) .
Confira o código abaixo para melhor entendimento: