Escrito por Leonardo Paes e Lúcio Figueiredo
Na matemática computacional há diversas maneiras de checar se um número é primo ou não, com diferentes algoritmos que possuem seus pontos fortes e fracos. Na aula de hoje, aprenderemos um algoritmo chamado de Crivo de Eratóstenes. Como o próprio nome sugere, seu criador foi Eratóstenes (a.C. 285-194 a.C.), um matemático grego.
Motivação Inicial
Imagine que precisamos resolver o seguinte problema: dado um número inteiro , indique quantos números primos existem no intervalo
(intervalo de
a
, inclusive). Para resolver este problema, podemos utilizar um algoritmo bastante simples: para cada número
no intervalo
, realizamos um teste de primalidade em
- se o teste indicar que
é primo, a resposta do problema aumenta em 1; senão, continua a mesma. Se utilizarmos o teste de primalidade apresentando na Aula 2, obtemos uma solução com complexidade
, já que iteramos por
números e realizamos um teste de primalidade em
em cada um.
Perceba, porém, que para valores de maiores ou iguais a
, o algoritmo acima é muito ineficiente, necessitando de pelo menos
operações! Felizmente, uma solução mais eficiente existe: o Crivo de Eratóstenes, que será apresentado a seguir.
Crivo de Eratóstenes
O algoritmo em si é bem simples:
- Escreva todos os números de
até
. Inicialmente, nós não vamos considerá-los nem primos nem compostos (números que possuem mais que 2 divisores distintos).
- Pegue o primeiro número que ainda não foi marcado como composto e diga que ele é primo.
- A partir desse número primo, percorra todos os seus múltiplos até
e marque-os como compostos. Eles necessariamente são compostos pois possuem como divisores, pelo menos, o número
, o primo atual e eles mesmos.
- Volte para o passo 2 até que todos os números sejam marcados como primos ou compostos.
A ideia por trás do algoritmo é a seguinte: Um número só é primo se nenhum outro primo no intervalo
divide
. Como estamos iterando pelos números primos da esquerda para a direita, estamos marcando como compostos todos os números que são seus múltiplos. Então, se chegarmos em um número que não foi dito como composto
ele é primo!
Visualização do algoritmo
Gif utilizado de: Wikipedia.
Código do algoritmo
Complexidade do Crivo de Eratóstenes
Você deve ter percebido que até agora não foi dito nada sobre a complexidade do algoritmo do Crivo. Nesta aula, provaremos que a sua complexidade é .
Para simplificar os cálculos, vamos assumir que é uma potência de
, ou seja
,
. Seja
a complexidade do algoritmo. Note que
é igual ao somatório, para cada primo
, do número de múltiplos de
menores ou iguais a
; em outras palavras,
é igual ao somatório de
para todo número primo
. Portanto, perceba que se em nosso algoritmo, ao invés de iterarmos por todos os múltiplos dos números primos, iterássemos pelos múltiplos de todos os números menores ou iguais a
, a complexidade resultante do algoritmo seria maior ou igual à do Crivo - ou seja, maior ou igual a
. Logo, temos que,
.
Vamos agora supor que a quantidade de operações é ainda maior (ou seja, utilizar um limite superior ainda mais "frouxo" para ). Ao invés de somarmos
para cada
, vamos somar
. Desse modo, o número total de operações (que continua maior que
) é:
Agora, note que , já que estamos calculando a parte inteira de cada logaritmo. Utilizando este fato, podemos concluir que a expressão acima é equivalente a
Como vimos que é menor ou igual à expressão calculada acima, obtemos que
complexidade do Crivo de Eratóstenes
.
Obs.: Na demonstração acima assumimos que o valor era uma potência de
. Caso isto não fosse verdade, o resultado obtido seria bastante semelhante, com a diferença de que a expressão
seria igual a
; ou seja,
somado a um valor "de resto" menor ou igual a
. Portanto, a complexidade do algoritmo é a mesma em ambos os casos.
Apesar de termos provado que o Crivo possui complexidade , é possível provar que o algoritmo é ainda mais eficiente, possuindo complexidade
. Como a demonstração deste fato requer conhecimentos de cálculo integral, não a mencionamos nesta aula; porém, caso queira conferi-la, leia este artigo (em inglês).
O Menor Fator Primo (SPF)
Uma das aplicações do Crivo de Eratóstenes é o cálculo do Menor Fator Primo de um inteiro, ou SPF (do inglês Shortest Prime Factor). Alguns exemplos de SPF:
, já que
.
, já que
.
, já que
.
Para calcular o SPF de todos os números no intervalo , basta fazer uma única modificação ao Crivo de Eratóstenes. Inicialmente, vamos declarar o vetor
, que indica o menor fator primo de um número, e vamos inicializá-lo com o valor
. Após isso, vamos executar o Crivo. Se no passo atual do algoritmo o primeiro número não marcado é o primo
, vamos identificar se
é ou não o menor fator primo de cada um de seus múltiplos, conferindo para cada múltiplo
de
se
é igual a
; se sim,
não foi marcado, e portanto
é seu menor fator primo; caso contrário, seu menor fator primo já foi encontrado em um passo anterior do algoritmo. O código abaixo implementa esta modificação do Crivo para encontrar SPFs:
Fatoração em 
Imagine que queremos fatorar um número inteiro em primos, ou seja, encontrar todos os seus divisores primos. Um algoritmo que resolve este problema é aquele que foi apresentado na aula anterior, com complexidade
. Porém, utilizando o Menor Fator Primo calculado pelo Crivo de Eratóstenes, conseguimos realizar fatorações em
.
Inicialmente, perceba que um número inteiro possui, no máximo,
fatores primos, já que o menor número primo é
e o maior expoente
tal que
é
. Utilizando o SPF, podemos iterar por todos os primos na fatoração de
em ordem, da seguinte forma:
- Inicialmente, a lista de fatores primos de
está vazia;
- Se
for maior que
, insira
na lista de fatores primos; senão, termine o algoritmo;
- Repita o passo anterior fazendo
Ou seja, primeiro inserimos na lista, depois
, e assim por diante, enquanto o número resultante for maior ou igual a
. Desta forma, conseguimos iterar por todos os fatores primos de
em ordem. Como
possui no máximo
fatores primos, a complexidade deste algoritmo de fatoração é
. O código abaixo implementa a fatoração de
utilizando este método: