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: