Teoria Aritmética dos Números – MA553 Monografia: Um algoritmo para solução de congruências do tipo ≡ ( ). Campinas, outubro de 2007 Antonio Carlos Campello, RA 059076 Isabel Leal, RA 061533 Resumo: Nessa monografia, estudaremos alguns conceitos básicos de teoria aritmética dos números, em particular congruências e testes de primalidade de inteiros, com o objetivo de formular um algoritmo preciso para resolver a seguinte problemática: Sejam , ∈ ≡1( Introdução: . Quais são os primos , com )? +1≤ ≤ , tais que Os primeiros trabalhos em teoria aritmética dos números são datados da Índia Antiga (por volta do ano de 800 AC) e envolvem soluções inteiras para as chamadas equações diofantinas [1], contudo, os resultados mais importantes no assunto só foram demonstrados pelos gregos, por volta do século 3 antes de Cristo. Dentre os matemáticos gregos, podemos colocar em posição de destaque Euclides de Alexandria (330-260 AC) e Eratóstenes de Cirene, (276 194 AC). Euclides foi professor, matemático e escritor. Sua principal obra (e contribuição) para a matemática foram “Os elementos”, em que são estabelecidas as bases da geometria euclidiana. Em teoria de números, é possível enumerar diversos resultados notáveis (e usados até hoje) atribuidos a ele, como o algoritmo para a divisão, a demonstração da irracionalidade do número √2 (diagonal de um quadrado de lado 1) e a prova da infinitude dos números primos. Todas essa demonstrações podem ser encontradas em [1]. Eratóstenes, por sua vez, realizou sua maior contribuição para a teoria de números com a criação do chamado Crivo de Eratóstenes, que será descrito cuidadosamente a seguir. O Crivo é, até hoje, uma das formas mais eficientes para criação de listas de números primos e, portanto, tem grande aplicabilidade, por exemplo, na teoria de criptografia. Dentre outros matemáticos que estudaram teoria dos números, foram notáveis Pierre de Fermat (1601-1665 DC), Leonhard Euler (1707-1783 DC), Friedrich Gauss (1777-1855 DC), et al. 1. Crivo de Eratóstenes Def. 1.1: Se e são números inteiros, dizemos que a divide b, denotando por | , se existir um inteiro c tal que = . Def. 1.2: Dizemos que d é o máximo divisor comum de a e b (e denotamos por ( , ) = ), se d é o maior inteiro que divide a e b. Def. 1.3: Um número inteiro n (n>1) é dito primo, se possuir somente dois divisores positivos, n e 1. Se n não é primo, dizemos que ele é composto. Teorema 1.1 (fundamental da Aritmética): Todo inteiro maior do que 1 pode ser representado de maneira única (a menos da ordem) como um produto de fatores primos. Demonstração: (i) Existência: Suponhamos, por absurdo, o oposto da tese, ou seja, que existe pelo menos um inteiro maior do que 1 que não possa ser representado por fatores primos. Seja A o conjunto de todos esses números. Como A é um subconjunto dos inteiros, certamente ele possui um elemento mínimo, pelo Princípio da Boa Ordenação. Seja esse elemento. Como é maior do que 2 (já que 2 é primo, e tem fatoração em fatores primos), então existem a e b, tais que = , com < < , e como ∉ ∉ , eles possuem fatoração e, portanto, = , possui fatoração, o que absurdo, pois ∈ . Logo, A não pode ter elemento mínimo, e, portanto, = ∅, o que completa a demonstração da existência. (ii) Unicidade: Para demonstrar a unicidade, vamos lançar mão do seguinte lema, cuja demonstração pode ser encontrada em [1]: Se | … , com p primo, então divide pelo menos um fator do produto. Sejam = … = … duas fatorações de x. Perceba que os ′ não são necessariamente distintos, assim como os ’s . Da igualdade, e da definição de divisibilidade, verificamos que | … e, portanto, pelo lema, existe k tal que, | → = , já que ambos são primos. Por extensão, para qualquer < , existe um < tal que | → = . Por fim, basta provar que n=k, que é trivial, já que, se n>k, teriamos que … … = … = … , o que é absurdo, já que > 1. Ou seja, o conjunto de deve ser idêntico ao de , finalizando a demonstração da unicidade. Por (i) e (ii), está demonstrado o Teorema Fundamental da Aritmética. Teorema 1.2: Se n não é primo, então possui, necessariamente, um fator primo menor do que ou igual a √ . Demonstração: Sendo n composto, então = . , onde 0 < < e 0 < < , o que pode ser visto como uma consequência do teorema anteiror. Sem perda de generalidade, vamos supor que ≤ . Suponhamos, por absurdo, que > √ . Logo, = . > √ . √ = , chegando na desigualdade > , que é obviamente um absurdo. Portanto, ≤ √ e, pelo teorema anterior, a possui algum fator primo, que deve ser menor que a e, por conseguinte, menor que √ . Daí, concluímos que n possui um fator primo menor que √ , o que completa a demonstração. Corolário 1.1: Se um número não possui nenhum fator primo p, tal que 2 ≤ ≤ √ , então é primo. Demonstração: Segue imediatamente do teorema, aplicando-se a sua contrapositiva. O Crivo de Eratóstenes é uma consequência do Teorema 1.2 e do Corolário 1.1. Já que para verificar se um número é primo basta testar divisibilidade pelos números menores que √ , então para criar uma lista de primos até n, é suficiente ter uma lista de todos os números até n e excluir dela todos os múltiplos de primos menores que √ . Esse processo pode ser descrito pelo seguinte algoritmo: Algoritmo 1(Crivo de Eratóstenes): Entrada: inteiro positivo ímpar . Saída: lista de primos ímpares ≤ . Etapa 1: Comece criando um vetor v de ( − 1)/2 posições, cada uma das quais deve estar preenchida com o valor 1; e fazendo = 3. Etapa 2: Se > , escreva em uma lista os números 2 + 1 para os quais a j-ésima entrada do vetor v é 1 e pare; senão senão vá para a etapa 3. Etapa 3: Se a posição ( − 1)/2 do vetor v está preenchida com 0 incremente P de 2 e volte à etapa 2; senão vá para a etapa 4. Etapa 4: Atribua o valor de a uma nova variável T: substitua por zero o valor da posição ( − 1)/2 do vetor v e incremente de 2 ; repita essas duas instruções até que T > n; quando isto acontecer, incremente P de 2 e volte à etapa 2. É fácil ver que a etapa 2 do algoritmo é uma consequência imediata do Teorema 1.2 e do seu corolário. O algoritmo acima, da maneira descrita, é uma versão do Crivo de Eratóstenes que cria uma lista de todos os primos ímpares. Se quisermos criar uma lista de todos os primos, basta adicionar o 2, que é, obviamente, o único primo par. Para mais informações sobre o algoritmo acima, ver referência [3]. O algoritmo acima será uma das chaves para a solução do problema proposto. 2. Congruências A principal motivação dessa sessão é apresentar alguns conceitos e propriedades básicas de congruências e, por fim, formular um algoritmo eficiente para calcular potenciação em aritmética de módulo que será usado para, depois de encontrados os números primos p, testar se ≡ 1( ). Def.2.1: Sejam a e b números inteiros. Dizemos que a é congruente a b, ), se |( − ). Se módulo m ( > 0) e denotamos por ≡ ( ∤ ( − ), dizemos que a é incongruente a b, módulo m, e denotamos por ( ). ≢ Propriedades: Se a,b,c,d,e e m são inteiros tais que ≡ ), então as seguintes propriedades são válidas: ≡ ( 1. 2. 3. 4. 5. 6. + − ≡ + − ≡ ≡ ≡ ≡ ≡ ( ( + ( − ( + − ( ( ) ) ) ) ( ) e ) ) Enunciamos aqui algumas propriedades das congruências. A demonstração de todas elas pode ser encontradas em [2]. Vamos nos limitar à demonstração de duas delas: ) → |( − ), ou seja Demonstração (propriedade 1): Se ≡ ( |( + − − ) → |( + ) − ( + ) e, por definição, + ≡ + ( ), completando a demonstração. ) → |( − ), ou seja m também divide (Propriedade 3): Se ≡ ( ( − ) multiplicado por algum inteiro não nulo. Sendo esse inteiro , temos que |( − ) → = ( ). As demonstrações para as outras propriedades são análogas e seguem imediatamente da definição de congruência, dada em 2.1, e das propriedades básicas da divisão. Apenas com essas propriedades, é possível criar um algoritmo eficiente para calcular a forma reduzida de potencias em módulo n sem, necessariamente, ter que calcular a potência do número e depois verificar a congruência, simplificando, portanto, vários passos computacionais. Vejamos uma motivação para o algoritmo: Considere o problema de calcular a forma reduzida (com < ) de ≡ ( ). E seja = 2 + 2 + … a representação binária de , onde os coeficientes valem 0 ou 1. Logo, temos que = … … = ( ) . e assim por diante. Perceba que, pela propriedade 3, basta calcular o módulo reduzido de cada componente e multiplicar todos eles, módulo n. Além do mais, os valores de são facilmente identificáveis: 1, se o expoente “restante” for ímpar, ou zero caso contrário. A argumentação exposta acima nos conduz ao seguinte algoritmo: Algoritmo 2 Entrada: Inteiros a, e e n, onde , > 0 Saída: a forma reduzida de módulo ≥ 0. Etapa 1: Começe com A = a, P = 1 e E = e. Etapa 2: Se E = 0, retorne P. Etapa 3: Se E for ímpar, então atribua a P o valor do resto da divisão de AP por n e a E o valor ( − 1)/2 e vá para a etapa 5, senão vá para a etapa 4. Etapa 4: Se E for par então atribua a E o valor de /2 e vá para a etapa 5. Etapa 5: Troque A por . ( ) e vá para 2. Mais detalhes sobre o algoritmo também podem ser encontrados na referência bibliográfica [3], no seu apêndice. 3. A congruência ≡ ( ). Tendo em mente os resultados apresentados nas sessões 1 e 2, podemos, enfim, atacar o problema objetivo desta monografia, desenvolvendo um algoritmo para o cálculo de congruências do tipo ≡ 1( ), sendo + 1 ≤ ≤ , um primo e , inteiros. Antes de apresentar o algoritmo, entretanto, precisamos fazer duas modificações no Crivo de Eratóstenes, de modo que ele escreva na lista apenas os números maiores do que + 1. Para isso, basta adicionar à etapa 2 a condicional: Se 2 + 1 > + 1, escreva na lista. A outra modificação, é adicionar o número 2 ao início da lista, aumentando em 1 o seu tamanho. Vamos chamar esse novo algoritmo de Algoritmo 1’. Com essa modificação, temos ferramentas suficientes para criar um algoritmo simples capaz de resolver o problema proposto. Algoritmo 3 Entrada: Valores para a e r, conforme a descrição do problema dada acima. Saida: Uma lista com os primos que satisfazem a equação. Etapa 1: Utilize o Algoritmo 1’ para criar um vetor com todos os primos de + 1 até , que chamaremos de vetor A. Etapa 2: Crie um novo vetor B. Etapa 3: Para todos os elementos da lista A, verifique, através do Algoritmo 2, se ≡1( ). Se sim, inclua no vetor B o elemento p e passe para o próximo elemento de A. Caso contrário, apenas passe para o próximo elemento de A. Apesar da aparente simplicidade desse algoritmo, algumas considerações necessitam ser feitas a seu respeito. Em primero lugar, o algoritmo proposto envolve a chamada de outros dois algoritmos (1 e 2), o que aumenta a sua complexidade e custo computacional, tornando-o dependende da eficiência dos dois algoritmos. Em segundo, há, de maneira intrínseca ao algoritmo, a exigência de criação de um vetor A, que pode ser arbitrariamente grande, a depender da quantidade de primos distribuidos entre a+1 e r. Isso quer dizer, que o algoritmo está limitado ao tamanho máximo que um vetor pode ter em uma linguagem de programação, em algum sistema. Implementações futuras envolvendo listas ligadas, aritmética de precisão múltipla e computação algébrica melhorariam substancialmente a sua eficiência, assim como diminuiriam a sua dependência de uma memória alocada. Além disso, vemos que o vetor B não possui um tamanho pré-determinado. Implementações para o algoritmo, deveriam envolver alocação dinâmica para a sua criação, já que, a prori, não é sabida nenhuma forma para a distribuição de primos que satisfazem a propriedade a seguir. Por fim, a implementação das idéias aqui apresentadas foi feita na linguagem C e baseou-se fielmente nos algoritmos propostos nessa monografia. Além das soluções triviais (para a = 1), encontramos a solução para a = 3 e p = 11, que verifica a seguinte relação de congruência: 3 ≡ 1 ( 11 ). Referências [1] Niven, I. et al, An Introduction to the Theory of Numbers, Fifth Edition. [2] Santos, J.P.O, Introdução à Teoria dos Números, IMPA 2003. [3] Coutinho, S.C, Números Inteiros e Criptografia RSA, IMPA 2005.