p - UCB

Propaganda
Uma análise comparativa entre os testes de primalidade AKS e Miller-Rabin
Fernando de Farias
Universidade Católica de Brasília
Curso de Matemática
Orientador: José Eduardo Castilho
RESUMO
Os algoritmos para determinação de primalidade são classificados em duas classes de métodos: os determinísticos e
os probabilísticos. Em geral, os algoritmos probabilísticos são de tempo polinomial enquanto que os determinísticos
são de tempo exponencial. O AKS é o primeiro algoritmo determinístico a executar este teste em tempo polinomial.
Este trabalho faz uma análise entre o método AKS e o método Miller-Rabin, que é um dos métodos probabilísticos
mais utilizados. Para isto, foi feita implementação na linguagem do Maple para a análise da comparação do
desempenho de cada algoritmo.
Palavras-chave: primalidade, números primos.
1. INTRODUÇÃO
Comprovar a primalidade de um número natural significa comprovar que este número só admite
como divisores positivos ele próprio e o número um. Os números que não são primos são
conhecidos como números compostos. Todo número composto pode ser escrito de forma única
(a menos da ordem dos fatores) como o produto finito de números primos. Com isto pode-se
dizer que os números primos formam uma base para a construção dos números inteiros. Este fato
justifica o porquê dos números primos serem objeto de estudo desde a antigüidade até os tempos
atuais. Testar a primalidade com números pequenos, até que a tarefa não é das mais complicadas,
mas à medida que os números se tornam maiores, a comprovação da primalidade tornar-se muito
problemática. Isto se deve ao fato de não se conhecer um procedimento de geração de números
primos.
“A importância dos testes de primalidade se deve principalmente aos sistemas de criptografia,
onde existe uma necessidade de gerar números primos com ordem de grandeza acima de 100
dígitos, que é vital para a segurança dos criptosistemas. Os primeiros algoritmos criados para
testar a primalidade de um número remontam à Grécia antiga. Até 2002, os principais algoritmos
desenvolvidos eram enquadrados em duas grandes classes”. (BRAGA, 2002):
•
•
De tempo não-polinomial e determinísticos: Afirmam com 100% de certeza a
primalidade de um número, mas o cálculo é realizado em tempo exponencial.
Exemplos: Crivo de Eratóstenes e Adleman- Rumely, que apresentam tempo de
O (log log log n )
execução da ordem de log(n )
.
De tempo polinomial, mas não-determinísticos: A complexidade do algoritmo é
em função de um polinômio. O tempo de cálculo não 'explode' quando o número
testado é muito grande, mas não dão certeza absoluta quanto à primalidade.
Exemplos: Teste de MILLER-RABIN.
1
Entretanto no final de 2002, um professor indiano, Manindra Agrawal, e seus dois alunos, Neeraj
Kayal e Nitin Saxena, descobriram um algoritmo que está sendo chamado de AKS (iniciais de
seus nomes), que permite verificar, sem margem de erro, se um inteiro positivo é ou não primo,
em tempo polinomial.
Neste trabalho se faz uma análise dos testes probabilísticos e os determinísticos, onde são
abordados dois testes para análise comparativa: o Testes de Miller-Rabin, que é um teste
probabilístico pertencente a família Teste de Monte Carlo, com margem de erro muito pequena,
da ordem de 10 −24 , e o Teste do AKS, que é determinístico e de tempo polinomial. Os
algoritmos têm como fundamentação teórica o Pequeno Teorema de Fermat, o qual será
apresentado na próxima seção.
2. PEQUENO TEOREMA DE FERMAT
Vários algoritmos eficientes de teste de primalidade sejam determinísticos ou probabilísticos se
baseiam no Pequeno Teorema de Fermat (PTF):
Teorema 2.1 Se p é um primo e se não divide o inteiro a ( p /| a) , então:
p é primo ⇒
Demonstração:
a
p −1
≡ 1 (mod p )
Considere os p − 1 primeiros múltiplos positivos de a , isto é, os inteiros:
a, 2a, 3a, ..., ( p − 1)a
Nenhum desses inteiros é congruente a 0 (mod p ) , além disso, dois quaisquer deles são
incongruentes (mod p ) , pois, se fossem:
ra ≡ sa (mod p ), 1 ≤ r < s ≤ p − 1
Então, o fator comum a poderia ser cancelado, porque o mdc (a, p ) = 1 , e teríamos:
r ≡ s (mod p ) o que é impossível, visto que 0 < s − r < p.
Assim sendo, cada um dos inteiros a, 2a, 3a, ..., ( p − 1)a é congruente (mod p ) a um único dos
inteiros 1, 2, 3, ..., p − 1 , considerados uma certa ordem, e por conseguinte multiplicando
ordenadamente todas essas p − 1 congruências por a , teremos:
a ⋅ 2a ⋅ 3a ..., ( p − 1)a ≡ 1 ⋅ 2 ⋅ 3 ⋅ ...( p − 1) (mod p )
ou seja:
a p −1 ( p − 1) ! ≡ ( p − 1) ! (mod p )
2
Como p é primo e p não divide ( p − 1) !, podemos cancelar o fator comum ( p − 1) !, o que dá a
congruência de Fermat:
a p −1 ≡ 1 (mod p )
□
Os números que satisfazem o PTF são chamados de prováveis primos. O termo prováveis se deve
ao fato de que o PTF não garante a volta. Para cada valor de a existe uma infinidade de não
primos que satisfaz esse teste os quais são chamados de pseudoprimos ou falsos primos. Como
exemplo tem-se o número de Carmichael, p = 1001152801 , que satisfaz o PTF, mas é um número
composto, pois este pode ser expresso como p = 11× 41× 61× 151× 241 .
3. ALGORITMO AKS
O algoritmo AKS ganhou destaque por ser o primeiro algoritmo publicado que é
simultaneamente polinomial, determinístico, e incondicional. O que isto significa, o tempo
máximo de processamento do algoritmo pode ser expresso como um polinômio em relação ao
número de dígitos do número analisado. Isto permite classificar o número informado como primo
ou composto ao invés de retornar um resultado probabilístico. O ponto chave do algoritmo tem
como base o seguinte Teorema:
Teorema 3.1 Suponha que a ∈ Z *p . Então p > 1 é primo se:
( x + a) p ≡ x p + a (mod p ).
Demonstração: Pelo Teorema Binomial de Newton, temos que:
p
 p
