COMPLEXIDADE COMPUTACIONAL NO CÁLCULO DE NÚMEROS

Propaganda
UNIJUÍ - UNIVERSIDADE REGIONAL DO NOROESTE DO ESTADO DO
RIO GRANDE DO SUL
DCEEng - DEPARTAMENTO DE CIÊNCIAS EXATAS E ENGENHARIAS
CURSO DE GRADUAÇÃO EM CIÊNCIA DA COMPUTAÇÃO
COMPLEXIDADE COMPUTACIONAL
NO CÁLCULO DE NÚMEROS PRIMOS E PERFEITOS
MAURI JOSÉ KLEIN
Santa Rosa, RS - Brasil.
2012
UNIJUÍ - UNIVERSIDADE REGIONAL DO NOROESTE DO ESTADO DO
RIO GRANDE DO SUL
DCEEng - DEPARTAMENTO DE CIÊNCIAS EXATAS E ENGENHARIAS
CURSO DE GRADUAÇÃO EM CIÊNCIA DA COMPUTAÇÃO
COMPLEXIDADE COMPUTACIONAL
NO CÁLCULO DE NÚMEROS PRIMOS E PERFEITOS
MAURI JOSÉ KLEIN
Trabalho de Conclusão de Curso apresentado ao
curso
de
Ciência
da
Computação,
do
Departamento de Ciências Exatas e Engenharias,
da Universidade Regional do Noroeste do Estado
do Rio Grande do Sul, como requisito parcial
para obtenção do grau de Bacharel em Ciência da
Computação.
Orientador: Professor Doutor GERSON BATTISTI
Santa Rosa – RS
2012
COMPLEXIDADE COMPUTACIONAL
NO CÁLCULO DE NÚMEROS PRIMOS E PERFEITOS
MAURI JOSÉ KLEIN
Trabalho de Conclusão de Curso apresentado ao
curso
de
Ciência
da
Computação,
do
Departamento de Ciências Exatas e Engenharias,
da Universidade Regional do Noroeste do Estado
do Rio Grande do Sul, como requisito parcial
para obtenção do grau de Bacharel em Ciência da
Computação.
______________________________________
Orientador: Prof. (Doutor). GERSON BATTISTI
BANCA EXAMINADORA
______________________________________
Prof. (Mestre) VINÍCIUS MARAN
Santa Rosa – RS
2012
Os primos são as pérolas que adornam a vastidão infinita
do universo de números que os matemáticos exploraram ao longo dos
séculos. Eles despertam a admiração dos matemáticos:
2, 3, 5, 7, 11, 13, 17, 19, 23… números eternos que existem
em uma espécie de mundo independente de nossa realidade física.
São um presente da natureza para o matemático.
(MARCUS DU SAUTOY, 2007, p.11).
Dedicatória
Ao meu pai Arno João Klein, pela
inspiração
proporcionada
para
os
estudos, principalmente pelo fascínio
aos números.
Aos meus filhos Keila Caroline e
Bruno Daniel e a minha esposa Marlise
Inês Seibt, um grande beijo.
AGRADECIMENTOS
Agradeço ao meu Professor Orientador Gerson Battisti, que foi a primeira pessoa
que tive contato na universidade e que desde o início me motivou com os seus desafios.
Lembro-me do primeiro dia de aula, no componente “Algoritmos”, o desafio dos “Jesuítas e
dos Canibais”.
Pois também foi assim que surgiu o interesse pelo assunto do meu TCC, que foi
colocado como desafio em uma aula de programação. Obrigado prof. Gerson.
Obrigado também a todos os professores que tive durante os 4 anos, que foram
todos muito respeitosos e eficientes proporcionando o aprendizado.
Especialmente agradeço ao meu filho Bruno Daniel, pela inspiração e pela
motivação que me proporcionou ao longo dos quatro anos de curso. A minha filha Keila
Caroline, que se fez presente entre nós no decorrer da minha caminhada como universitário.
Com ela veio uma nova motivação para que a caminhada fosse concluída com êxito. E a
minha esposa Marlise Inês Seibt que por horas assumia todos os compromissos para que
pudesse me dedicar aos estudos.
RESUMO
Fatorar um número de 12 milhões de dígitos ou confirmar sua primalidade não é
trivial. Os números primos são conhecidos e estudados há séculos por cientistas e
pesquisadores de diversas áreas, que dedicam suas vidas para desvendar alguma
particularidade desta classe de números tão singular. Com o advento da computação e a
capacidade de processamento das máquinas, os estudos vêm evoluindo com muita rapidez,
porém o custo computacional ainda é muito grande, levando-se em consideração, por
exemplo, o teste de primalidade de 243112609 - 1. Por esta dificuldade de fatoração, utilizam-se
os números primos na criptografia de dados para transmissão pela web, principalmente o
Modelo de Criptografia RSA, que consiste basicamente na obtenção de dois números primos
enormes como base para criação da chave pública para cifrar os dados. Assim, fica
praticamente impossível alguém obter os divisor do número e achar a chave privada para
decifrar os dados. Este trabalho visa quantificar e especificar a complexidade destes cálculos,
e conhecer mais um pouco destes números especiais.
Palavras-Chave: primos; mersenne; perfeitos; criptografia; complexidade;
ABSTRACT
Factoring a number of 12 million digits or confirm its primality is not trivial. Prime
numbers are known and studied for centuries by scientists and researchers from various fields
who dedicate their lives to uncover some peculiarity of this class of numbers so unique. With
the advent of computing and processing capability of the machines, studies have evolved very
quickly, but the computational cost is still too great, taking into account, for example the test
of primality 243.112.609 - 1. By factoring this difficulty, we use prime numbers to encrypt data
for transmission over the web. The model basically consists RSA Encryption in getting two
huge prime numbers as the basis for creating the public key to encrypt the data. Thus, it is
virtually impossible for anyone to get the divisor of the number and find the private key to
decrypt the data. Quantify and specify the complexity of these calculations is to know a little
more of these special numbers.
Keywords: prime; Mersenne; perfect; cryptography; complexity;
LISTA DE SIGLAS
√
≅
≡
µs
A.C
AKS
ASCII
Cong.
FFT
Hs.
IDE
log
MDC
Min.
Mod.
O
P
PE.
Pg.
PT
RSA
S
WEB
Ω
Ө
-
Raíz quadrada;
Aproximadamente;
Congruência;
Milissegundos;
Antes de Cristo;
Manindra Agrawal, Neeraj Kayal e Nitin Saxena;
American Standard Code for Information Interchange
Congruência;
Transformada Rápida de Fourier;
Horas;
Integrated Development Environment;
Logaritmo;
Mínimo Divisor Comum;
Minutos;
Módulo;
Ó Grande;
Polinomial;
Padre;
Página;
Pontos;
Ronald Rivest, Adi Shamir e Leonard Adleman
Segundos;
World Wide Web;
Ômega;
Theta;
LISTA DE FIGURAS
Figura 1: Pseudocódigo para teste de primalidade; .................................................................. 21
Figura 2: Notação O; g(n) O f(n). ............................................................................................. 23
Figura 3: Notação Ω; g(n) Ω f(n). ............................................................................................ 24
Figura 4: Notação Ө; g(n) Ө f(n). ............................................................................................. 25
Figura 5: Exemplo de Criptografia RSA .................................................................................. 40
Figura 6: Algoritmo Força Bruta em JAVA ............................................................................. 42
Figura 7: Exemplo do Crivo de Erastóstenes até o número 101. ............................................. 44
Figura 8: Algoritmo do crivo de Erastóstenes; ......................................................................... 45
Figura 9: Exemplo do teste de Lucas-Lehmer .......................................................................... 47
Figura 10: Número gerado com p=607: 2607-1. ........................................................................ 47
Figura 11: Algoritmo em Java do teste de Lucas-Lehmer ....................................................... 48
Figura 12: Algoritmo Java do Teorema de Fermat................................................................... 50
Figura 13: Algoritmo para o teste dos números primos de Sophie Germain ........................... 55
Figura 14: Diagrama Conceitual Java ...................................................................................... 57
Figura 15: Congruência Prima mod. 30 x Força Bruta............................................................. 64
Figura 16: Incidência de Falsos Primos em cada intervalo ...................................................... 66
Figura 17: Algoritmo congruência 11, 23, 59 mod. 60. ........................................................... 67
Figura 18: Exemplo de divisibilidade de um número de mersenne. ........................................ 68
Figura 19: Crivo de Erastóstenes x Força Bruta ....................................................................... 70
Figura 20: Lucas-Lehmer X Força Bruta. ................................................................................ 71
Figura 21: Teorema de Fermat x Força Bruta. ......................................................................... 72
LISTA DE QUADROS
Quadro 1: Tamanho do Problema x Tempo de Execução .............................................. 18
Quadro 2: Complexidade Melhor Caso para teste de primalidade; ................................ 21
Quadro 3: Complexidade Caso Médio para teste de primalidade .................................. 22
Quadro 4: Complexidade Caso Médio para teste de primalidade .................................. 22
Quadro 5: Probabilidade de incidência de números perfeitos. ....................................... 29
Quadro 6: Lista de números perfeitos............................................................................. 31
Quadro 7: fatores primos de números inteiros positivos ................................................ 33
Quadro 8: Semi-primo e fatores do desafio RSA Laboratories. ..................................... 35
Quadro 9: Exemplos de prova da infinitude dos números primos. ................................ 37
Quadro 10: Tempo de execução do algoritmo de força bruta. ....................................... 43
Quadro 11: Analise do crivo de Erastóstenes ................................................................. 46
Quadro 12: Análise do teste de Lucas-Lehmer .............................................................. 49
Quadro 13: Números primos de Sophie Germain ente 1 e 104. ...................................... 54
Quadro 14: Congruência pelo método binário ............................................................... 61
Quadro 15: Congruência mod. 30 num determinado intervalo de números ................... 63
Quadro 16: Congruência Prima mod. 30 x Força Bruta. ................................................ 64
Quadro 17: Incidência de primos e falsos primos com cong. 11, 23 e 59 mod. 60. ....... 65
Quadro 18: Lista de Falsos Primos com Congruência 11, 23, 59 mod. 60. ................... 66
Quadro 19: Crivo de Erastóstenes x Força Bruta. .......................................................... 70
Quadro 20: Lucas-Lehmer X Força Bruta. ..................................................................... 71
Quadro 21: Teorema de Fermat x Força Bruta. .............................................................. 72
SUMÁRIO
INTRODUÇÃO ............................................................................................................ 14
2. COMPLEXIDADE COMPUTACIONAL DE ALGORITMOS .......................... 18
2.1. MEDIDAS DE COMPLEXIDADE .................................................................... 19
2.1.1. Melhor Caso ................................................................................................. 21
2.1.2. Caso Médio................................................................................................... 21
2.1.3. Pior Caso ...................................................................................................... 22
2.2. NOTAÇÕES DE COMPLEXIDADE ................................................................. 23
2.2.1. Notação O ..................................................................................................... 23
2.2.2. Notação Ω ..................................................................................................... 24
2.2.3. Notação Ө ..................................................................................................... 24
3. TEORIA DOS NÚMEROS ..................................................................................... 26
3.1. NÚMEROS PERFEITOS ................................................................................... 26
3.2. NÚMEROS PRIMOS ......................................................................................... 32
3.2.1. Aplicações, Pesquisas e Premiações. .......................................................... 33
3.2.2. Infinitude dos Números Primos ................................................................. 35
3.2.3. Criptografia RSA ........................................................................................ 37
3.3. DERIVAÇÕES DE NÚMEROS PRIMOS ......................................................... 40
3.3.1. Força Bruta .................................................................................................. 41
3.3.2. O Crivo de Erastóstenes ............................................................................. 43
3.3.3. Teste de Lucas – Lehmer ........................................................................... 46
3.3.4. O Teorema de Fermat ................................................................................ 50
3.3.5. Algoritmo AKS ........................................................................................... 51
3.3.6. Primos de Sophie Germain ......................................................................... 52
4. ANÁLISE E AVALIAÇÃO DE ALGORÍTMOS ................................................. 56
4.1. JAVA E IDE NETBEANS .................................................................................. 56
4.1.1 Classe BigInteger .......................................................................................... 57
4.1.2. Principais Métodos Utilizados .................................................................... 58
4.2. MÉTODO BINÁRIO PARA DIVISÃO DE GRANDES NÚMEROS ............... 60
4.3. CONSTATAÇÕES ATRAVÉS DE TESTES OU PROVAS MATEMÁTICAS 61
4.3.1. Congruência prima mod. 30 ....................................................................... 62
4.3.2. Congruência 11, 23 ou 59 mod. 60 ............................................................. 65
4.3.3. Divisores de Números de Mersenne são da Forma 2kp + 1..................... 67
4.3.4. Comparações entre os principais algoritmos: .......................................... 69
5. CONSIDERAÇÕES FINAIS ................................................................................... 73
BIBLIOGRAFIA .......................................................................................................... 76
14
INTRODUÇÃO
Enquanto Dario I rei da Pérsia (522 – 486 A.C) contava os dias através de nós
em barbantes e outros povos ainda mais antigos controlavam suas informações
contábeis representadas em peças de diferentes formatos e armazenadas em urnas de
barro (IFRAH, 1947), temos hoje acessibilidade a computadores com grande poder
computacional capazes de gerar um número com mais de mil páginas em poucos
segundos, especificamente, neste caso, através de uma equação matemática proposta por
Marin mersenne para gerar um tipo especial de números – os números de mersenne.
Neste contexto podemos dizer que os números primos desafiam a capacidade
do ser humano. A singularidade destes números é fascinante.
Matemáticos de todas as partes do mundo buscam explicações, novas teorias,
conjecturas ou tentam provar algumas já existentes. Este estudo já se estendeu para
outras áreas de pesquisa. Cientistas da computação são potencialmente capazes e de
fundamental importância para esta pesquisa, devido à complexidade dos cálculos que
precisam ser feitos.
A hipótese de Riemann contextualizada e exemplificada por Clay Mathematics
Institute em seu site foi um passo dado apenas com um pé, e continua sendo um dos
principais desafios que os intrigados cientistas e alguns aventureiros tentam provar. Ela
consiste, basicamente, na relação dos números primos entre si.
Outra classe de números especiais está diretamente ligada com os números
primos: os números perfeitos.
Os números perfeitos são produto da multiplicação tendo como um dos fatores
um número primo gerado pela equação de mersenne. Sabendo-se que um número de
mersenne é primo, pode-se obter um número perfeito.
O maior número primo, que também é primo de mersenne, encontrado e
confirmado até a data deste trabalho possui mais de 12 milhões de dígitos (12.978.189
dígitos), ou mais de 5 mil páginas com letra tamanho 10 PT. Como o número perfeito é
gerado pela multiplicação deste número pelo número gerado com o expoente
imediatamente inferior, chega-se a um número perfeito de mais de 25 milhões de dígitos
(25.956.377 dígitos). Com isto percebemos o grau de complexidade que estes cálculos
produzem. Estudar esta complexidade é o objetivo deste trabalho (GIMPS, 2012).
15
Todos estes conceitos podem parecer complexos e matemáticos demais para
serem abordados em Ciência da Computação, entretanto, é sabido que a matemática
constitui a base de toda computação. Ainda assim podemos questionar a importância de
um trabalho que pretende desvendar mais uma fração de um dos problemas mais
intrigantes já vistos pela humanidade: os números primos.
Em 2002 três cientistas publicaram o trabalho “Primes is in P”, primeiro
algoritmo que determina se um número é primo em tempo polinomial. Nos anos
seguintes foram feitas melhorias no algoritmo, reduzindo ainda mais o tempo de
processamento, porém como a entrada é excessivamente grande, o custo computacional
continua elevado, e, sendo assim, ainda não é suficientemente rápido para ameaçar a
segurança dos dados da rede mundial de computadores.
Como vimos, têm-se motivos suficientes para pesquisar e estudar estas duas
classes de números e a sua complexidade computacional e tentar aumentar a eficiência
no cálculo destes números, através da implementação de novos algoritmos, utilizando
algumas constatações feitas durante o desenvolvimento do trabalho.
Além da importância acadêmica e do reconhecimento que este trabalho pode
proporcionar, o mesmo ainda envolverá conceitos de segurança eletrônica utilizada na
criptografia de dados e a sua relação com os números primos, dando assim aplicação
concreta para este trabalho.
Sabe-se que a complexidade dos cálculos e a inexistência de uma equação que
seja capaz de gerar números primos, foi o motivo pelo qual três cientistas utilizaram os
números primos para desenvolver o sistema de criptografia RSA que é utilizado
atualmente para transmissão de dados pela WEB (COUTINHO, 2000).
Sem a criptografia não seria possível fazer transações bancarias, compras e
pagamentos com cartão de crédito, além de coisas mais simples, como acessar sua conta
de email.
Todavia, existem outros riscos nestas transações que podem nos causar danos,
porém não é de interesse deste trabalho estudá-las.
Pretende-se, através deste trabalho de conclusão de curso, detalhar todos os
atributos e qualidades dos números primos, primos de mersenne e perfeitos; avaliar os
resultados já confirmados, prever o tempo para obtenção de novos resultados e
16
eventualmente apresentar um algoritmo capaz de gerar números com estas
características.
Para tanto, será feito um detalhamento da arquitetura computacional para as
quatro operações básicas e o seu respectivo custo computacional, além da estrutura e
funcionamento da classe BigInteger da linguagem de programação Java e seus métodos,
os quais serão empregados para a resolução dos testes com números grandes, com
quantidade de algarismos acima das aceitas por atribuição direta a variáveis comuns,
como por exemplo: 2n onde n = 43112609.
Ainda será detalhado o uso de números primos para a criptografia de dados
relacionando com os resultados obtidos nesta pesquisa, a fim de obter uma dimensão da
complexidade empregada na encriptação. E no mesmo contexto apresentar-se-á a prova
da infinitude dos números primos, o que significa que, caso não se encontre um
algoritmo capaz de fatorar um número grande utilizado na encriptação de dados, sempre
haverá disponibilidade de números primos para serem utilizados na Criptografia.
Como contribuição acadêmica deste trabalho, serão levados em consideração
possíveis derivações ou classes especiais de números que apresentem uma característica
em comum, as quais podem ser fortes “candidatas” a números primos ou até podem ser
descartadas dos testes por apresentarem características de número composto. Com isso
se torna possível uma seleção de números restrita, resultando em algoritmos mais
rápidos, inclusive com possibilidade de adaptação em algoritmos de teste de
primalidade já existentes.
Por fim serão destacados possíveis números primos com uma grande
quantidade de algarismos para fazer os testes de primalidade, com o algoritmo mais
eficiente encontrado ou construído durante o trabalho de pesquisa e implementação.
Portanto, considerando a intensidade das pesquisas que se faz em países
europeus, asiáticos e também nos Estados Unidos e uma quase inexistência de
interessados no estudo de algo tão singular no Brasil, mostrou-se como uma
possibilidade de desenvolver uma linha de pensamento que poderá ser seguida.
O trabalho está basicamente dividido em cinco grandes capítulos, os quais são
amplamente descritos e com subtítulos intuitivos para melhor compreensão da
organização do mesmo.
17
No primeiro capítulo temos a introdução como uma descrição geral do trabalho
feito, suas principais características, com os temas abordados e algumas constatações
interessantes para aguçar o interesse pelo trabalho.
No segundo capítulo será apresentada uma revisão bibliográfica sobre a
complexidade computacional de algoritmos, as suas definições, medidas e notações de
complexidade, alem da especificação de melhor caso, pior caso e caso médio.
No terceiro capítulo teremos toda contextualização da teoria dos números.
Serão abordados os números perfeitos como suas características históricas e das novas
pesquisas. Os números primos com as suas derivações e aplicações. Serão apresentados
alguns algoritmos para testes de primalidade, além da prova da sua infinitude dos
números primos.
No quarto capítulo será feita uma análise dos algoritmos utilizados e
apresentados no trabalho e feita uma avaliação de cada um deles, para apresentar
possíveis melhorias através da introdução de alguns cálculos.
No quinto e último Capítulo serão feitas as constatações finais, com a
apresentação de possíveis linhas de pesquisa que possam ser seguidas em trabalhos
futuros.
18
2. COMPLEXIDADE COMPUTACIONAL DE ALGORITMOS
Um algoritmo é um procedimento a ser realizado para se obter uma saída satisfatória
para qualquer entrada, desde que haja tempo e memória suficientes para tal (TOSCANI
E VELOSO, 2002).
Cada procedimento é composto de uma regra ou de um conjunto de regras que
se comportam de maneira a proporcionar o resultado para o qual o algoritmo foi
proposto.
Estes conceitos teóricos sobre algoritmos não são obrigatoriamente aceitáveis
na prática, já que a solução de um problema pode ser simples do ponto de vista
conceitual, e na prática a solução pode não ser alcançável em tempo hábil, devido ao
tamanho da entrada.
Um algoritmo para o cálculo do determinante de uma matriz, por exemplo,
pode variar muito dependendo do método, ou seja, do conjunto de regras utilizadas para
obter o resultado. Se utilizado o método de Cramer, podem-se levar séculos para
calcular o determinante de uma matriz 20 x 20 (20 linhas e 20 colunas). Já com o
método de Gauss o cálculo levará menos de um segundo (TOSCANI E VELOSO,
2002).
n
Método de Cramer
Método de Gauss
2
22 µs
50 µs
3
102 µs
159 µs
4
456 µs
353 µs
5
2,35 ms
666 µs
10
1,19 min.
4,95 ms
20
15225 séculos
38,63 ms
40
5.1033 séculos
0.315 s
Quadro 1: Tamanho do Problema x Tempo de Execução
Fonte: TOSCANI E VELOSO, 2002; pg. 2.
Os números primos também são exemplo de problemas de difícil solução. Com
a inexistência de padrão entre os números primos, os mesmos não podem ser
representados por equações. Assim o teste de primalidade de números extremamente
19
grandes é feito em tempo polinomial, pelo algoritmo AKS, porém na prática, como a
constante, ou seja, o próprio número a ser testado é muito grande, este teste pode levar
muitos anos para testar apenas um número dependendo do tamanho do mesmo.
Existem vários algoritmos para o teste de primalidade e cada um deles usa um
conjunto diferente de regras para chegar ao resultado. Com isto têm-se diferentes
desempenhos destes algoritmos, com dispêndio de tempo e memória distintos, alguns
com soluções exatas, outros com probabilidades. Constata-se ainda que o mesmo
resultado pode ser alcançado com um conjunto de regras diferentes.
Pode-se também utilizar o mesmo algoritmo, ou seja, o mesmo conjunto de
regras, porém com a implementação diferente de operações. Isto pode proporcionar uma
melhora significativa no tempo de execução do algoritmo.
Como exemplo disto tem-se o modulo de uma divisão, teste fundamental na
primalidade dos números. O módulo é o resto r de uma divisão de um número n por um
divisor d. Quando o r for zero, n é um número composto.
EXEMPLO:
21023 -1 módulo 2047 é igual a 0:
n = 21023 -1 =
898846567431157953864652595394512366808988489471153286367150
405788663379027504815663542386612037680105600569399356966788
293948844072083112464237153197370621888839467124327426381511
098006230470597265414760425028844190753411712314407369565552
704136185816752553422931491199736229692398581524176781648121
12068607
d = 2047
r=0
2.1. MEDIDAS DE COMPLEXIDADE
A quantidade de trabalho que um algoritmo requer para apresentar uma solução
satisfatória não pode ser representada na forma de um número, ou uma constante, isto
por que o número de operações básicas efetuadas pelo algoritmo depende do tamanho
da entrada.
20
E mesmo que o tamanho da entrada, ou seja, a quantidade de valores colocados
como entrada do algoritmo seja igual, o trabalho do algoritmo pode ser maior ou menor,
dependendo de outros aspectos.
Por exemplo, para dizer se um número n é primo ou não, podemos ter um custo
computacional muito diferente, dependendo do valor de n.
Esta variação de complexidade, conforme o tamanho da entrada, pode ser
denominada complexidade no pior caso. Já se considerarmos uma solução possível com
menos custo, levando em conta a probabilidade de ocorrência de cada entrada de um
mesmo valor, a complexidade pode ser definida como complexidade esperada ou média
(TOSCANI E VELOSO, 2002).
Considerando um algoritmo de força bruta, sem o uso de nenhuma técnica ou
função matemática específica para cálculo de números primos e que para um
determinado número ser primo ele não pode ter nenhum divisor além de 1 e dele
próprio, a complexidade de confirmação de um número como primo sempre é a
complexidade do prior caso.
Já um número composto vai apresentar características de Melhor Caso e Caso
Médio, já que a fatoração do número n se dá antes de serem esgotadas todas as
possibilidades.
Para termos uma noção mais clara da complexidade computacional para a
definição da primalidade de um número primo podemos considerar para cada caso uma
entrada n específica e um algoritmo de força bruta que testa todas as possibilidades.
Levando em consideração que, caso um número seja composto, ele terá pelo
menos um divisor menor que a sua raiz quadrada. Assim, consideram-se esgotadas
todas as possibilidades de decomposição de um número, quando testamos todos os
valores de 2 até a sua raiz quadrada.
21
Para [i de 2 até √n ] faça:
Se (n mod i = 0) então:
Retorna COMPOSTO
Senão
i = i + 1;
retorna PRIMO;
Figura 1: Pseudocódigo para teste de primalidade;
Para efeitos de teste foram utilizados os números 83, 84 e 85 escolhidos por
apresentarem as características desejadas para a demonstração dos três casos de
complexidade.
a) Melhor Caso
Para uma entrada n=84;
Neste caso o algoritmo somente será executado uma vez, sendo que na divisão
de n por 2 já será constatado que se trata de um número composto. Esta é considerada a
complexidade de menor custo para o algoritmo em questão;
n
Operação
Operando
Resto
84
mod.
2
0
Quadro 2: Complexidade Melhor Caso para teste de primalidade;
b) Caso Médio
Para uma entrada n=85;
Neste caso o algoritmo será executado mais de uma vez, porém menos de 9
vezes (9 é a parte inteira da raiz quadrada de 85).
n
Operação
Operando
Resto
85
mod.
2
1
85
mod.
3
1
85
mod.
4
1
22
85
mod.
5
0
Quadro 3: Complexidade Caso Médio para teste de primalidade
Percebe-se que na divisão de n por 5 já será constatado que se trata de um
número composto. Esta é considerada a complexidade de médio custo para o algoritmo
em questão;
c) Pior Caso
Para uma entrada n=83;
Neste caso o algoritmo será executado 9 vezes (9 é a parte inteira da raiz
quadrada de 85), ou seja, o máximo de opções a serem testadas, mesmo levando em
consideração um n menor que na opção anterior.
n
Operação
Operando
Resto
83
mod.
2
1
83
mod.
3
2
83
mod.
4
3
83
mod.
5
3
83
mod.
6
5
83
mod.
7
6
83
mod.
8
3
83
mod.
9
2
Quadro 4: Complexidade Pior Médio para teste de primalidade
Percebe-se que na divisão de n por todos os valores possíveis, não é constatada
nenhuma divisão exata, o que indica que o n em questão é um número primo e assim
sendo, a execução do teste de primalidade é considerado de complexidade de pior caso
para o algoritmo em questão;
23
2.2. NOTAÇÕES DE COMPLEXIDADE
Para definir o custo computacional de um determinado algoritmo são utilizadas ordens
assintóticas, as quais são definidas pelo crescimento da complexidade conforme as
entradas.
O comportamento assintótico de um algoritmo é muito utilizado pelo fato de
tornar possível uma análise da complexidade do mesmo para uma entrada relativamente
grande sem ter que executá-lo.
Existem várias comparações de complexidade assintótica, mas as mais
utilizadas são as definidas pelas notações O (ó grande), Ω (Ômega) e Ө (Theta)
(TOSCANI E VELOSO, 2002).
Nos cálculos de complexidade, muitas vezes utiliza-se da simplificação, ao
passo que avaliar toda a informação de uma função pode ser muito difícil. Assim, no
contexto deste trabalho será utilizada a complexidade log n.
2.2.1. Notação O
Uma notação O define que o crescimento do custo de uma determinada função é
superior à outra função.
Considerando as seguintes funções:
f(n) = 3n;
g(n) = n2;
800
700
600
500
400
f(n)
300
g(n)
200
100
0
1
2
3
4
5
6
Figura 2: Crescimento assintótico O f(n);
24
Neste caso a função f(n) cresce mais rapidamente que a função g(n), por isso pode
afirmar que: g(n) O f(n).
2.2.2. Notação Ω
Uma notação Ω define que o crescimento do custo de uma determinada função é
inferior à outra função.
Considerando as seguintes funções:
f(n) = 5n;
g(n) = 7*n5;
60000
50000
40000
f(n)
30000
g(n)
20000
10000
0
1
2
3
4
5
6
Figura 3: Crescimento assintótico Ω f(n).
Neste caso a função g(n) cresce mais rapidamente que a função f(n), por isso pode
afirmar que: f(n) Ω g(n).
2.2.3. Notação Ө
Uma notação Ө define que o crescimento do custo de uma determinada função é igual à
outra função.
Considerando as seguintes funções:
f(n) = 6*n+4;
g(n) = 2*n+2;
25
45
40
35
30
25
f(n)
20
g(n)
15
10
5
0
1
2
3
4
5
6
Figura 4: Crescimento assintótico Ө f(n).
Neste caso a função g(n) cresce com a mesma rapidez que a função f(n), por isso pode
afirmar que: f(n) Ө g(n).
26
3. TEORIA DOS NÚMEROS
Há evidências, em registros dos antigos egípcios, de que eles tiveram
conhecimentos sobre número primos. O Papiro egípcio de Ahmes ou de Rhind, de cerca
de 1650 A.C, era um papiro onde um escriba de nome Ahmes ensinava as soluções de
85 problemas de aritmética e geometria. Mais tarde, no século XIX, este papiro foi
encontrado pelo egiptólogo inglês Rhind. Curiosamente, neste papiro, os números
primos foram escritos de forma diferente dos números compostos (IFRAH, 1997).
Percebe-se que a história dos números é muito antiga e está contada no livro A
História Universal dos Algarismos do Autor Georges Ifrah, e vinha sofrendo alterações
e melhorias ao longo dos séculos. A evolução ocorreu lentamente e em épocas
permanecia estagnada até o surgimento de uma nova linha de pensamento que trazia
novas ideias e teorias.
Por volta de 300 A.C. na Grécia Antiga surgiram os primeiros conceitos de
números primos. Euclides afirma na definição 11 do Livro VII dos Elementos: “Um
número primo é aquele que é medido apenas pela unidade” (EUCLIDES, 1944).
Na definição 13 do Livro dos Elementos, Euclides define também os números
compostos. Um número composto é aquele que é medido por algum número.
Coutinho (2004) traduz estes conceitos para os dias de hoje, e define a
expressão ‘medido por’ como ‘divisível por um numero menor do que ele próprio’.
Assim podemos definir como primos os números que não são divisíveis por nenhum
número menor que ele, exceto o 1.
3.1. NÚMEROS PERFEITOS
Os números perfeitos foram caracterizados 300 A.C. por Euclides, que definiu
um número como perfeito quando a soma de todos os seus divisores é igual ao próprio
número. Por volta do ano 600 o PE. Frances Marin Mersenne juntamente com Pierre de
Fermat definiu a classe dos números de mersenne, que são da forma 2n -1.
27
Exemplo 1:
Número 6:
Divisores: 1 + 2 + 3 = 6;
Se aplicarmos a fórmula de mersenne teremos:
FM = 2² - 1 = 3; o 2 e o 3 são números primos, portanto:
(2² - 1) * (2² - 1) = 3 * 2 = 6.
Assim 6 é um número perfeito;
Exemplo 2:
Número 28:
Divisores: 1 + 2 + 4 + 7 + 14 = 28;
Se aplicarmos a fórmula de mersenne teremos:
FM = 2³ - 1 = 7; o 3 e o 7 são números primos, portanto:
( 2³ - 1 ) * ( 2³ - 1 ) = 7 * 4 = 28.
Assim 28 é um número perfeito;
Sendo n um número primo e o resultado da equação também for um número
primo, podemos gerar um número perfeito, através da multiplicação de ambos. Portanto
temos uma relação direta dos números de mersenne com os números perfeitos, que
também é objeto de pesquisa deste trabalho.
Em artigo publicado em 2010, os pesquisadores chineses: Sibao Zhang,
Xiaocheng Ma e Lihang Zhou desenvolveram um estudo sobre a distribuição dos
números primos de mersenne, apresentando uma tabela de probabilidade de incidência
em um determinado intervalo de números.
Nesta tabela, o n é um número sequencial que define a quantidade de números
primos de mersenne. Quando o log 2 (n) é igual a n/2, pode-se definir a quantidade de
números primos de mersenne para o intervalo de 1 até n, como sendo a diferença entre
o n atual e todos os n onde obtivemos a relação de igualdade entre o log 2 (n) e n/2.
28
EXEMPLO 1:
Para n=16, temos:
Q = (log 2 (n)) – 1 = (log 2 (16)) – 1;
Q = 4 – 1 = 3;
Q
3
8
R(n) = 22 = 22 = 2 = 256;
Log 2 R(n) é igual a n/2 quando:
n = 2, 4, 8, 16 (4 valores);
número de primos de mersenne = (n – quantidade de valores);
número de primos de mersenne = 16 – 4 = 12;
Portanto, de 1 até n=16 (para 2R(n) – 1 = 2256 - 1) temos 12 números primos de
mersenne.
EXEMPLO 2:
Para n=32, temos:
Q = (log 2 (n)) – 1 = (log 2 (32)) – 1;
Q = 5 – 1 = 4;
Q
4
16
R(n) = 22 = 22 = 2 = 65536;
Log 2 R(n) é igual a n/2 quando:
n = 2, 4, 8, 16, 32 (5 valores);
número de primos de mersenne = (n – quantidade de valores);
número de primos de mersenne = 32 – 5 = 27;
Portanto, de 1 até n=32 (para 2R(n) – 1 = 265536 - 1) temos 27 números primos de
mersenne.
O quadro abaixo apresenta a lista de todos os números primos de mersenne em
seus respectivos intervalos, baseados nesta conjectura.
29
n
R(n)
P, Q
log2R(n)
n/2
n
R(n)
P, Q
1
2
P(1)
1.00000
0.50000
27
11213
P(23) 13.45288 13.50000
2
2
Q(0)
1.00000
1.00000
28
19937
P(24) 14.28316 14.00000
3
3
P(2)
1.58496
1.50000
29
21701
P(25) 14.40547 14.50000
4
4
Q(1)
2.00000
2.00000
30
23209
P(26) 14.50240 15.00000
5
5
P(3)
2.32193
2.50000
31
44497
P(27) 15.44142 15.50000
6
7
P(4)
2.80735
3.00000
32
65536
Q(4)
7
13
P(5)
3.70044
3.50000
33
86243
P(28) 16.39612 16.50000
8
16
Q(2)
4.00000
4.00000
34
110503
P(29) 16.75373 17.00000
9
17
P(6)
4.08746
4.50000
35
132049
P(30) 17.01071 17.50000
10
19
P(7)
4.24793
5.00000
36
216091
P(31) 17.72128 18.00000
11
31
P(8)
4.95420
5.50000
37
756839
P(32) 19.52963 18.50000
12
61
P(9)
5.93074
6.00000
38
859433
P(33) 19.71303 19.00000
13
89
P(10)
6.47573
6.50000
39
1257787
P(34) 20.26246 19.50000
14
107
P(11)
6.74147
7.00000
40
1398269
P(35) 20.41521 20.00000
15
127
P(12)
6.89968
7.50000
41
2976221
P(36) 21.50505 20.50000
16
256
Q(3)
8.00000
8.00000
42
3021377
P(37) 21.52677 21.00000
17
521
P(13)
9.02514
8.50000
43
6972593
P(38) 22.73326 21.50000
18
607
P(14)
9.24555
9.00000
44
13466917
P(39) 23.68292 22.00000
19
1279 P(15) 10.32080
9.50000
45
20996011
P(40) 24.32361 22.50000
20
2203 P(16) 11.10525 10.00000
46
24036583
P(41) 24.51873 23.00000
21
2281 P(17) 11.15545 10.50000
47
25964951
P(??) 24.63006 23.50000
22
3217 P(18) 11.65150 11.00000
48
30402457
P(??) 24.85768 24.00000
23
4253 P(19) 12.05427 11.50000
49
32582657
P(??) 24.95760 24.50000
24
4423 P(20) 12.11081 12.00000
50
37156667
P(??) 25.14712 25.00000
25
9689 P(21) 13.24213 12.50000
51
42643801
P(??) 25.34583 25.50000
26
9941 P(22) 13.27918 13.00000
52
43 112 609
P(??) 25.36161 26.00000
(?) Seqüência ainda não provada.
Quadro 5: Probabilidade de incidência de números perfeitos.
Fonte: (ZHANG, MA, ZHOU, 2010).
log2R(n)
n/2
16.00000 16.00000
30
Observando o quadro, fica evidente a infinitude dos números primos de
mersenne e por consequência os números perfeitos. Porém, considerando que a
incidência de números primos de mersenne apresentada no por Sibao Zhang, Xiaocheng
Ma e Lihang Zhou é uma conjectura, esta infinitude também não pode ser provada.
No entanto, pela conjectura teríamos:
Para n=64:
Q = (log 2 (n)) – 1 = (log 2 (64)) – 1;
Q = 6 – 1 = 5;
Q
5
32
R(n) = 22 = 22 = 2 = 4294967296;
Log 2 R(n) é igual a n/2 quando:
n = 2, 4, 8, 16, 32, 64 (6 valores);
número de primos de mersenne = (n – quantidade de valores);
número de primos de mersenne = 64 – 6 = 58;
4.294.967.296
Portanto, de 1 até n=64 (para 2R(n) – 1 = 2
- 1) teríamos 58 números
primos de mersenne.
Atualmente são conhecidos 47 números primos de mersenne e números
perfeitos que estão representados no quadro abaixo.
31
Ordem
1
Expoente
2
Díg. Primo Mersenne
1
Díg. Número Perfeito
1
Ano
----
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
??
??
??
??
??
??
3
5
7
13
17
19
31
61
89
107
127
521
607
1279
2203
2281
3217
4253
4423
9689
9941
11213
19937
21701
23209
44497
86243
110503
132049
216091
756839
859433
1257787
1398269
2976221
3021377
6972593
13466917
20996011
24036583
25964951
30402457
32582657
37156667
42643801
43112609
1
2
3
4
6
6
10
19
27
33
39
157
183
386
664
687
969
1281
1332
2917
2993
3376
6002
6533
6987
13395
25962
33265
39751
65050
227832
258716
378632
420921
895932
909526
2098960
4053946
6320430
7235733
7816230
9152052
9808358
11185272
12837064
12978189
2
3
4
8
10
12
19
37
54
65
77
314
366
770
1327
1373
1937
2561
2663
5834
5985
6751
12003
13066
13973
26790
51924
66530
79502
130100
455663
517430
757263
841842
1791864
1819050
4197919
8107892
12640858
14471465
15632458
18304103
19616714
22370543
25674127
25956377
---------1456
1588
1588
1772
1883
1911
1914
1876
1952
1952
1952
1952
1952
1957
1961
1961
1963
1963
1963
1971
1978
1979
1979
1982
1988
1983
1985
1992
1994
1996
1996
1997
1998
1999
2001
2003
2004
2005
2005
2006
2008
2009
2008
(?) Seqüência ainda não provada.
Quadro 6: Lista de números perfeitos
(GIMPS, 2012)
32
O maior número primo encontrado até hoje tem mais de 12 milhões de dígitos
e torna-se obvio que não seria possível armazenar este número em uma variável comum.
O fato do maior primo ser um número de mersenne, está diretamente
relacionada com o algoritmo de teste de primalidade utilizado para estes números. O
teste de primalidade de Lucas-Lehmer é de longe o mais eficiente e com menor custo
computacional, mas apenas se aplica aos números de mersenne.
Este número “especial” foi encontrado em 2009 por uma rede voluntária
através de um sistema de computação distribuída denominada: Great Internet Mersenne
Prime Search (GIMPS). Conforme publicou em seu site a Electronic Frontier
Foundation, o grupo recebeu o prêmio de $100.000,00 o qual foi distribuído entre vários
colaboradores.
No Site do GIMPS é possível se cadastrar e participar da pesquisa. Cada
membro da rede recebe números para teste de primalidade. Os números são gerados a
partir da fórmula de mersenne (2n-1) e caso sejam primos são também geradores de
números perfeitos.
No mesmo endereço eletrônico o GIMPS também confirma a inexistência de
qualquer número primo de mersenne diferente dos que já estão confirmados menores
que 224036583. Este número primo de mersenne havia sido descoberto em 2004 e em
dezembro de 2011 foi confirmado como 41º número primo de mersenne; A garantia se
dá pelo fato do grupo ter testado duas vezes cada número de mersenne menor. Portanto,
o “garimpo” continua para valores acima de 224036583.
Ainda não é conhecido nenhum número perfeito impar e conjectura-se que eles
não existam, porém a prova não é trivial.
3.2. NÚMEROS PRIMOS
Uma das definições dos números primos é que todo número inteiro maior que um, e que
não seja um número primo, pode ser escrito como um produto de números primos.
Desta forma pode-se afirmar que todo inteiro positivo não primo pode ser escrito como
um produto de números primos (SAUTOY, 2007).
33
Inteiro positivo
Fatores
2
Primo
3
Primo
4
2*2
5
Primo
6
2*3
7
Primo
8
2*2*2
9
3*3
10
2*5
11
Primo
12
2*2*3
13
Primo
14
2*7
15
3*5
Quadro 7: fatores primos de números inteiros positivos
É necessário definir um primo como sendo um produto de fator único e
assumir que o número 1 é um produto vazio ou sem fator. Cada número inteiro positivo
tem uma única representação como produto de números primos e pode também ser
identificado unicamente através de seus fatores, ou seja, um inteiro positivo pode ser
decomposto de uma única maneira em fatores primos mesmo sem considerarmos a
ordem dos fatores.
Assim pode-se afirmar que não existem dois números inteiros positivos
distintos cujas decomposições em primos sejam equivalentes. Estas afirmações foram
provadas pelos antigos gregos.
3.2.1. Aplicações, Pesquisas e Premiações.
Já na metade do século XIX, foi feito um grande progresso nas pesquisas relacionadas a
teoria dos números. Bernhard Riemann passou a abordar o problema de uma forma
completamente nova. Utilizando uma nova perspectiva, começou a compreender parte
do padrão responsável pelo caos dos primos. Havia uma harmonia sutil e inesperada
34
escondida sob o ruído externo dos primos. Apesar deste grande salto adiante, a nova
música ainda ocultava muitos de seus segredos.
Riemann foi audacioso e fez uma previsão ousada sobre a melodia misteriosa
que havia descoberto. Essa previsão ficou conhecida como a hipótese de Riemann.
Quem conseguir provar que a intuição de Riemann sobre a natureza dessa música estava
correta terá explicado por que os primos nos transmitem uma impressão tão convincente
de aleatoriedade (SAUTOY, 1965).
Riemann desenvolveu sua ideia original após descobrir um espelho matemático
através do qual era possível observar os primos. Hoje, o problema proposto em 1859
por Bernhard Riemann faz parte de um seleto grupo de problemas não resolvidos, e para
quem solucionar a apresentar a prova da Hipótese de Riemann será pago um prêmio de
um milhão de Dólares oferecido em 2000 por Clay Mathematics Institute (CLAY
MATHEMATICS INSTITUTE, 2012).
Outras pesquisas e as novas descobertas foram feitas com o passar dos anos e
com isso, os números primos vieram a ter uma relevante importância para a Ciência da
Computação. Alguns algoritmos e estruturas de dados se baseiam nos números primos
como é o caso das tabelas Hash (ROBERT, 1990).
A partir de 1970, devido ao conceito de criptografia de chave-pública,
passaram a formar a base dos primeiros algoritmos de criptografia, como exemplo,
podemos citar o algoritmo cryptosystem da RSA (SINGH, 2001).
Os números primos passaram a ser tão interessantes que começaram a ser
oferecidas premiações para que fizesse alguma nova descoberta. A Eletronic Frontier
Foundation (EFF) oferecia US$100,000 para quem encontrasse um primo de 10 milhões
de dígitos. Este número primo foi descoberto em 2009 por uma rede voluntária através
de um sistema de computação distribuída denominada: Great Internet Mersenne Prime
Search, já citada anteriormente (GIMPS, 2012).
Atualmente são oferecidos os prêmios de US$150,000 para um número primo
com mais de 100 milhões de dígitos e US$250,000 para um primo com mais de um
bilhão de dígitos. (EFF, 2012)
A RSA Factoring Challenge oferecia até US$200,000 para quem encontrasse
os fatores primos de um semi-primo ou pseudo-primo de até 2048 bits. Esse desafio foi
encerrado em 2007, e os números semi-primos fatorados podem ser encontrados na
35
página da RSA Factoring Challenge (RSA Laboratories, 2012). Mesmo assim, uma
equipe de pesquisa liderada por T. Kleinjung, obteve exito na fatoração do desafio
número RSA-768 (768 bits). Enquanto o Desafio Factoring RSA não está mais ativo, o
factoring da RSA-768 representa um marco importante para a comunidade. Os fatores
foram encontrados em 12 de dezembro de 2009, e foram apresentadaos no artigo
Factorization of a 768-bit RSA modulus, de 18 de fevereiro de 2010 (KLEINJUNG,
2010).
Fator 1
Fator 2
Número Semi-Primo
33478071698956898
36746043666799590
12301866845301177551304949583849
78604416984821269
42824463379962795
62720772853569595334792197322452
08177047949837137
26322791581643430
15172640050726365751874520219978
68568912431388982
87642676032283815
64693899564749427740638459251925
88379387800228761
73966651127923337
57326303453731548268507917026122
47116525317430877
34171433968102700
14291346167042921431160222124047
37814467999489;
92798736308917;
92747377940806653514195974598569
(116 dígitos).
(116 dígitos).
02143413; (232 dígitos).
Quadro 8: Semi-primo e fatores do desafio RSA Laboratories.
Fonte: (KLEINJUNG, 2010).
3.2.2. Infinitude dos Números Primos
Euclides ofereceu uma demonstração em sua obra Os Elementos (Livro IX, Proposição
20) que prova por absurdo que existem infinitos números primos. Esta prova é baseada
em alguns conceitos matemáticos. Um desses conceitos e a primalidade entre dois
números, ou seja, os “coprimos”.
36
PROVA 1: Coprimos são números que porventura, mesmo sendo compostos,
não tenham nenhum divisor em comum (ALENCAR FILHO, 1981).
Tendo por exemplo os números 8 e 9:
Divisores do número 8: 1, 2, 4, 8;
Divisores do número 9: 1, 3, 9;
Observa-se no exemplo que MDC de 8 e 9, é o número 1, e portanto os dois
números são coprimos.
Isto acontece com todos os números sequenciais, ou seja, n e n+1.
PROVA 2: Outra prova matemática utilizada para comprovar a primalidade
dos números primos é a decomposição de todo e qualquer número por números primos
menores, ou seja, qualquer que seja o número ele é composto pelo produto de números
primos menores, a não ser que ele próprio seja um número primo. Assim, temos
argumentos suficientes para apresentar a prova da infinitude:
Suponhamos que os números primos seja finitos e que estão representados na
lista L = {2, 3, 5, 7}
Multiplicando todos os números da lista, temos:
2*3*5*7 = 210;
Pela PROVA 1, sabemos que 211 não tem nenhum divisor em comum com o
210. E pela PROVA 2, sabemos que ele deve ser divisível por qualquer número, caso
não seja primo.
Assim, conclui-se que, se 211 não é um valor primo, tem-se pelo menos um
outro primo além dos que estão na suposta lista “finita” L = {2, 3, 5, 7}, que dividiria o
número 211. Caso não haja pelo menos um divisor primo para o número 211, ele
próprio é um número primo.
Assim, para qualquer lista supostamente finita de números primos, tem-se pelo
menos um outro número primo, e portanto, conclui-se que os números primos são
infinitos. No quadro podemos observar alguns exemplos desta prova:
37
Lista supostamente finita
Produto
Coprimo do
Fatores do
de números Primos
dos Fatores
produto
coprimo
2
2
2+1 = 3
3
2*3
6
6+1 = 7
7
2*3*5
30
30+1 = 31
31
2*3*5*7
210
210+1 = 211
211
2 * 3 * 5 * 7 * 11
2310
2310+1 = 2311
2311
2 * 3 * 5 * 7 * 11 * 13
30030
30030+1 = 30031
59, 509
2 * 3 * 5 * 7 * 11 * 13 * 17
510510
510510+1 = 510511
19, 97, 277
Quadro 9: Exemplos de prova da infinitude dos números primos.
Provada a infinitude dos números primos, tem-se a garantida à existência de
diferentes primos para a utilização na criptografia de dados, abordada na próxima seção.
3.2.3. Criptografia RSA
Encontrar números primos com 100 algarismos parece ser algo inteiramente inútil,
embora a maioria das pessoas reconheça que a matemática está envolvida no
desenvolvimento de tecnologia de maneira geral, poucos esperam que os números
primos possam provocar um grande efeito em suas vidas.
Entretanto, os números primos passaram recentemente ao primeiro plano do
mundo das pesquisas. Não estão mais confinados apenas aos matemáticos e a alguns
aventureiros.
Nos anos 1970, os cientistas: Ron Rivest, Adi Shamir e Leonard Adleman
revolucionaram a busca por números primos, que deixou de ser uma brincadeira para se
tornar uma importante ferramenta de negócios. Explorando uma descoberta feita por
Pierre de Fermat no século XVII, os três descobriram um modo de usar os primos para
proteger os dados trafegados pela Web através da criptografia denominada RSA, em
homenagem aos três autores.
Quando a idéia foi lançada, nos anos 1970, ninguém podia imaginar as
dimensões que as transações pela WEB alcançariam. Porém, sem a força dos números
primos, esse tipo de comércio jamais poderia existir hoje em dia. Sempre que fazemos
compras pela internet, nossos computadores utilizam um sistema de segurança que
38
depende da existência de números primos com inúmeras casas decimais (SINGH,
2001).
O Livro dos Códigos, pag. 419 (SINGH, 2001), trás um exemplo do
funcionamento da criptografia RSA para a transmissão de uma mensagem, no qual
podemos observar a cifragem e a decifragem RSA passo a passo:
PASSO 1: Escolhe-se dois números primos gigantes p e q. Para o exemplo são
escolhidos os números p = 17, e q = 11.
PASSO 2: Multiplica-se p x q para encontrar o N.
N = 11 x 17 = 187.
qq = (p-1)x(q-1)
Escolhe-se um primo relativo à qq que pode ser e = 7;
Primo relativo à qq é apenas aquele que não possui divisor em comum, ou seja,
sendo qq = 160 e possuindo vários divisores (160 possui 8 divisores: 2, 4, 8, 10, 16, 20,
40 e 80) qualquer número que não tenha nenhum destes divisores serve.
Na prática se e for também um número primo, ou seja, não possuir divisor
algum, ele será com certeza um primo relativo à qq. Assim qualquer número primo
serve como e. Atualmente o sistema de criptografia RSA fixa este número para todas as
chaves.
PASSO 3: Divulgam-se os números e e N; Estes números são a chave pública
para a encriptação dos dados e devem estar disponíveis para quem quiser cifrar uma
mensagem a ser enviada.
PASSO 4: A mensagem a ser enviada deve ser convertida em um número M.
O processo de conversão passa pela transformação da mensagem em dígitos binários e
depois em decimais, produzindo então o texto cifrado C.
C = Me (mod. N);
PASSO 5: Para enviar simplesmente a letra X como mensagem. No ASCII isto
é representado por 1011000, que equivale a 88 em decimais. Assim, M = 88.
PASSO 6: Para cifrar a mensagem, utiliza-se o N = 187, e = 7 e M = 88.
Utilizando a fórmula para cifrar, temos: C = 887 (mod. 187).
PASSO 7: Efetuando o cálculo da fórmula temos:
C = 887 = 40867559636992 = 11 (mod. 187).
Envia-se, portanto a mensagem 11.
39
PASSO 8: Para decifrar a mensagem que foi cifrada com o algoritmo de mão
única fica praticamente impossível, se levarmos em consideração que são utilizados
números primos gigantes e que os desconhecemos. Porém quem gerou a chave pública,
possui os fatores do número N, e pode decifrar a mensagem enviada através da chave
privada.
PASSO 9: A chave privada é calculada a partir da seguinte fórmula.
e x d = 1 (mod. qq);
7 x d = 1(mod. 16 x 10);
7 x d = 1(mod. 160);
d = 23;
Neste passo percebe-se que é necessário sabermos quais foram os números
primos utilizados para a geração da chave pública. Assim, se utilizarmos outros valores
não chegamos ao resultado esperado, ou seja, não se obtêm a mensagem correta que foi
enviada.
PASSO 10: Apenas neste momento conseguimos decifrar a mensagem. Com
todos os valores obtidos, aplicamos os mesmos na seguinte fórmula:
M = cd (mod 187);
M = 1123 (mod 187);
M = 895430243255237372246531 (mod. 187);
M = 88 = X na tabela ASCII.
Com este exemplo percebe-se que a função utilizada para encriptar os dados é
de mão única, podendo apenas ser revertida por alguém que tenha acesso aos dois
números primos que foram utilizados.
A função permite, porém, que qualquer um cifre mensagens para qualquer
pessoa que divulgue a chave pública, mas apenas tem acesso á mensagem correta, a
pessoa que conhece a chave privada.
40
Figura 5: Exemplo de Criptografia RSA
(PLACE, 2012)
A segurança da criptografia RSA baseia-se na grande dificuldade de fatoração
desse produto dos dois números primos gigantes. Mesmo tendo esse produto (que faz
parte da chave pública divulgada), a segurança ainda é garantida justamente pela
complexidade desta fatoração.
Para quantificar esta complexidade, podemos considerar que se um algoritmo
de busca de chave através da força bruta (busca exaustiva da chave). Um computador
executando um milhão de instruções por segundo durante um ano, levaria 30 mil anos
para efetuar a fatoração necessária no pior caso, para uma chave de 512 bits. Uma chave
assimétrica de 768 bits demandaria 200 milhões de anos; uma chave assimétrica de
1.024 bits demandaria 300 bilhões; e finalmente, uma chave de 2.048 bits exigiria 300
quinqüilhões de anos para ser quebrada (SINGH, 2001).
3.3. DERIVAÇÕES DE NÚMEROS PRIMOS
Como já foi citado na descrição geral das características e na expressão de algumas
particularidades dos números primos, obtêm-se através de inúmeras derivações,
diferentes classes destes números, cada qual com características especiais.
41
Todos os números têm como característica fundamental para ter a condição de
número primo, a divisibilidade apenas pela unidade e por ele próprio. Porém, a partir
daí vê-se uma enorme gama de particularidades de cada classe, o que facilmente poderia
levar alguém a concluir pela total falta de padrão e relação entre os mesmos.
Por isso inúmeros pesquisadores definiram diferentes algoritmos que podem
ser aplicados aos números primos, dependendo do resultado que se deseja obter.
Neste trabalho serão abordados apenas as principais derivações dos números
primos, com suas características, aplicações e algoritmos utilizados na sua
determinação, além da complexidade computacional de cada algoritmo.
3.3.1. Força Bruta
Um algoritmo de teste de primalidade por força bruta testa todas as possibilidades de
divisibilidade de um número até a sua raiz quadrada sem levar em consideração nenhum
outro método.
Neste trabalho será apresentado um algoritmo de força bruta desconsiderandose apenas os números pares, seguindo o princípio de que o único número primo par é o
número 2.
Um pseudo código do algoritmo de força bruta pode ser observado a seguir:
Para i de 3 até √n faça:
Se (n mod. i = 0) então:
Retorna composto;
Senão:
n=n+2;
Retorna primo;
Para os testes de desempenho e comparação com outros algoritmos foi
utilizado o seguinte código JAVA:
42
Figura 6: Algoritmo Força Bruta em JAVA
O teste de primalidade por força bruta e um método determinístico, porém sua
complexidade é O (2√n), considerando que a complexidade de algoritmos é calculada
com base no tamanho da entrada, que neste caso, é a quantidade de dígitos da
representação binária do número em questão, e não o próprio número (SAUTOY,
2007).
Para o teste de primalidade pelo método de força bruta temos um maior custo
computacional se comparado com qualquer outro algoritmo, e o tempo de execução
para um determinado intervalo pode ser observado no quadro abaixo.
Quantidade de números
Tempo de execução
primos no intervalo
em Milissegundos
0 a 1000000
78498
1690
0 a 2000000
148933
4153
Intervalo
43
0 a 3000000
216816
7577
0 a 4000000
283146
10764
0 a 5000000
348513
14590
0 a 6000000
412849
19035
0 a 7000000
476648
23484
0 a 8000000
539777
28041
0 a 9000000
602489
34213
0 a 10000000
664579
38813
Quadro 10: Tempo de execução do algoritmo de força bruta.
3.3.2. O Crivo de Erastóstenes
Por volta de 200 A.C. o grego Erastóstenes criou o “Crivo de Erastóstenes”, o qual
consiste basicamente em pegar uma lista de números e pelo método de exclusão retirar
os números compostos, até que sobrem somente os números primos. Este algoritmo foi
o primeiro algoritmo a ser aplicado para definir os números primos em um determinado
intervalo.
Dispõe-se os números naturais de 1 a n, onde n é o limite superior até o qual se
deseja obter os números primos, procedendo-se da seguinte maneira:
- Inicialmente, risca-se o 1, que não é primo.
- Em seguida, risca-se todos os múltiplos de 2, exceto o 2 que é primo;
- Depois de riscar todos os múltiplos do primeiro primo, passe para o próximo
primo, que é o número 3 e risque todos os múltiplos de 3, menos ele próprio.
44
Figura 7: Exemplo do Crivo de Erastóstenes até o número 101.
Percebe-se que alguns números são marcados mais de uma vez, pois são
múltiplos de mais um primo, como o número 15, múltiplo de 3 e 5. Por isso, é
importante saber em que primo p podemos parar, para evitar operações desnecessárias.
Para encontrar esse p ideal usa-se o conceito de que, se um divisor d divide um
número n, caso ele seja maior que a raiz quadrada de n, logo o seu quociente é menor
que a raiz quadrada de n. Assim precisa-se apenas utilizar os números primos menores
que a raiz quadrada de n e definir os seus múltiplos.
Através desta expressão é possível observar que para n = 50, por exemplo, é
possível parar no primo 7, pois √50 ≅ 7, ou seja, para determinar todos os números
primos menores que 50 basta riscar os múltiplos de 2, 3, 5 e 7.
Um exemplo de código JAVA para o Crivo de Erastóstenes está representado a
seguir:
45
Figura 8: Algoritmo do crivo de Erastóstenes;
Percebe-se pontos importantes no algoritmo do crivo de Erastóstenes, os quais
serão destacados a seguir.
O parâmetro recebido pelo método limite indica o limite superior, até o qual
serão definidos todos os números primos.
No primeiro laço “for” o vetor criado com tamanho igual ao limite superior é
preenchido com os valores sequenciais de 1 ao limite.
No segundo laço “for” o valor da posição atual do vetor é comparado com o
seu valor inicial, e caso o valor permaneça inalterado, significa que é um número primo.
Neste momento ele é impresso na console. E ainda dentro da condição temos outra
condição a qual limita o trabalho de exclusão dos múltiplos até a raiz quadrada do
limite. E dentro desta condição temos o terceiro laço “for”, que zera todos os múltiplos
do número primo recém-encontrado, tornando-os todos números compostos.
O crivo de Erastóstenes é um algoritmo bem interessante para intervalos de
números, porém não é indicado para teste de primalidade de um número específico, com
46
centenas de dígitos, por exemplo, pois a Complexidade de tempo cresce
exponencialmente com o número de dígitos. Portanto o Crivo de Erastóstenes tem
complexidade exponencial O (2n log n), sendo n o número a ser testado.
Abaixo, pode-se observar o tempo de execução com determinados intervalos
de números, utilizando o algoritmo proposto neste trabalho.
Quantidade de números
Tempo de execução
Probabilidade de
primos no intervalo
em Milissegundos
ocorrência
0 a 1000000
78498
64
0,0785
0 a 2000000
148933
120
0,0745
0 a 3000000
216816
194
0,0723
0 a 4000000
283146
261
0,0708
0 a 5000000
348513
319
0,0697
0 a 6000000
412849
388
0,0688
0 a 7000000
476648
471
0,0681
0 a 8000000
539777
546
0,0675
0 a 9000000
602489
619
0,0669
0 a 10000000
664579
653
0,0665
Intervalo
Quadro 11: Analise do crivo de Erastóstenes
Com o Crivo de Erastóstenes fica evidente que, na medida em que os números
vão crescendo, a chance de ser um número primo diminui, o que pode ser observado na
última coluna do quadro acima.
3.3.3. Teste de Lucas – Lehmer
O teste de Lucas-Lehmer é um teste de primalidade determinístico muito eficiente para
determinar se um número é primo, porém aplica-se somente aos números primos de
mersenne. Uma vez que é conhecido que os números de Mersenne só podem ser primos
para subscritos primos, a atenção pode ser limitada a números de Mersenne da forma,
em que é um primo ímpar (WOLFRAM MATH WORLD, 2012).
47
Um pseudo código deste algoritmo pode ser visto da seguinte forma
(LEHMER, 1981):
Teste_LucasLehmer(p):
s := 4;
PARA i de 3 até p
s = s2-2 mod. 2p-1;
SE (s = = 0) ENTAO:
2p-1 = PRIMO;
SENÃO
2p-1 = COMPOSTO;
Como exemplo prático e de fácil compreensão, consideramos p=7:
Temos: 2p – 1 = 27-1 = 127;
Figura 9: Exemplo do teste de Lucas-Lehmer
Percebe-se que temos p - 2 iterações, sendo que p (primo) é a potencia de dois.
Com isso, para definir se 2607 – 1 é primo temos apenas 605 iterações, sendo
que o número gerado por 2607 – 1 possui 183 dígitos:
Figura 10: Número gerado com p=607: 2607-1.
48
Outro fator interessante deste algoritmo é que após cada iteração trabalha-se
apenas com o resto da divisão e que possibilita a obtenção do resultado sem que
tenhamos números ainda mais colossais.
Figura 11: Algoritmo em Java do teste de Lucas-Lehmer
Empregando o teste de Lucas-Lehmer implementado em Java para o teste de
primalidade dos primeiros 20 números primos de mersenne, pode-se observar
claramente o bom desempenho do mesmo, se comparado a algoritmos de força bruta,
por exemplo.
49
Quantidade de
Tempo de execução em
dígitos
Milissegundos
2
1
≅0
2
3
1
≅0
3
5
2
≅0
4
7
3
≅0
5
13
4
≅0
6
17
6
≅0
7
19
6
≅0
8
31
10
2
9
61
19
6
10
89
27
8
11
107
33
9
12
127
39
11
13
521
157
114
14
607
183
178
15
1279
386
1931 (1,9 seg.)
16
2203
664
9717 (9,7 seg.)
17
2281
687
10642 (10,6 seg.)
18
3217
969
29838 (29,8 seg.)
19
4253
1281
69649 (1 min. 9,6 seg.)
20
4423
1332
79166 (1 min. 19,1 seg.)
Ordem
Expoente
1
Quadro 12: Análise do teste de Lucas-Lehmer
Utilizando algumas otimizações como por exemplo a Transformada Rápida de
Fourier (FFT), o custo deste algoritmo é de Õ = (p2 log p) (MERSENNE PRIMES,
2012). Este baixo custo em comparação a outros algoritmos de teste de primalidade,
juntamente às características dos números de Mersenne são os principais responsáveis
pelo fato de que os maiores primos conhecidos atualmente são primos de Mersenne.
50
3.3.4. O Teorema de Fermat
O pequeno Teorema de Fermat é um teorema relacionado aos números primos e tem
sido utilizado por outros algoritmos de descoberta de primalidade como o Monte-Carlo
e o AKS. Este teorema faz uma análise de relação entre um número primo p e um
número qualquer a.
O teorema consiste de forma resumida em verificar a congruência entre da
seguinte equação:
a p - a ≡ 0 (mod. p) ou
2 p ≡ 2 (mod. p).
Um algoritmo em Java pode ser observado na figura abaixo:
Figura 12: Algoritmo Java do Teorema de Fermat.
51
Caso a congruência seja satisfeita o número definido por p = primo1 é um
provável primo, já que esta congruência também é satisfeita por números não primos, o
que torna este algoritmo probabilístico.
Mesmo assim ele é muito utilizado pela sua eficiência para números grandes. A
probabilidade de erro do algoritmo, para um número aleatoriamente escolhido de 1024
bits é de apenas uma em 10-41, sendo um dos principais motivos para a adoção deste
teste em vários programas é que juntamente ao bom nível de confiança nos seus
resultados, este teste possui um tempo de execução polinomial de Õ (k.log 2 n) usando
exponenciação modular (SINGH, 1998). Juntamente com outros testes o algoritmo é
uma opção interessante.
3.3.5. Algoritmo AKS
Em seu artigo PRIMES is in P, Manindra Agrawal, Neeraj Kayal e Nitin Saxena
preocuparam a humanidade, supostamente pondo em risco todo e qualquer tipo de
criptografia RSA utilizado na transmissão de dados seguros pela internet. Com o teste
de primalidade de um número sendo executado em tempo polinomial, facilmente seriam
quebradas as chaves de criptografia (AGRAVAL, KAYAL e SAXENA, 2000).
Porém com um detalhamento melhor das funções que o algoritmo executa e
através de um estudo mais aprofundado deste algoritmo, viu-se que na prática ele não é
tão eficiente assim. Mesmo com o seu custo computacional polinomial o algoritmo
trabalha com uma constante expressivamente grande, o que o torna lento na prática.
A ideia do algoritmo pode ser apresentada de forma resumida em alguns passos
conforme o artigo:
- Para a verificação da primalidade de um número n:
- Decidir se n é uma potência perfeita de um número natural, ou seja, ver se n
tem raiz quadrada exata. Caso tenha então n é composto.
- Encontrar um fator primo onde este fator primo compreende um intervalo
onde certamente haverá um fator primo para n caso n seja composto;
Calcular o máximo divisor comum de a e n, para a ≤ r, se for maior do que 1
então n é composto; Neste caso o ‘a’ é incrementado para fatores primos até que seja
igual ao intervalo definido no passo anterior.
- Se n for menor ou igual a r, n é primo.
52
Por último o AKS verifica a congruência (x – a)n ≡ (xn – a) (mod. xr – 1, n),
para todo a que satisfaça a condição da estrutura de controle do laço de repetição, se
existir a congruência, n é composto. Caso contrário n primo.
Este algoritmo apresenta complexidade computacional Õ (log6 n), na sua
primeira versão (AGRAVAL, KAYAL e SAXENA, 2000). Atualmente são encontradas
outras versões com melhorias na implementação do algoritmo reduzindo a sua
complexidade.
3.3.6. Primos de Sophie Germain
Outra classe de números primos são os “primos de Sophie Germain”, que se
tornaram conhecidos pelo fato de estar provado que o teorema de Fermat é aplicável
para esta classe de números. Um número é caracterizado como primo de Sophie
Germain quando p é um número primo e 2 * p + 1 também é primo (SOPHIE
GERMAIN PRIMES: BOTH P AND 2P+1 ARE PRIME).
Todos os números primos com exceção dos números primos 2, 3 e 5, são
congruentes 1, 7, 11, 13, 17, 19, 23, 29 mod. 30, ou seja, qualquer número com resto
diferente é divisível por 2 ou 3 ou 5, ou ainda por 2 destes ou até pelos 3.
Fazendo uma analise das possibilidades:
Resto 1 módulo 30:
31 * 2 + 1 = 63 não é primo (divisível por 3);
61 * 2 + 1 = 123 não é primo (divisível por 3);
Resto 7 módulo 30:
37 * 2 + 1 = 75 não é primo (divisível por 5);
67 * 2 + 1 = 135 não é primo (divisível por 5);
Resto 13 módulo 30:
43 * 2 + 1 = 87 não é primo (divisível por 3);
73 * 2 + 1 = 147 não é primo (divisível por 3);
Resto 17 módulo 30:
47 * 2 + 1 = 95 não é primo (divisível por 5);
107 * 2 + 1 = 205 não é primo (divisível por 5);
53
Resto 19 módulo 30:
79 * 2 + 1 = 159 não é primo (divisível por 3);
109 * 2 + 1 = 219 não é primo (divisível por 3);
Resto 11 módulo 30:
11 * 2 + 1 = 23
41 * 2 + 1 = 83
Resto 23 módulo 30:
23 * 2 + 1 = 47
53 * 2 + 1 = 107
Resto 29 módulo 30:
59 * 2 + 1 = 119
89 * 2 + 1 = 179
Observando os exemplos acima podemos concluir que os únicos números
capazes de gerar números primos de Sophie Germain são:
n ≡ 11 (mod. 30) - (11, 41, 71, 101, 131, ... n);
n ≡ 23 (mod. 30) - (23, 53, 83, 113, 143, ... n);
n ≡ 29 (mod. 30) - (29, 59, 89, 119, 149, ... n);
Obviamente que nem todos os números com resto 11, 23 e 29 geram números
primos de Sophie Germain, apenas a prova de que os outros restos NUNCA o farão.
No quadro a seguir tem-se a representação dos números primos de Sophie
Germain do intervalo de 1 até 104.
54
2
113
359
683
1049
1511
2003
2399
2939
3491
3911
4733
5231
5849
6329
7043
7643
8243
9221
3
131
419
719
1103
1559
2039
2459
2963
3539
4019
4793
5279
5903
6449
7079
7649
8273
9293
5
173
431
743
1223
1583
2063
2543
2969
3593
4073
4871
5303
6053
6491
7103
7691
8513
9371
11
179
443
761
1229
1601
2069
2549
3023
3623
4211
4919
5333
6101
6521
7121
7823
8663
9419
23
191
491
809
1289
1733
2129
2693
3299
3761
4271
4943
5399
6113
6551
7151
7841
8693
9473
29
233
509
911
1409
1811
2141
2699
3329
3779
4349
5003
5441
6131
6563
7193
7883
8741
9479
41
239
593
953
1439
1889
2273
2741
3359
3803
4373
5039
5501
6173
6581
7211
7901
8951
9539
53
251
641
1013
1451
1901
2339
2753
3389
3821
4391
5051
5639
6263
6761
7349
8069
8969
9629
83
281
653
1019
1481
1931
2351
2819
3413
3851
4409
5081
5711
6269
6899
7433
8093
9029
9689
89
293
659
1031
1499
1973
2393
2903
3449
3863
4481
5171
5741
6323
6983
7541
8111
9059
9791
Quadro 13: Números primos de Sophie Germain ente 1 e 10 4.
Para definir um número como primo de Sophie Germain, precisa-se fazer o
teste de primalidade de dois números. Neste trabalho foi utilizado o seguinte algoritmo
em JAVA:
55
Figura 13: Algoritmo para o teste dos números primos de Sophie Germain
56
4. ANÁLISE E AVALIAÇÃO DE ALGORÍTMOS
Existem inúmeros algoritmos para teste de primalidade de números em específico, ou
para geração de uma lista de números primos em um determinado intervalo. Também
existem cálculos aproximados da incidência de números primos. Porém, neste trabalho
foram apresentados os cinco principais algoritmos, levando em consideração o contexto
histórico e também a aplicabilidade: Crivo de Erastóstenes, Lucas-Lehmer, Teorema de
Fermat, AKS e os primos de Sophie Germain.
Serão apresentadas algumas constatações a partir das pesquisas e testes
realizados durante todo período de estudos. Estas observações poderão porventura fazer
parte dos algoritmos existentes, visando à melhora do desempenho dos mesmos.
4.1. JAVA E IDE NETBEANS
Para o desenvolvimento deste trabalho, mas precisamente para a construção e
codificação dos algoritmos, foi utilizado a linguagem de programação Java e o
Ambiente de Desenvolvimento Integrado (IDE) NetBeans 7.2, desde a fase de
desenvolvimento dos algoritmos geradores dos números primos e perfeitos, até os testes
de primalidade e os algoritmos para a análise dos dados obtidos.
A linguagem Java foi utilizada por possuir suporte a números muito grandes
através da abstração BigInteger e por ser a linguagem utilizada pela universidade em
seu currículo acadêmico.
Na figura abaixo observamos o diagrama conceitual da linguagem JAVA,
contendo o pacote java.math e todos os métodos e funções matemáticas necessárias para
o desenvolvimento deste trabalho, inclusos na Classe BigInteger.
57
Figura 14: Diagrama Conceitual Java
Fonte: http://docs.oracle.com/javase/7/docs
4.1.1 Classe BigInteger
Todas as operações da Classe BigIntegers recebem tratamento como primitivos
de Java do tipo inteiros. BigInteger, portanto, fornece suporte a todos os operadores
primitivos Java de número inteiro, e todos os métodos de java.lang.Math. Além disso,
BigInteger fornece operações de aritmética modular, testes de primalidade
(probabilísticos), manipulação de bits, e algumas outras operações diversas.
A semântica de operações aritméticas desta classe é exatamente igual a dos
operadores inteiros Java aritméticas, como definido na especificação de linguagem Java.
Por exemplo, uma divisão por zero gera uma “ArithmeticException” e divisão de um
negativo por um positivo resulta um resto (ou zero) negativo. Os “BigIntegers” ocupam
o espaço necessário para acomodar os resultados de uma operação.
Semântica de operações lógicas bit a bit exatamente iguais aos dos operadores
de Java bit a bit inteiros. Os operadores binários (e, ou, xor) implicitamente realizam a
extensão de sinal no mais curto dos dois operandos antes de realizar a operação.
58
Realizar operações de comparação conforme comparações inteiras, análogas às
realizadas por Java relacional e operadores de igualdade.
Operações aritméticas modulares são fornecidos para calcular resíduos,
executar exponenciação, e calcular inversos multiplicativos. Estes métodos sempre
retornam um resultado não negativo.
Operações de bits operam sobre um único bit da representação de
complemento de seus operandos. Se necessário, o operando é sinal estendido para que
ele conténha o bit designado. Nenhuma das operações de um único bit pode produzir
um BigInteger com um sinal diferente do BigInteger em questão. Como eles afetam
apenas um único bit, o infinito tamanho da palavra fornece abstração para esta classe, a
qual garante que existem infinitos bits que precedem cada BigInteger.
Todos os métodos e construtores desta classe apresentam como saída a
expressão: “NullPointerException” quando passa-se uma referência de objeto nula para
qualquer parâmetro de entrada (JAVA SE DOCUMENTATION, 2012) .
4.1.2. Principais Métodos Utilizados
Além das operações aritméticas básicas, como: adição, subtração, multiplicação e
divisão; serão utilizadas algumas operações adicionais, as quais estão detalhadas a
seguir. Porém, todos os métodos estão baseados e caracterizados pela soma e subtração,
que são as operações possíveis computacionalmente, não considerando a forma como as
quais serão feitas pelo Java.
a) Soma de dois valores
public BigInteger add (BigInteger val)
Parâmetro:
Valor ao qual o atual valor BigInteger será acrescido.
Retorno:
BigInteger somado ao valor do parâmetro (this + val).
59
b) Subtração de dois valores
public BigInteger subtract (BigInteger val)
Parâmetro:
Valor ao qual o atual valor BigInteger será subraído.
Retorno:
BigInteger subtraído do valor do parâmetro (this - val).
c) Multiplicação de dois valores
public BigInteger multiply (BigInteger val)
Parâmetro:
Valor ao qual o atual valor BigInteger será multiplicado.
Retorno:
BigInteger multiplicado ao valor do parâmetro (this * val).
d) Divisão de dois valores
public BigInteger divide (BigInteger val)
Parâmetro:
Valor pelo qual o atual valor BigInteger será divido.
Retorno:
Quociente da divisão do atual BigInteger pelo valor do parâmetro (this / val).
Exceção:
Val igual a 0.
e) Exponenciação
public BigInteger pow (int exponent)
Parâmetro:
Expoente ao qual a base BigInteger está elevada.
Retorno:
BigInteger elevado ao expoente (this exponent ).
Exceção:
Expoente negativo.
60
f) Módulo de dois valores
public BigInteger remainder (BigInteger val)
Parâmetro:
Valor pelo qual o atual valor BigInteger será divido
Retorno:
Resto da divisão do atual BigInteger pelo valor do parâmetro (this / val).
Exceção:
Expoente negativo.
4.2. MÉTODO BINÁRIO PARA DIVISÃO DE GRANDES NÚMEROS
Para a divisão de números expressivamente grandes, com grande quantidade de dígitos,
formados a partir da exponenciação de base dois, foi utilizado o método de divisão
apresentado a seguir descrito por ATKIN e BERNSTEIN em 2004.
Por exemplo, para chegar-se ao resto da divisão de 2p por p*2+1, temos:
1º PASSO: Transformar o p para binário;
2º PASSO: Considerar resto inicial igual a 1;
3º PASSO: Remover o primeiro bit de p.
4º PASSO:
Caso o bit removido seja “1”, multiplica-se o valor do resto2 por 2.
Caso o bit removido seja “0” mantêm o valor do resto2.
5º PASSO:
Efetua-se a congruência do resto2 por p*2+1, atribuindo este valor ao resto;
Este procedimento deve ser efetuado até que não tenha mais bits a serem
removidos, isto é, a quantidade de bits define a quantidade de iterações.
Caso a congruência seja 1, após a realização do “loop”, o número em questão é
um número COMPOSTO, pois ele tem divisão exata.
61
Considerando p=23 para 2p - 1:
resto2
Bits Restantes
Remover bit
Multiplicação por 2
Mod. 47
1 x 1=1 *
10111
1
1x2=2
2
2 x 2=4
0111
0
-
4
4 x 4=16
111
1
16 x 2 = 32
32
32 x 32=1024
11
1
1024 x 2 = 2048
27
27 x 27=729
1
1
729 x 2 = 1458
1
Obs.: Inicia-se com resto 1.
Quadro 14: Congruência pelo método binário
Este método é bastante útil para o teste de primalidade através do teorema de
Fermat e também para encontrar pequenos fatores dos números de mersenne.
Se observarmos no quadro acima, podemos ver que temos números de tamanho
reduzido, ou seja, como é efetuada a congruência após cada iteração, podemos concluir
que este número jamais será maior que ((p*2)2)*2. Ao passo que uma exponenciação
simples geraria um valor com grandes dimensões.
Considerando o exemplo acima, com p = 23:
Valor máximo possível pelo método Binário: 4232.
Valor máximo pela exponenciação simples: 8388608;
Esta diferença se torna ainda mais visível considerando p = 607:
Valor máximo possível pelo método Binário: 2937888.
Valor máximo pela exponenciação simples:
5311379928167670986895882065524686273295931177270319231994441382004035
5986085224273916250226522928566888932948624650101534657933765270723940
9519978766587351943831270835393219031728127(183 dígitos);
4.3. CONSTATAÇÕES ATRAVÉS DE TESTES OU PROVAS MATEMÁTICAS
Os números primos apresentam inúmeras particularidades e por este motivo não existem
algoritmos capazes de determinar se um número expressivamente grande é primo ou
não em um tempo razoavelmente baixo, ou seja, não tem como calcular o próximo
62
número primo através de uma equação ou fórmula, mas sim, temos que testar cada
número pra comprovar sua primalidade.
A algoritmo “crivo de Erastóstenes” apresenta um resultado satisfatório se
considerada a intenção de apresentar todos os números primos em um determinado
intervalo de valores. Este algoritmo, pela sua forma de efetuar a seleção dos primos, não
apresenta possibilidade de melhora. Isto é, ele puramente faz as multiplicações e elimina
os produtos, e apresenta os valores que sobraram, ou seja, que não possuem divisores, e
portanto, são primos.
O algoritmo de Lucas-Lehmer é um teste determinístico muito eficiente, mas
apenas aplicável aos números de mersenne. Os maiores números primos conhecidos são
primos de mersenne justamente pelo fato de utilizar este algoritmo para o teste de
primalidade. Porém, como já foi descrito neste trabalho, o maior número primo possui
mais de 12 milhões de dígitos (GIMPS, 2012), e mesmo com o uso do algoritmo de
Lucas-Lehmer, o teste de primalidade leva muito tempo.
O único algoritmo determinístico e polinomial, que funciona para o teste de
primalidade de qualquer número, é o AKS. Porém, como já foi dito anteriormente, na
prática ele é muito lento, devido ao valor de entrada que é muito grande.
O Teorema de Fermat, como já evidencia o nome, era apenas um teorema e que
já foi comprovado que o teste de primalidade por este método não é garantido, ou seja,
nem todos os números de Fermat são primos, e portanto, é um algoritmo probabilístico.
Outra derivação dos números primos apresentada no trabalho, são os primos de
Sophie Germain, que propriamente não é um teste de primalidade, mas sim uma
definição de uma classe específica de números primos. Os números primos de Sophie
Germain apresentam uma característica interessante que foi utilizada para apresentar
algumas constatações que serão apresentadas no próximo subcapítulo.
4.3.1. Congruência prima mod. 30
Um teste básico que pode ser feito no início de cada algoritmo apresentado, é verificar a
congruência do número a ser testado pelo produto dos 3 primeiros números primos. Isto
se torna possível, pois uma congruência diferente, sempre será divisível por 2, 3 ou 5, já
que o próximo número primo é o 7 o primeiro número não primo por sua causa é o 49
que é maior que 30.
63
Podemos tomar como referência os três primeiros números primos: 2, 3 e 5. O
produto destes números é igual a: 2 x 3 x 5 = 30. Desta forma para que um número seja
um possível número primo ele deve apresentar congruência 1, 7, 11, 13, 17, 19, 23, 29
mod. 30. Ou seja, a congruência deve ser um número primo, do contrário pode-se
retornar “composto” para o número em questão.
N
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
mod. 30
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Possível Primo
Composto
x
x
x
x
x
x
x
x
x
x
x
x
x
x
Primo
x
x
Quadro 15: Congruência mod. 30 num determinado intervalo de números
64
Portanto percebemos que num intervalo de 30 valores, apenas 8 precisam ser
testados, e destes 8 apenas 3 são realmente primos. Esta ocorrência de primos diminui
na medida em que o tamanho dos números vai aumentando.
Se compararmos o tempo de execução de dois algoritmos de força bruta, e
introduzirmos este teste de congruência em um deles pode-se observar um ganho
considerável no teste de primalidade para um determinado intervalo.
Para o teste foram considerados intervalos de 30 números impares consecutivos
a partir do valor de n.
n
Congruência Prima
mod. 30 (segundos)
Força Bruta
(segundos)
Diferença
(segundos)
Custo em Relação a
Força Bruta (%)
1010
1011
1012
1013
1014
0,110
1,139
4,939
16,05
52,112
0,094
2,028
8,738
40,01
98,167
-0,016
0,889
3,799
23,96
46,055
117,0212766
56,16370809
56,52323186
40,11497126
53,08504895
Quadro 16: Congruência Prima mod. 30 x Força Bruta.
O gráfico apresenta uma visualização da eficiência deste algoritmo de força
bruta com a inclusão da congruência prima mod. 30. Ao passo que os números vão
aumentando a diferença entre os mesmos se torna cada vez mais visível.
Comparação de Desempenho
Congruência Prima mod 30
Força Bruta
Tempo em Segundos
200
150
100
50
0
10
11
12
13
Potência de Base 10 (Intervalo de 30 valores)
Figura 15: Congruência Prima mod. 30 x Força Bruta.
14
65
4.3.2. Congruência 11, 23 ou 59 mod. 60
Caso um número tenha congruência 11, 23 ou 59 mod. 60, ele pode ser testado
pelo algoritmo do teorema de Fermat, e apresentará uma grande possibilidade de ser um
número primo, caso apresente as características desejadas, isto porque, são estas
congruências que também satisfazem as características apresentadas para os primos de
Sophie Germain. O teste consiste basicamente em:
n = número a ser testado;
SE n ≡ primo (mod. 30) então:
SE (2(n-1)/2 – 1 ≡ 0 (mod. n)) então:
Retorna PROVÁVEL PRIMO;
SENÃO
Retorna COMPOSTO;
Este teste é bastante eficiente, porém é um teste probabilístico. No quadro
abaixo consta a incidência de primos e falsos primos em um determinado intervalo
proposto.
Intervalo
0 a 1000000
0 a 2000000
0 a 3000000
0 a 4000000
0 a 5000000
0 a 6000000
0 a 7000000
0 a 8000000
0 a 9000000
0 a 10000000
Quant. total
Quant. de Primos
Incidência
Falsos
Incidência
de primos
11, 23, 59 mod. 60
(%)
Primos
(%)
78498
148933
216816
283146
348513
412849
476648
539777
602489
664579
14762
18,81
0
0
27948
18,77
2
0,007156
40727
18,78
3
0,007366
53108
18,76
3
0,005649
65349
18,75
4
0,006121
77461
18,76
5
0,006455
89403
18,76
5
0,005593
101292
18,77
5
0,004936
113026
18,76
5
0,004424
124682
18,76
5
0,00401
Quadro 17: Incidência de primos e falsos primos com congruência 11, 23 e 59 mod. 60.
O quadro abaixo apresenta todos os falsos primos menores que 108
(100.000.000), com os seus respectivos divisores que os tornam números compostos.
66
Congruência mod. 60
23
11
23
11
11
23
59
11
59
23
11
11
23
23
23
Falso Primo
1325843
1441091
2748023
4469471
5016191
10610063
11081459
13338371
15139199
17134043
27271151
27380831
27808463
31436123
79786523
Divisor
499
347
479
1831
647
2351
227
3163
2383
1097
503
1511
4567
1619
2579
Quadro 18: Lista de Falsos Primos com Congruência 11, 23, 59 mod. 60.
Pode-se claramente observar na medida em que os números vão aumentando
de proporção, a probabilidade do teste falhar é cada vez menor. Na figura abaixo é
apresentada esta constatação.
Falsos Primos
90 a 100
80 a 90
Intervalo ( x 106)
70 a 80
60 a 70
50 a 60
40 a 50
Falsos Primos
30 a 40
20 a 30
10 a 20
0 a 10
0
1
2
3
4
5
Quantidade de Falsos Primos
Figura 16: Incidência de Falsos Primos em cada intervalo
67
O algoritmo utilizado para efetuar os testes de primalidade está representado
abaixo. No mesmo foram efetuadas as congruências por 60 e posteriormente o cálculo
para verificação da primalidade utilizando o método binário para obtenção da
congruência.
Figura 17: Algoritmo congruência 11, 23, 59 mod. 60.
4.3.3. Divisores de Números de Mersenne são da Forma 2kp + 1
Uma propriedade muito interessante dos números de Mersenne é que qualquer fator q
de 2p-1 deve ser da forma 2kp + 1. Além disto, caso 2kp+1 seja um número primo, ele
sempre divide 2p-1(GIMPS, 2012).
68
Para se fazer o teste de primalidade de um número de mersene utiliza-se o
algoritmo de Lucas-Lehmer. Porém para números expressivamente grandes este
algorítmo também tem seu custo computacional e pode levar dias, meses ou anos para
testar um único número.
Por esta razão pode-se incluir um teste básico no início do algoritmo de LucasLehmer, fazendo com que se deixe de testar inúmeros expoentes.
Considerando o expoente p=23, tem-se;
SUGESTÃO DE TESTE INICIAL:
SE (p*2+1 = primo) ENTÃO
223-1 É COMPOSTO;
SENÃO
TESTE DE LUCAS-LEHMER;
O teste de primalidade de (p*2+1) deve ser feito por qualquer método
determinístico, já que o segundo teste, o teste de Lucas-Lehmer será feito para todos os
números compostos. Logo, um falso primo poderia determinar um número como sendo
COMPOSTO, mesmo sendo um número PRIMO.
Com isto pode-se, por exemplo, afirmar que:
Figura 18: Exemplo de divisibilidade de um número de mersenne.
O exemplo acima gera um número extremamente grande, ou seja, o expoente p
= (2(43112609-1)-1)/2 possui mais de 12 milhões de dígitos, e a 243112609-1 é o primeiro
divisor exato deste número, isto é, gera congruência 0, considerando que o divisor é um
número primo(GIMPS, 2012).
69
4.3.4. Comparações entre os principais algoritmos:
Comparar o tempo de execução entre os algoritmos apresentados neste trabalho é uma
forma de quantificar e de evidenciar a evolução que vem ocorrendo com as pesquisas e
novas intervenções nos mesmos. Temos algoritmos distintos que se aplicam a alguma
classe de números primos e, portanto, não podem ser comparados com outros que não
apresentam esta característica.
Desta forma serão apresentadas algumas comparações entre algoritmos que
possuam características em comum.
Para todos os testes realizados neste trabalho, foi utilizado um computador com
as seguintes configurações: Intel Core 2 Duo T5550 (1.83 Ghz, 667 Mhz FSB, 2 MB L2
Cache), 2 GB DDR2.
70
a) Crivo de Erastóstenes x Força Bruta:
Intervalo
Crivo de
Força Bruta
Diferença
Erastóstenes
(milissegundos) (milissegundos) (milissegundos)
Custo em
Relação à Força
Bruta (%)
1690
1626
3,786982
4153
4033
2,889477
7577
7383
2,56038
10764
10503
2,424749
14590
14271
2,186429
19035
18647
2,03835
23484
23013
2,005621
28041
27495
1,947149
34213
33594
1,809254
38813
38160
1,682426
0 a 1000000
0 a 2000000
0 a 3000000
0 a 4000000
0 a 5000000
0 a 6000000
0 a 7000000
0 a 8000000
0 a 9000000
0 a 10000000
64
120
194
261
319
388
471
546
619
653
Quadro 19: Crivo de Erastóstenes x Força Bruta.
Comparação de Desempenho
Tempo em milissegundos
FORÇA BRUTA
CRIVO DE ERATÓSTENES
45000
40000
35000
30000
25000
20000
15000
10000
5000
0
1
2
3
4
5
6
7
intervalo de números ( x 1000000 )
Figura 19: Crivo de Erastóstenes x Força Bruta
8
9
10
71
b) Lucas-Lehmer x Força Bruta:
n para
(2n - 1)
Lucas-Lehmer
(milissegundos)
Força Bruta
(milissegundos)
2
3
5
7
13
17
19
31
61
89
1
1
1
1
1
1
1
2
6
8
1
1
1
1
1
1
1
40
796799
13716714000*
Diferença
Custo em Relação
(milissegundos) à Força Bruta (%)
0
0
0
0
0
0
0
38
796994
13716713992
100
100
100
100
100
100
100
5
0,000752823
0,0000000583
* Estimativa de tempo de execução (158,75 dias);
Quadro 20: Lucas-Lehmer X Força Bruta.
Comparação de Desempenho
LUCAS-LEHMER
FORÇA BRUTA
45
Tempo em milissegundos
40
35
30
25
20
15
10
5
0
2
3
5
7
13
17
Expoente
OBS.: Expoentes 61 e 89 não estão no gráfico, para melhor visualização;
Figura 20: Lucas-Lehmer X Força Bruta.
19
31
72
c) Teorema de Fermat x Força Bruta:
n
Teorema de Fermat
(milissegundos)
1014
2
3
5
9
14
10
15
1016
1017
3162
16869
50234
195602
537574
Custo em Relação à
Força Bruta (%)
3160
0,063251107
16866
0,017784101
50229
0,009953418
195593
0,00460118
537560
0,002604293
Quadro 21: Teorema de Fermat x Força Bruta.
Comparação de Desempenho
Teorema de Fermat
Força Bruta
600000
500000
Tempo em Milissegundos
10
18
Força Bruta
Diferença
(milissegundos) (milissegundos)
400000
300000
200000
100000
0
14
15
16
Potência de Base 10
Figura 21: Teorema de Fermat x Força Bruta.
17
18
73
5. CONSIDERAÇÕES FINAIS
Descrever a singularidade dos números primos parece pouco diante da importância que
representam para os matemáticos em relação à teoria dos números e para a segurança
computacional em relação à criptografia de dados. Porém, precisamos conhecê-los para
entendê-los, e estudá-los para conseguir saborear a sua peculiaridade.
Integrando uma seleta série de sete problemas denominados como desafios do
milênio, anunciados no dia 24 de maio de 2000, a hipótese de Riemann simboliza e
confirma esta importância.
A busca para uma explicação e a prova de uma fórmula para os números
primos já dura dois mil anos e os matemáticos estão aflitos para encontrar a resposta
desse enigma, que parece resistir a qualquer tentativa de encaixá-los em um padrão
reconhecível. Pode-se imaginar a euforia que uma descoberta ocasionaria.
Para quem aprecia números, gosta de matemática e sabe utilizar o poder de
processamento dos computadores atuais, os números primos se tornam um ótimo ramo
de pesquisa. Além disto, existem inúmeras premiações - citadas neste trabalho – para
entusiasmar ainda mais os pesquisadores.
Contudo, a complexidade do problema faz com que muitos pesquisadores
desistam sem ter ao menos conhecido toda a sua extraordinária beleza, e trabalhos como
este são escassos e geralmente são feitos sobre uma classe de números primos em
específico.
Neste trabalho, porém, abordou-se os números primos de uma forma mais
ampla, relatando suas principais aplicações práticas e de pesquisa, com comparações
entre os principais algoritmos estudados, inclusive com uma abordagem ampla dos
números perfeitos.
Os números perfeitos possuem uma relação estreita com os números primos de
mersenne, e o seu detalhamento se deu por este fato, além da sua magnitude e da sua
representatividade no mundo das pesquisas. Calculados pelo algoritmo de LucasLehmer, são objeto de pesquisa de inúmeros matemáticos, intrigados com as suas
particularidades.
O teste de primalidade de Lucas-Lehmer é o mais eficiente de todos os
algoritmos determinísticos existentes, porém só se aplica aos números de mersenne,
74
porém utilizando algumas técnicas é possível definir um número como primo em um
número reduzido de iterações.
Já o algoritmo para o teste de primalidade pelo teorema de Fermat se aplica a
todos os números e é mais eficiente que o teste de Lucas-Lehmer, porém é
probabilístico. A frequência com que define um número como sendo primo mesmo que
seja composto é bastante baixa, o que justifica a sua utilização para encontrar números
primos grandes, especialmente para a utilização nos algoritmos de criptografia de dados.
O
algoritmo
AKS
tem
complexidade
computacional
polinomial,
é
determinístico, aplica-se a todos os números, mas na prática ele se torna lento pela
utilização de uma constante muito elevada. Este algoritmo é de difícil implementação e
não foi feita a comparação de desempenho neste trabalho.
Outra classe de números primos detalhada neste trabalho são os primos de
Sophie Germain, que foram utilizados nos testes desenvolvidos com mod 60. Sua
complexidade não foi avaliada, pois não se trata de um teste de primalidade, apenas é
uma derivação dos números primos.
O Crivo de Erastóstenes foi detalhado por ser o primeiro algoritmo utilizado
para a definição de uma série de números primos em um intervalo de valores. A sua
importância é apenas histórica e a utilização deste algoritmo para teste de primalidade
de um número, não é viável.
O algoritmo de força bruta é o mais utilizado didaticamente e o de mais fácil
compreensão e entendimento. A sua prova é evidente e a sua aplicabilidade é para todos
os valores. Levando em conta estes fatores, o algoritmo de força bruta serviu de base de
comparação de desempenho entre os algoritmos.
Com as comparações, percebeu-se que, na medida em que os números crescem,
o ganho no desempenho dos algoritmos específicos em relação ao de força bruta fica
cada vez mais evidente, sendo que a utilização do mesmo é inviável para teste de
números de mais de 12 milhões de dígitos, por exemplo.
Algumas constatações importantes foram feitas e detalhadas no decorrer deste
trabalho, e ficam como sugestão de adaptação para os algoritmos existentes:
Para os números de mersenne, observou-se que o teste de Lucas-Lehmer é
muito eficiente, porém pode-se levar em consideração, que os seus divisores são da
forma 2kp + 1 (item 4.3.1) e sendo assim, caso 2kp + 1 seja um número primo, 2p-1
75
jamais será um número primo, pelos seguintes fatos: Ou p é composto, logo 2p-1
também o será; ou p é primo de Sophie Germain, e neste caso aplica-se o teorema de
Fermat.
Portanto, antes da aplicação do teste de Lucas-Lehmer poderia ser verificada a
primalidade de 2kp+1, sendo k=1 e p o expoente a ser testado. Assim para saber se 223-1
é primo, bastaria testar se o 23*1*2+1 = 47 é primo. Esta sugestão carece de maiores
estudos e uma análise.
Outra observação importante que pode ser empregada no algoritmo de teste de
primalidade, é a verificação da congruência módulo 30. Caso a congruência seja um
número composto, consequentemente o número em questão será composto. Caso
contrário, pode-se aplicar o teste de Fermat. Assim, serão testados poucos números
compostos, ou seja, grande parte dos números compostos serão descartados com estes
dois testes iniciais.
Cada detalhe pode ser muito importante para uma nova descoberta, e cada
descoberta pode significar um avanço na busca incansável da solução deste incrível
problema matemático. Cabe aos novos pesquisadores, desenvolver técnicas e pesquisas
nesta área, pois há muito por ser feito. Apenas uma classe de números primos é vasto o
suficiente para um estudo aprofundado e o desenvolvimento de uma investigação de
cada detalhe em busca de um aperfeiçoamento do mesmo.
Como sugestão de trabalhos futuros: provar a infinitude dos números primos de
mersenne; analisar o algoritmo proposto nesta conclusão referente ao teste de LucasLehmer; provar a inexistência de números perfeitos impares.
76
BIBLIOGRAFIA
AGRAVAL, Manindra; KAYAL, Neeraj; SAXENA, Nitin. Primes is in P. Indian
Institute
of
Technology
Kanpur,
Índia,
Disponível
em:
<http://www.cse.iitk.ac.in/users/manindra/algebra/primality_v6.pdf> Acesso em: 27 de
outubro de 2012
ALENCAR FILHO, Edgard de - Teoria Elementar dos Números - Livraria Nobel
S.A. - 1981.
ATKIN, A.; BERNSTEIN, D. Prime Sieves using Binary Quadratic Forms.Mathematics of Computation, Providence - Rhode Island - USA, v. 73, p. 1023-1030,
2004.
CLAY MATHEMATICS INSTITUTE. Riemann Hypothesis. Disponível em:
<http://www.claymath.org/millennium/Riemann_Hypothesis/> Acesso em: 27 de março
de 2012.
COUTINHO, S. C. Números inteiros e criptografia RSA. Segunda edição, IMPA,
2000. 213 págs.
COUTINHO, S. C. Primalidade em tempo polinomial – Uma introdução ao
algoritmo AKS. Universidade Federal do Rio de Janeiro, 2004.
CRANDALL, R; MAYER, E; Papadopoulos, J. The Twenty Fourth Fermat Number
is Composite. 2003.
Criptografia assimétrica. Disponível em:
<http://www.gta.ufrj.br/grad/07_2/delio/Criptografiaassimtrica.html> Acesso em: 11 de
setembro de 2012.
DEITEL, Paul; DEITEL, Harvey. Java: Como Programar. Traduzido por Edson
Furmankiewicz. São Paulo: Pearson Prentice Hall, 2010. 8ª Ed.Tradução de: Java: how
to program.
DOMINGUES, Hygino H. - Fundamentos de Aritmética - Atual - 1991.
EUCLIDES. Elementos de Geometria. Versão Latina de Frederico Commandino.
1944.
EFF. Record 12-Million-Digit Prime Number Nets $100,000 Prize. Disponível em:
<https://www.eff.org/press/archives/2009/10/14-0> Acesso em: 27 de março de 2012
GERSTING, Judith L. Fundamentos Matemáticos para a Ciência da Computação:
um tratamento moderno de matemática discreta. Traduzido por Valéria de Magalhães
Iorio. Rio de Janeiro: LTC, 2008. Tradução de: Mathematical structures for computer
science, 5 th Ed.
77
GIMPS - Great Internet Mersenne Prime Search.
<http://www.mersenne.org> Acesso em: 27 de março de 2012
Disponível
em:
IFRAH, Georges. História Universal dos Algarismos: a inteligência dos homens
contada pelos números e pelo cálculo. Traduzido por Alberto Muñoz e Ana Beatriz
Katinsky. Rio de Janeiro: Nova Fronteira, 1997 – 2V. Tradução de: Histoire universalle
dês chiffres.
KLEINJUNG, Thorsten; et al. Factorization of a 768-bit RSA modulus. Fevereiro de
2010.
LEHMER, D. N. "An extended theory of Lucas' functions," Ann. Math., 31 (1930) 419448. Reprinted in Selected Papers, D. McCarthy editor, v. 1, Ch. Babbage Res. Center,
St. Pierre, Manitoba Canada, pp. 11-48 (1981).
MERSENNE PRIMES: History, Theorems and Lists. Disponível
<http://primes.utm.edu/mersenne/index.html> Acesso em: 27 de março de 2012.
em:
ORACLE AND/OR ITS AFFILIATES. Java SE Documentation. Disponível em:
<http://docs.oracle.com/javase/7/docs> Acesso em: 24 de agosto de 2012.
Perfect numbers. Disponível em: < http://www.gap-system.org/~history/HistTopics/
Perfect_numbers.html> Acesso em: 27 de março de 2012.
PLACE, Ricardo Leocádio. Criptografia, assinatura digital e alguns outros
conceitos. Disponível em: <http://eltiger.wordpress.com/2008/10/12/ciptografiaassinatura-digital-e-alguns-outros-conceitos/> Acesso em: 15 de outubro de 2012.
ROBERT, Sedgewick. Algorithms in C. Addison-Wesley, 1990.
RSA
Laboratories.
Cryptographic
Challenges.
Disponível
<http://www.rsa.com/rsalabs> Acesso em: 15 de outubro de 2012.
em:
SAUTOY, du Marcus. A musica dos números primos. A história de um problema não
resolvido na matemática. Traduzido por Diego Alfaro. Rio de Janeiro: Jorge Zahar Ed.,
2007.
SINGH, Simon. O Livro dos Códigos. A Ciência do sigilo – do antigo Egito à
criptografia quântica. Tradução de Jorge Calife. – Rio de Janeiro: Record, 2001.
SINGH, Simon. O Último Teorema de Fermat. Rio de Janeiro: Record, 1998.
Sophie Germain Primes: both p and 2p+1 are prime. Disponível em:
<http://www.primesdemystified.com/sophiegermainprimes.html> Acesso em: 29 de
março de 2012
78
TOSCANI, Laira Vieira; VELOSO, Paulo A. S.. Complexidade de Algoritmos.
Instituto de Informática da UFRGS: Editora Sagra Luzzatto, 2002.
UCLA - University of California, Los Angeles. Departament of Mathematics.
Disponível em: <http://www.math.ucla.edu> Acesso em: 27 de março de 2012
WOLFRAM MATH WORLD, Lucas-Lehmer test. Disponível em:
<http://mathworld.wolfram.com/Lucas-LehmerTest.html> Acessado em: 15 de outubro
de 2012.
ZHANG, Sibao; MA, Xiaocheng; ZHOU, Lihang. Some Notes on the Distribution of
Mersenne
Primes.
Applied
Mathematics,
2010,
1,
312-315.
doi:10.4236/am.2010.14041
Published
Online
October
2010
(http://www.SciRP.org/journal/am)
Download