( x + a ) p = ∑  x p − j a j ,
j =0  j 
 p
Mas nos casos em que j é diferente de 1 e p , o coeficiente binomial   é divisível por p , logo
j
todos os termos intermediários desta expansão são divisíveis por p , ou seja, são concruentes a
zero modulo p .
Assim
( x + a ) p ≡ x p + a p ≡ x p + a (mod p ).
Onde na última congruência é devida ao PTF. Desta forma obtém-se o seguinte critério de
primalidade
p é primo ⇔ ( x + a ) p ≡ x p + a (mod p ), para todo a < p
3
O conceito básico do algoritmo AKS é a congruência do Teorema 3.1. Analisando a expansão
 p
p
binomial inteira de ( x + a ) , ou seja, os temos   com j ≠ 1 e p ,pode-se determinar se p é
 j
primo, pois caso todos os termos da expansão com j = 1,... p − 1 forem divisíveis por p , então p
seria primo, caso contrário teríamos p composto. Pode-se verificar esta propriedade no
Triangulo de Pascal, onde cada linha representa os coeficientes da expansão binomial.
Triangulo de Pascal
0:
1
1:
1
1
2:
1
2
1
3:
1
3
3
1
4:
1
4
6
4
1
5:
1
5
10
10
5
1
6:
1
6
15
20
15
6
1
7:
1
7
21
35
35
21
7
1
8:
1
8
28
56
70
56
28
8
1
9:
1
9
36
84
126
126
84
36
9
1
10:
1
10
45
120
210
252
210
120
45
10
1
Para p muito grande este tipo de teste é impraticável e o AKS implementa o teste, descrito a
seguir. A idéia do AKS é tornar o teste binomial rápido e ainda ser capaz de provar que o teste
responde corretamente mesmo quando p é composto. Ao invés de trabalharmos com módulo p ,
trabalhamos com módulo um polinômio x r − 1 (onde r é um primo razoavelmente pequeno). Ou
p
p
seja, no lugar de calcular ( x + a ) , calcula-se o resto da divisão de ( x + a ) por x r − 1 , que é
feito usando o mesmo método em Álgebra para dividir um polinômio por outro. Dessa maneira,
p
teremos no máximo r − 1 termos para examinar, enquanto que na expansão de ( x + a ) temos
p −1 .
Desta forma, o AKS considera a seguinte congruência:
(x + a ) p ≡ (x p + a ) (mod( x r − 1, p)) ,
(3.1)
onde (mod( x r − 1, p ) ) representa aplicar (mod x r − 1) e ao resultado aplicar (mod p ) . Como a
congruência original é satisfeita sempre que p é primo, então tem-se que a congruência (3.1)
também é satisfeita quando p é primo. A justificativa é que quando duas expressões são iguais,
4
apresentam o mesmo resto quando se divide essas duas expressões por x r − 1 . O que não é óbvio
é que a nova congruência é sempre falsa quando n é composto, pois mesmo quando
(x − a )n ≡/ (x n − a ) (mod n )
é possível que dois diferentes polinômios tenham o mesmo resto quando divididos por x r − 1 O
que o AKS mostra é que se p é composto, e se for escolhido o valor de r , que satisfaz as
condições
(3.2)
q > 2 r log p e p (r −1) / q ≡/ 1(mod r ) ,
onde q é o maior fator primo de r − 1 . Desta forma basta testar apenas um número pequeno de
a até encontrarmos um tal que
(x − a ) p ≡/ x p − a mod x r − 1, p .
□
(
)
(
)
Uma vez que se encontra um valor de a que satisfaz a relação acima, prova-se que p é
composto. O valor de a deve estar no intervalo entre 1 e 2 r log p , (BRAGA, 2002).
Algoritmo AKS
1. Entrada p > 1
2. Se p = a b com b > 1 , retorna COMPOSTO
3. r ← 2
4. Enquanto (r < p ) {
5. Se mdc (r , p ) ≠ 1 , retorna COMPOSTO
6. Se r é primo{
7.
Encontre q o maior divisor primo de r − 1
Se q > 2 r log p e p (r −1) / q ≡/ 1(mod r ) vai para 10.}
8.
9. r ← r + 1 }
10. Para a = 1 até 2 r log p faça
11. Se (x + a ) ≡/ x p + a (mod x r − 1, p ) retoma COMPOSTO
12. retorna PRIMO
p
Os passos do algoritmo, numerados de um a nove, usam alguns conceitos algébricos que
descartam valores que não influenciam na determinação da primalidade. No passo dois verificase, se n é uma potência de um número inteiro. Isto é feito por um algoritmo em tempo polinomial.
Nos passos de três a nove procura-se os valores de r e q que satisfaçam as condições (3.2). Nos
passos dez e onze verifica-se a existência de um valor de a que não satisfaça a condição (3.1),
retornando que o número é composto. Caso contrário tem-se que o número p é primo.
5
4. TESTE DE MILLER-RABIN
“Com teste de Miller-Rabin inaugurou a classe de testes probabilísticos chamada de Testes de
Monte Carlo. Estes testes são usados largamente na maioria das funções de teste de primalidade
presentes nos softwares matemáticos, dada a sua velocidade e o grau de confiabilidade. De fato,
o esse algoritmo não verifica a primalidade de um número, a única coisa que ele detecta com
segurança é se o número é composto”. (COUTINHO, 2003). Ele faz um teste, que verifica se o
número é composto. “Caso o teste seja falho existe a probabilidade de 75% do número ser primo.
Aplica-se novamente o teste e se este for falho a probabilidade do número ser primo passa a ser
93%”. (MARTINEZ e CAMPOS, 2004). O procedimento pode ser repetido e na quinta iteração
tem-se uma probabilidade aproximada de 99.9% do número ser primo. Se o número passar no
teste, ele é composto. É importante dizer que o teste Miller-Rabin, como às vezes também é
chamado, não dá indícios sobre a fatoração prima do número n. O algoritmo tem como base o
resultado do seguinte teorema:
Teorema 4.1 Seja p ímpar e p − 1 = 2 k q com q ímpar e k ≥ 1 . Se p é primo e a ∈ Z *p , então
a q ≡ 1(mod n ) ou existe um i ∈ {0,1,..., k − 1} tal que
a 2 q ≡ 1(mod p )
i
Demonstração: Consideremos a seguinte seqüência de potencias módulo p :
a q , a 2 q ,..., a 2
k −1
q
, a2
k
q
Se p for um número primo, então pelo menos uma destas potências tem que ser congruente a 1
modulo p , pois, pelo (Teorema 2.1), temos
a2
k
q
= a p −1 ≡ 1(mod p ) .
2 q
Seja i ≥ 1 o menor expoente tal que a ≡ 1(mod p ) . Podemos escrever
i
(
) ( + 1).
− 1) ou p | (a
+ 1). Sendo i o menor
Como p é primo e p | (a − 1) , então ou p | (a
− 1) não é divisível por p . Segue que
expoente tal que (a − 1) é divisível por p , então (a
+ 1), isto é, a − 1 ≡ −1 (mod p ) .
p divide (a
a2 q − 1 = a2
i
i −1
− 1 ⋅ a2
2i −1 q
2i q
2i q
6
i −1
q
2i −1 q
2i −1 q
2i q
2i −1 q
q
Concluído que se p é primo, então uma das potências da seqüência dada tem que ser congruente
a − 1(mod p ) quando i ≥ 1 . Agora, se i = 0 então a q ≡ 1(mod p ) e a esta congruência não
podemos aplicar o produto notável, pois q é ímpar.
Portanto, se p é primo, então uma das potências da seqüência é congruente a − 1 módulo p ou
□
a q ≡ 1(mod p )
Se os primos se destinarem para o uso "industrial" (por exemplo, para a encriptação RSA),
geralmente não é necessário provar sua primalidade. É suficiente saber que a probabilidade do
número ser composto é menor do que 10 24 %. Neste caso, podem-se utilizar os testes (fortes) de
primalidade provável.
Algoritmo Miller-Rabin
1. escolha a ∈ {1,..., p − 1} aleatório
2. escreva p − 1 = 2 t q, q ímpar
3. calcule sucessivamente a0 = a q (mod p ), a1 = a02 (mod p ),...a k = a k2−1 (mod p ) até que
k = t ou a k ≡ 1(mod p )
4. se k = t e a k ≡/ 1(mod p ) retorne COMPOSTO
5.
senão k = 0 então retorne PRIMO
6.
senão a k −1 ≡/ −1(mod p ) então retorne COMPOSTO
7.
senão retorne PRIMO
5. ANÁLISE COMPARATIVA
Nesta seção faz-se um teste comparativo entre os algoritmos AKS e Miller-Rabin. O objetivo é
comparar a eficiência no contexto computacional. Para isto, implementou-se os algoritmos,
usando o Maple com o pacote numtheory . Os códigos e detalhes da implementação, são
apresentados em anexo. Os testes foram realizados num computador: Pentium 4 CPU 3.00 GHz,
512 MB de RAM.
TABELA 1: Analise comparativa
Tempo
Números
1001152801
1000151
4339
155121545123548965423579
514269624785214512122156
706197
AKS
0.734 s
4700 s
“ ran out of memory”
3.891 s
RABIN
0s
0.47 s
0.781 s
0.31 s
7
0.078
O primeiro número testado foi um pseudo primo, o AKS levou 0.734 para testar o número, o
RABIN com a probabilidade mais de 99,9% levou 0 segundos fazer a observação de que este
zero significa tempo menor que 0.001 comparando com o tempo do AKS, ambos verificam que o
número não é primo.
Segundo é um primos de 7 dígitos, AKS levou 4700 s em média para retornar que não havia
memória suficiente para verificar sua primalidade enquanto o RABIN levou 0.47 segundos com
a probabilidade de 99,9% de esse número ser primo.
O terceiro exemplo é um número primo, para o qual o AKS levou 3.891 segundos. Neste caso o
valor de r é igual ao valor de p .
O quarto número é composto, AKS levou 0.781 segundos para retornar que o número era
composto e RABIN levou 0.31 segundos com a probabilidade desse número ser primo de menos
0.001%.
O teste de Miller-Rabin, aplicado ao número 45127, dá como verdadeiro que este seja primo. No
entanto a implementação do método AKS, apresentada neste trabalho, não conseguiu fazer a
verificação após 10 horas de processamento. Isto se deve a forma como o passo 11 do algoritmo
foi implementado.
A limitação de memória tem ocorrido no passo 11 do algoritmo. Este passo tem sido um
limitante na implementação apresentada. Neste aspecto, o Rabin tem sido mais eficiente. Foi
testado o primo de 80 dígitos, apresentado abaixo, e o Rabin respondeu como sendo primo, num
tempo de 0.52s, enquanto o AKS já está dando limitação de memória para primos com 7 dígitos.
Primo de 80 dígitos:
10011528011151515151234511521212158784512154155512121548789555895515121261321153
6. CONCLUSÃO
Em todos os casos testados verificou-se a eficiência na questão tempo de processamento e
memória no algoritmo do Miller-Rabin é superior ao algoritmo do AKS. “Na implementação do
algoritmo AKS o passo 11 calcula duplo módulo entre polinômios de grau p e r . Esta divisão
polinomial foi implementada com o uso do comando rem do pacote numtheory do Maple, A
implementação eficiente deste passo tem sido um desafio aos programadores”. (SANTOS e
ENOQUE). Apesar do número de operações ser de tempo polinomial, este tem sido o passo
significativo no cálculo do tempo total de execução. Crandall e Papadopoulos (2003) sugerem
uma nova implementação para este passo que deve melhorar a desempenho do algoritmo, mas
que não pode ser testada neste trabalho, por exigir uma linguagem de programação de baixo
nível. Outro fator que deve melhorar a “performance” do algoritmo é se fazer uma
implementação num ambiente de cálculo, que não seja simbólico. O AKS tem sua relevância do
ponto de vista matemático, mas em termos práticos o Rabin tem-se mostrado mais eficiente.
8
REFERÊNCIAS BIBLIOGRÁFICAS
BRAGA, da R. B, Algoritmo AKS primalidade de um numero em tempo polinomial, UFRJ artigo de conclusão
de curso, 11 de setembro, 2002.
COUTINHO, S. C. Uma introdução ao algoritmo AKS, Coleção Iniciação cientifica, Sociedade brasileira de
matemática. 2003
CRANDALL, R. e PAPADOPOULOS, J. On the implementation of AKS-class primality test, Advanced
Computation Group. Apple Computer e University of Maryland College Park. 2003
MARTINEZ, E. B. F e CAMPOS, M. T, Algoritmo AKS para verificação de primalidade em tempo polinomial,
2004, < http://www.cic.unb.br/~pedro/trabs/primal.htm >, Acesso em 28/08/2007.
SANTOS, P. NETO, R. X. e ENOQUE T. Uma tentativa de implementação do algoritmo de primalidade AKS.
2002, Disponível em <http://www.cic.unb.br/~pedro/trabs/primal.htm> , Acesso em 10/11/2007.
Fernando de Farias Nunes ([email protected])
Curso de Matemática, Universidade Católica de Brasília
EPCT – QS 07 – Lote 01 – Águas Claras – Taguatinga – CEP.: 72966-700
9
ANEXOS.
A1: Algoritmo AKS.
Esta implementação tem como base o código apresentado por Braga (2004). A implementação
original faz uso a função isprime, que é um procedimento probabilístico. Isto influencia nos
resultados de eficiência do algoritmo. Desta forma optou-se por substituir a função isprime pelo
Crivo de Erastóteles. Deve-se observar que nesta fase do algoritmo, o número r , que deve ser
testado pelo Crivo, é bem pequeno com relação ao número p .
# Procedimento AKS
aks := proc (n)
local r, fs, q, a;
if (NisAB(n)) then RETURN(false) fi;
r := 2;
while r < n do
if igcd(n, r) <> 1 then RETURN(false); fi; # n is composite
if crivo(r) then
if r = 2 then q := 2; else fs := factorset(r-1); q := fs[-1]; fi;
if q >= simplify(4.0*sqrt(r)*log(n)) and (n^((r-1)/q) mod r) <> 1 then
break;
fi;
fi;
if r > 2 then r := r + 2; else r:=r+1; end if;
# print(`Valor de r = `,r);
od;
print("Valor de r = ",r);
for a from 1 to simplify(2.0*sqrt(r)*log(n)) do
if ((rem((x-a)^n, x^r-1, x) mod n)<>(rem(x^n-a, x^r-1, x) mod n)) then
RETURN(false); # n is composite
fi;
od;
RETURN(true); # n is prime
end:
# Função que checa se n = a^b
NisAb := proc(n)
local b, ret, lim;
ret:=false;
lim:= trunc(evalf((log(n))/log(2)));
for b from 2 to lim while (not ret) do
if type(eval(exp(log(n)/b)),integer) then
if type(eval(root(n,b)),integer) then ret := true end if;
10
fi;
od;
return(ret);
end:
# Crivo de Erastóteles
crivo:=proc(n::integer)
local k;
k:=3;
while (k < sqrt(n*1.0)) do
if n mod k = 0 then return false; end if;
k:=k+2;
end do;
return true;
end:
A2: Algoritmo Miller-Rabin.
# Teste de Miller-Rabim
MRtest := proc (N::integer,t::integer)
local a,d,e,j,k,s,u,f,found,randomelement;
randomelement := rand(2..N-1);
for s from 1 to t do
a := randomelement();
if N mod a = 0 then
return(false) fi;
d := igcd(a,N);
if d>1 then
return(false) fi;
e := a&^(N-1) mod N;
if not (e=1) then
return(false) fi;
# print(`Iniciando o teste de Miller-Rabin`);
u := N-1;
k := 0;
while (u mod 2 = 0) do
u := u/2;
k := k+1;
od;
f := a &^u mod N;
# print(f);
if (f=-1 mod N) or (f=1 mod N) then break fi;
11
found := false;
for j from 1 to k do
f := f &^2 mod N;
# print(f);
if f=-1 mod N then found := true; break fi;
od;
if found then break else
return(false)
fi;
od;
print(`Teste MR`,t,`vezes aplicado:`,N,` supostamente é primo`);
return(true);
end:
12
Download