Anda di halaman 1dari 92

ALGORITMOS EN TEORA DE

NMEROS Y ALGEBRA.
Implementacin en Java.

Walter Mora F.
Centro de Recursos Virtuales - CRV
Revista digital Matemtica, Educacin e Internet
Escuela de Matemtica
Instituto Tecnolgico de Costa Rica.
CONTENIDO

1 ALGORITMOS EN TEORIA DE NUMEROS. 1


1.1 Mximo Comn Divisor. 1
1.1.1 Introduccin. 1
1.1.2 Algoritmo e Implementacin. 3
1.2 Mnimo comn Mltiplo. 4
1.3 Algoritmo extendido de Euclides. 4
1.3.1 Algoritmo e Implementacin. 6
1.4 Inversos multiplicativos en Zm 7
1.4.1 Algoritmo. 8
1.5 Raz Cuadrada para BigInteger 8
1.5.1 Introduccin. 8
1.5.2 Algoritmo e Implementacin. 8
1.6 Criba de Eratstenes: Cmo colar nmeros primos. 10
1.6.1 Introduccin 10
1.6.2 Algoritmo e Implementacin. 12
1.6.3 Primos entre m y n. 15
Ejercicios 16
1.7 Factorizacin por ensayo y error. 17
1.7.1 Introduccin 17
1.7.2 Probando con una progresin aritmtica. 17
iii
iv CONTENIDO

1.7.3 Algoritmo. 18
1.7.4 Implementacin en Java. 20
1.8 Mtodo de factorizacin rho de Pollard. 23
1.8.1 Introduccin. 23
1.8.2 Algoritmo e Implementacin. 24
Ejercicios 28
1.9 Pruebas de Primalidad. 28
1.9.1 Introduccin. 28
1.9.2 Prueba de primalidad de Miller Rabin. 28
1.9.3 Algoritmo e Implementacin. 31
Ejercicios 33
1.10 Algoritmo Chino del Resto. 33
1.10.1 Algoritmo e Implementacin. 35
1.11 Implementacin de una clase BigRational. 39
1.11.1 Aproximacin Racional de un Nmero Real 39
1.11.2 Algoritmo. 40
Ejercicios 41
1.11.3 Implementacin de la Clase en Java 41

2 ALGORITMOS EN ALGEBRA. 47
2.1 Una clase para Operaciones con Polinomios 47
2.1.1 Operaciones Bsicas en Z[x] . 47
2.1.2 Evaluacin de Polinomios. Mtodo de Horner. 48
2.1.3 Algoritmo 48
2.1.4 Implementacin de la Clase BPolinomio. 48
Ejercicios 56
2.1.5 Algoritmo de Euclides. Divisin de Polinomios. 57
2.1.6 Algoritmo e Implementacin. 58
2.1.7 Implementacin en Java para el caso Q[x]. 58
Ejercicios 60
2.2 Mximo Comn Divisor (MCD) de Dos Polinomios. 60
2.2.1 Teora Preliminar 60
2.2.2 MCD en Dominios Euclidianos. Algoritmo de Euclides y
Algoritmo Extendido de Euclides. 65
2.2.3 Algoritmo e Implementacin. 66
Ejercicios 68
2.2.4 MCD en un DFU. Algoritmo Primitivo de Euclides 68
2.2.5 MCD en un DFU. Algoritmo PRS y el Algoritmo PRS
Subresultante 75
2.3 Algoritmo Heurstico. 79
2.3.1 Representacin -dica de un nmero y de un polinomio. 80
2.3.2 Reconstruccin del Mximo Comn Divisor 82
CONTENIDO v

2.4 ... 85
2.4.1 Algoritmo e Implementacin. 85
2.5 ... 85
2.5.1 Algoritmo e Implementacin. 85
2.6 ... 86
2.6.1 Algoritmo e Implementacin. 86

Referencias 86
CAPITULO 1

ALGORITMOS EN TEORIA DE NUMEROS.

1.1 MXIMO COMN DIVISOR.

1.1.1 Introduccin.

Si a y b son nmeros naturales, un divisor comn (o sea, un factor en comn) es un nmero


natural d que divide a ambos. Para obtener unicidad en la definicin del mximo comn
divisor, consideramos divisores comunes positivos.

Si d > 0 es un divisor comn de a y b entonces d a y d b, as que tenemos un nmero


finito de divisores comunes por lo que debe haber un nico divisor comn ms grande que
todos los dems.

Definicin 1.1 Si a, b Z entonces el mximo comn divisor de a y b es

MCD(a, b) = Mx{d Z+ : d|a y d|b}

Si a = b = 0, definimos MCD(0, 0) = 0.

Algoritmos en Teora de Nmeros y Algebra.. Walter Mora F. 1


Derechos Reservados c 2008 Revista digital Matemtica, Educacin e Internet (www.cidse.itcr.ac.cr)
2 ALGORITMOS EN TEORIA DE NUMEROS.

La identidad MCD(0, 0) = 0 es una extensin de la definicin (usual) que es adecuada para


algunas generalizaciones importantes (conjuntos parcialmente ordenados y retculos).

EJEMPLO 1.1

MCD(4, 8) = 4
MCD(0, a) = MCD(a, 0) = a

Para calcular el mximo comn divisor de a y b se usa el algoritmo de Euclides y este


algoritmo esta basado en el algoritmo de la divisin.

Teorema 1.1 (Algoritmo de la divisin)

Sean a, b Z con b 6= 0. Existen q, r Z tal que

a = bq + r con 0 r < |b|.

Como la divisibilidad no es afectada por los signos, vamos a establecer el algoritmo de


Euclides solo para nmeros naturales.

Teorema 1.2 (Algoritmo de Euclides)

Sean a y b son nmeros naturales, se aplica el algoritmo de la divisin de la siguiente


manera

a = bq1 + r1

b = r1 q2 + r2

r1 = r2 q3 + r3
..
.
rn2 = rn1 qn + rn

rn1 = rn qn+1 + 0

Entonces rn es el mximo comn divisor de a y b.


MXIMO COMN DIVISOR. 3

EJEMPLO 1.2

MCD(78, 32) = 2. En efecto;

78 = 32 2 + 14

32 = 14 2 + 4

14 = 43+2

4 = 22+0

Teorema 1.3 MCD(a1 , a2 , ..., an ) = MCD(a1 , MCD(a2 , ..., an )).

1.1.2 Algoritmo e Implementacin.


En este algoritmo usamos rem(a,b) para denotar el resto de la divisin de a por b.

Algoritmo 1.1: Mximo comn divisor


Entrada: a, b N.
Salida: MCD(a, b)
1 c = |a|, d = |b| ;
2 while d 6= 0 do
3 r = rem(c, d);
4 c = d;
5 d = r;
6 return MCD(a, b) = |c|;

1.1.2.1 Implementacin en Java. En la clase BigInteger de Java viene implemen-


tado el mtodo gcd que calcula el mximo comn divisor.

El siguiente bloque de cdigo se puede usar para obtener MCD(a, b).

import java.math.BigInteger;
class MCD
{
public static void main(String[] args)
{
BigInteger a = new BigInteger("67");
4 ALGORITMOS EN TEORIA DE NUMEROS.

BigInteger b = new BigInteger("24");

//mximo comn divisor


BigInteger mcd = a.gcd(b);
System.out.println("MCD("+a+","+b+")= "+mcd);
}
}

1.2 MNIMO COMN MLTIPLO.

Definicin 1.2 Si a, b Z+ entonces el mnimo comn mltiplo de a y b es el ms pe-


queo entero m > 0 tal que a|m y b|m. Se escribe MCM(a, b) = m

Teorema 1.4 Si a, b son enteros no ambos nulos, MCD(a, b) MCM(a, b) = ab, es decir

ab
MCM(a, b) = .
MCD(a, b)

Teorema 1.5 MCM(a1 , a2 , ..., an ) = MCM(a1 , MCM(a2 , ..., an )).

1.3 ALGORITMO EXTENDIDO DE EUCLIDES.

Teorema 1.6 (Identidad de Bzout)

Si a, b son dos enteros no ambos cero, existen s,t Z (no nicos) tales que

sa + tb = MCD(a, b)

La ecuacin sa + tb = MCD(a, b) no tiene solucin nica para s,t enteros. Se puede


obtener una solucin despejando los residuos, en el algoritmo de Euclides, y haciendo una
sustitucin hacia atrs.
ALGORITMO EXTENDIDO DE EUCLIDES. 5

EJEMPLO 1.3

mcd(78, 32) = 2. En efecto;

78 = 32 2 + 14

32 = 14 2 + 4

14 = 43+2

4 = 22+0

De acuerdo a la identidad d Bzout, existen s,t Z tal que s 78 + t 32 = 2. En


este caso, una posibilidad es 7 78 17 32 = 2, es decir s = 7 y t = 17.

s y t se obtuvieron as: primero despejamos los residuos en el algoritmo de Euclides


de abajo hacia arriba, iniciando con el mximo comn divisor,

78 = 32 2 + 14 14 = 78 32 2

32 = 14 2 + 4 4 = 32 14 2 _

14 = 4 3 + 2 2 = 14 4 3 _

4 = 22+0

Ahora hacemos sustitucin hacia atrs, sustituyendo las expresiones de los residuos.

En cada paso se ha subraya el residuo que se sustituye

2 = 14 4 3

= 14 (32 14 2)3

= 14 7 32 3

= (78 32 2)7 32 3

= 7 78 17 32
6 ALGORITMOS EN TEORIA DE NUMEROS.

1.3.1 Algoritmo e Implementacin.

Algoritmo 1.2: Algoritmo Extendido de Euclides


Entrada: a, b enteros, b 6= 0.
Salida: MCD(a, b), t y s
1 c = |a|, d = |b| ;
2 c1 = 1, d1 = 0 ;
3 c2 = 0, d2 = 1 ;
4 while d 6= 0 do
q = quo(c, d), r = c qd,
r1 = c1 qd1 , r2 = c2 qd2 ,
c = d, c1 = d1 , c2 = d2 ,
5 d = r, d1 = r1 , d2 = r2 ,
6 return gcd = |c|, s = c1 /sgn(a) sgn(c), t = c2 /sgn(b) sgn(c) ;


1 si x0
En este algoritmo, sgn(x) = .
1 si x<0

1.3.1.1 Implementacin en Java. Como ejemplo, creamos una clase con dos mto-
dos: signo y el mtodo mcdYst que devuelve el arreglo (mcd, s, t). El mtodo signum
de la clase BigInteger devuelve 0 si el nmero es el cero de esta clase. Aqu implementa-
mos un mtodo signo propio.

En la clase BigInteger, el mtodo divide devuelve el cociente de la divisin.

import java.math.BigInteger;
class mcdst
{
mcdst(){ }

//M\etodo signo
static int signo(BigInteger k)
{
if(k.signum()>=0 )
return 1;
else return -1;
}

// devuelve (mcd, s, t)
static BigInteger[] mcdYst(BigInteger a, BigInteger b)
{
BigInteger[] salida1 = new BigInteger[3];
BigInteger c,d,c1,d1,c2,d2,q,r,r1,r2,s,t;
c = a.abs();
d = b.abs();
INVERSOS MULTIPLICATIVOS EN ZM 7

c1 = BigInteger.ONE;
d1 = BigInteger.ZERO;
c2 = BigInteger.ZERO;
d2 = BigInteger.ONE;

while(d.compareTo(BigInteger.ZERO)!=0)
{
q = c.divide(d);//quo(c,d)
r = c.add(q.multiply(d).negate());
r1= c1.add(q.multiply(d1).negate());
r2= c2.add(q.multiply(d2).negate());
c = d; c1= d1; c2= d2; d = r; d1= r1; d2= r2;
}
salida1[0]=c.abs();
salida1[1]=c1.divide(new BigInteger(""+signo(a)*signo(c)));
salida1[2]=c2.divide(new BigInteger(""+signo(b)*signo(c)));
return salida1;
}

public static void main(String[] args)


{
BigInteger a = new BigInteger("67");
BigInteger b = new BigInteger("24");
BigInteger[] vgst = new BigInteger[3];
mcdst obj = new mcdst();

vgst = gst.mcdYst(a,b);

System.out.println("("+vgst[1]+")("+a+")+"+"("+vgst[2]+")("+b+") = "+vgst[0]);
}
}

1.4 INVERSOS MULTIPLICATIVOS EN ZM

Sea a Zm . Si a y m son primos relativos, a1 existe.

Si a y m son primos relativos, existen x,t tal que xa + tm = 1. Entonces,

x = a1 a1tm
= km + a1 con k = a1t,

a1 = rem(x, m)
8 ALGORITMOS EN TEORIA DE NUMEROS.

1.4.1 Algoritmo.

Algoritmo 1.3: Inverso Multiplicativo mod m.


Entrada: a Zm
Salida: a1 mod m, si existe.
1 Calcular x,t tal que xa + tm = MCD(a, m) ;
2 if MCD(a, m) > 1 then
3 a1 mod m no existe
4 else
5 return rem (x, m).

1.5 RAZ CUADRADA PARA BIGINTEGER

1.5.1 Introduccin.


No tenemos en BigInteger un mtodo para calcular b n c . Sin embargo podemos hacer
este clculo aplicando el mtodo de Newton a la ecuacin x2 n = 0, operando con ar-
itmtica entera en todo momento. El algoritmo que sigue esta basado en el siguiente teorema

Teorema 1.7 Si n N, la frmula recursiva


1 n
xk+1 = xk + , k 0, x0 = n.
2 xk


converge a n . Adems, si |xk+1 xk | < 1 entonces xk+1 = b n c .

1.5.2 Algoritmo e Implementacin.

Este algoritmo funciona con aritmtica entera. Como la sucesin xk es decreciente a partir
de algn ndice, el algoritmo se detiene en el momento en que xk+1 = xk .

En este algoritmo, quo(a,b) es el cociente de la divisin de a por b.


RAZ CUADRADA PARA BIGINTEGER 9

Algoritmo 1.4: Raz cuadrada de un BigInteger


Entrada: N N
Salida: N N
1 if N = 1 then
2 return 1
3 if N > 1 then
4 xk = N ;
5 xk+1 = quo(N, 2) ;
6 while xk+1 < xk do
7 xk = xk+1 ;
8 xk+1 = quo(xk+1 + quo(N, xk+1 ), 2) ;
9 return xk+1 ;

1.5.2.1 Implementacin en Java.

import java.math.BigInteger;

public class BIsqrt


{
BIsqrt(){ }

BigInteger raiz(BigInteger n)
{
BigInteger DOS = new BigInteger("2");
BigInteger xkm1 = n.divide(DOS);
BigInteger xk = n;

if(n.compareTo(BigInteger.ONE)< 0)
return xkm1=n;
while(xkm1.compareTo(xk)<0)
{
xk=xkm1;
xkm1=xkm1.add(n.divide(xkm1));
xkm1=xkm1.divide(DOS);
}
return xkm1;
}
public static void main(String[] args)
{BigInteger N = new BigInteger("100798798797987987987987987987987987987987980");
BIsqrt obj = new BIsqrt();
System.out.println(""+obj.raiz(N));
}
}
10 ALGORITMOS EN TEORIA DE NUMEROS.

La salida del programa para n = 100798798797987987987987987987987987987987980


es 10039860496938589832927.

Si quisiramos usar el criterio de parada que nos da el teorema ( |xk+1 xk | < 1 ) entonces
la condicin en el while sera

while(xk.add(xkm1.negate()).compareTo(BigInteger.ONE)>0)

1.6 CRIBA DE ERATSTENES: CMO COLAR NMEROS PRIMOS.

1.6.1 Introduccin
La criba1 de Eratstenes es un algoritmo que permite colar todos los nmeros primos
menores que un nmero natural dado n, eliminando los nmeros compuestos de la lista
{2, ..., n}. Es simple y razonablemente eficiente.

Primero tomamos una lista de nmeros {2, 3, ..., n} y eliminamos de la lista los mltiplos de
2. Luego tomamos el primer entero despus de 2 que no fue borrado (el 3 ) y eliminamos
de la lista sus mltiplos, y as sucesivamente. Los nmeros que permanecen en la lista son
los primos {2, 3, 5, 7, ...}.

Recordemos que un nmero n o es primo o es divisible por un primo n . Por lo tanto,
si un nmero en el conjunto {2, 3, ..., n} no
es divisible por un primo n entonces es
primo. En lo que sigue en vez de usar d n usamos la expresin equivalente (en este
contexto) d 2 n.

EJEMPLO 1.4

Encontrar los primos menores que 30. El proceso termina cuando el cuadrado del mayor
nmero confirmado como primo es < 30.

La lista es

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
2 es primo, eliminamos todos los mltiplos de 2. La lista actualizada es

2 3 5 7 9 11 13 15 17
19 21 23 25 27 29
El siguiente nmero (despus del 2 ) es el 3. Como 32 < 30, las cosas no terminan aqu.
Eliminamos los mltiplos de 3.

1 Criba, tamiz y zaranda son sinnimos. Una criba es un herramienta que consiste de un cedazo usada para limpiar
el trigo u otras semillas, de impurezas. Esta accin de limpiar se le dice cribar o tamizar.
CRIBA DE ERATSTENES: CMO COLAR NMEROS PRIMOS. 11

La lista actualizada es

2 3 5 7 11 13 17
19 23 25 29

El siguiente nmero (despus del 3 ) es el 5. Como 52 < 30, las cosas no terminan aqu.
Eliminamos los mltiplos de 5.

La lista actualizada es

2 3 5 7 11 13 17
19 23 29

El siguiente nmero (despus del 5 ) es el 7. Como 72 > 30, El proceso termin.

Los mltiplos del k simo primo pk se eliminan desde p2k en adelante. Esto es as pues
cuando llegamos a pk ya se han eliminado 2 pk , 3 pk , ..., pk1 pk . Por ejemplo, cuando
llegamos al primo 7, ya se han eliminado los mltiplos de 2, 3 y 5, es decir, ya se han
eliminado 2 7, 3 7, 4 7, 5 7 y 6 7. Por eso iniciamos en 72 .

Hasta aqu, la implementacin podra hacerse con un arreglo booleano de tamao n. El


cdigo Java sera

int n = 100000000;
boolean[] esPrimo = new boolean[n + 1];
for (int i = 2; i <= n; i++)
esPrimo[i] = true;
for (int i = 2; i*i <= n; i++)
{
if (esPrimo[i])
{
for (int j = i; i*j <= n; j++)
esPrimo[i*j] = false;
}
}

Como los pares no son primos (excepto 2 ), solo analizamos los nmeros impares 2

n3
2i + 3 : i = 0, 1, ... = {3, 5, 7, 9, ...}
2


2 Si n3 n3
n = 2k + 1 es impar, = k 1 N. Si n = 2k es par, = k2
2 2
12 ALGORITMOS EN TEORIA DE NUMEROS.

Por cada primo p = 2i + 3 (tal que p2 < n ), debemos eliminar los mltiplos impares de p
menores que n, a saber

(2k + 1)p = (2k + 1)(2i + 3), k = i + 1, i + 2, ...

Observe que si k = i + 1 entonces el primer mltiplo en ser eliminado es

p2 = (2i + 3)(2i + 3)

Esto nos dice que para implementar el algoritmo solo necesitamos un arreglo (booleano)
de tamao (n-3)/23 . El arreglo lo llamamos EsPrimo[i], i=0,1,...,(n-3)/2.
Cada entrada del arreglo EsPrimo[i] indica si el nmero 2i + 3 es primo o no. As, si el
nmero s = 2i + 3 es primo entonces EsPrimo[(s-3)/2]=true. Por tanto, lo nico que
debemos hacer es inicializar el arreglo con todos sus valores en true y luego asignar el valor
false a las entradas que representan a los nmeros (2k + 1)(2i + 3), k = i + 1, i + 2, ... es
decir, debemos hacer la asignacin

EsPrimo[((2k+1)(2i+3) - 3)/2] = false

1.6.2 Algoritmo e Implementacin.

Algoritmo 1.5: Criba de Eratstenes


Entrada: n N
Salida: Primos entre 2 y n
1 max = [ (n 3)/2 ] ;
2 boolean esPrimo[i], i = 1, 2, ...,max;
3 for j = 1, 2, ..., max do
4 esPrimo[ j] =True;
5 i = 0;
6 while (2i + 3)(2i + 3) n do
7 k = i+1;
8 while (2k + 1)(2i + 3) n do
9 esPrimo[((2k + 1)(2i + 3) 3)/2] =False;
10 k = k + 1;
11 i = i + 1;
12 Imprimir;
13 for j = 1, 2, ..., max do
14 if esPrimo[ j] =True then
15 Imprima j

3 En Java esta divisin es entera


CRIBA DE ERATSTENES: CMO COLAR NMEROS PRIMOS. 13

1.6.2.1 Implementacin en Java. Vamos a implementar una clase que recibe el


nmero natural n > 2 y devuelve un vector con los nmeros primos n. Para colar los
nmeros compuestos usamos un arreglo

boolean [] esPrimo = new boolean[(n-3)/2].

Al final llenamos un vector con los primos que quedan.

import java.util.Vector;
public class cribaEratostenes
{
//constructor
cribaEratostenes(){ }

//mtodo que calcula la lista


Vector HacerlistaPrimos(int n)
{
Vector salida = new Vector(1);
int k = 1;
int max = (n-3)/2;
boolean[] esPrimo = new boolean[max+1];

for(int i = 0; i <= max; i++)


esPrimo[i]=true;

for(int i = 0; (2*i+3)*(2*i+3) <= n; i++)


{
k = i+1;

while( ((2*k+1)*(2*i+3)) <= n)


{
esPrimo[((2*k+1)*(2*i+3)-3)/2]=false;
k++;
}
}

salida.addElement(new Integer(2));

for(int i = 0; i <=max; i++)


{ if(esPrimo[i])
salida.addElement(new Integer(2*i+3));
}
salida.trimToSize();
return salida;
}

//Como usar esta clase?


public static void main(String[] args)
{
14 ALGORITMOS EN TEORIA DE NUMEROS.

int n = 100;
Vector lista_de_primos;
cribaEratostenes criba = new cribaEratostenes();

//usar el metodo HacerlistaPrimos


lista_de_primos = criba.HacerlistaPrimos(n);

//Cantidad de primos <= n


System.out.println("Primos <="+ n+": "+lista_de_primos.size()+"\n");
//imprimir vector (lista de primos)
for(int p = 1; p < lista_de_primos.size(); p++)
{
Integer num = (Integer)lista_de_primos.elementAt(p);
System.out.println(""+(int)num.intValue());
}
}
}

1.6.2.2 Uso de la memoria En teora, los arreglos pueden tener tamao mximo
Integer.MAX_INT = 231 1 = 2 147 483 647 (pensemos tambin en la posibilidad de un
arreglo multidimensional!). Pero en la prctica, el mximo tamao del array depende del
hardware de la computadora. El sistema le asigna una cantidad de memoria a cada apli-
cacin; para valores grandes de n puede pasar que se nos agote la memoria (veremos el
mensaje OutOfMemory Error). Podemos asignar una cantidad de memoria apropiada
para el programa cribaEratostenes.java desde la lnea de comandos, si n es muy grande.
Por ejemplo, para calcular los primos menores que n = 100 000 000, se puede usar la
instruccin

C:\usrdir> java -Xmx1000m -Xms1000m cribaEratostenes

suponiendo que el archivo cribaEratostenes.java se encuentra en C:\usrdir.

Esta instruccin asigna al programa una memoria inicial (Xmx) de 1000 MB y una memo-
ria mxima (Xms) de 1000 MB (siempre y cuando existan tales recursos de memoria en
nuestro sistema).

En todo caso hay que tener en cuenta los siguientes datos

n Primos <= n
--------------------------------------
10 4
100 25
1 000 168
10 000 1 229
CRIBA DE ERATSTENES: CMO COLAR NMEROS PRIMOS. 15

100 000 9 592


1 000 000 78 498
10 000 000 664 579
100 000 000 5 761 455
1 000 000 000 50 847 534
10 000 000 000 455 052 511
100 000 000 000 4 118 054 813
1 000 000 000 000 37 607 912 018
10 000 000 000 000 346 065 536 839

1.6.3 Primos entre m y n.

Para encontrartodos los primos entre m y n (con m < n ), eliminamos los mltiplos de
los primos nque estn entre m y n. Para hacer esto notemos que si p es un nmero
primo inferior a n y si pq es el primer mltiplo de p mayor o igual a m 1, entonces
como m 1 = pq + r (con 0 r < p ), tenemos que m 1 r + p = p(q + 1) es el primer
mltiplo de p que supera a m. Luego m 1 r + kp = p(q + k) es el ksimo mltiplo
de p que supera a m.

En resumen, los mltiplos de p ms grandes que m son

m 1 r + p, m 1 r + 2p, m 1 r + 3p, ...

siendo m 1 = pq + r con 0 r < p.

EJEMPLO 1.5

Para encontrar los


primos entre m = 10 y n = 30, debemos eliminar los mltiplos
de los primos 30 5. Es decir, los mltiplos de los primos p = 2, 3, 5.

Como 10 1 = 2 4 + 1, el 2 elimina elimina los nmeros m 1 r + kp = 8 + 2k,


es decir {10, 12, ..., 30}

Como 10 1 = 3 3 + 0, el 3 elimina los nmeros m 1 r + kp = 9 + 3k, es decir


{12, 15, 18, 21, 24, 27, 30}

Como 10 1 = 5 1 + 4, el 5 elimina los nmeros m 1 r + kp = 5 + 5k, es decir


{10, 15, 20, 25.}

Finalmente nos quedan los primos 11, 13, 17, 19, 23, 29.
16 ALGORITMOS EN TEORIA DE NUMEROS.

1.6.3.1 Algoritmo. El algoritmo que se presenta es una versin ms bien cruda. En


los ejercicios se pide una implementacin ms eficiente.

Usamos un arreglo esPrimo de tamao n m + 1. Si esPrimo[j]=True entonces


m + j es un primo en la lista. Con las restricciones impuestas en el ciclo while,
1 r + k pi {0, 1, ..., n m} as que podemos declarar esPrimo[-1-r+k pi]=False
para colar los primos.

Algoritmo 1.6: Colado de primos entre m y n.


Entrada: n, m N con m < n.
Salida: Primos entre m y n
1 Primos = una lista de primos n ;
2 for j = 0, 1, ..., n m do
3 esPrimo[ j] =True;
4 np = cantidad de primos en la lista Primos;
5 for i = 1, 2, ..., np do
6 k = 1;
7 r = residuo(m 1, Primos[i]) ;
8 if m 1 r = 0 then
9 k = 2;
10 while m 1 r + kp n do
11 esPrimo[1 r + k Primos[i]] = False;
12 k = k + 1;
13 Imprimir;
14 for j = 0, 1, ..., n m do
15 if esPrimo[ j] =True then
16 Imprima j + m

Para
que la criba opere entre m y n necesitamos tener un arreglo con todos los primos
n . Observe adems que podemos obviar los pares y trabajar con arreglos ms pequeos
de solo impares.

Observe que si m 1 r = 0 debemos empezar a eliminar con k = 2. Tambin pode-


mos empezar a eliminar desde p2 si m p2 n, para esto debemos iniciar en k =
(1 m + p2 + r)/p pues con este valor de k, m 1 r + kp = p2 .

EJERCICIOS
1.1 G. Robin (ver [Rib95]) prob en 1983 que si pn denota el nsimo primo, entonces

pn n ln(n) + n (ln ln(n) 0.9385)


FACTORIZACIN POR ENSAYO Y ERROR. 17

Use esta cota para implementar un programa que devuelva el primo pn (para n pequeo,
digamos n 2000000.
1.2 Implementar un programa para calcular los primos entre m y n con los refinamientos
que se mencionaron.

1.7 FACTORIZACIN POR ENSAYO Y ERROR.

1.7.1 Introduccin

El mtodo ms sencillo de factorizacin (y muy til) es el mtodo de factorizacin por


ensayo y error (FEE). Este mtodo va probando con los posibles divisores de n hasta
encontrar un factor de este nmero.

En vez de probar con todos los posibles divisores de n (es decir, en vez de usar fuerza bruta)
podemos hacer algunos refinamientos para lograr un algoritmo ms eficiente en el sentido
de reducir las pruebas a un conjunto de nmeros ms pequeo, en el que se encuentren los
divisores pequeos de n.

1.7.2 Probando con una progresin aritmtica.

Como estamos buscando factores pequeos de n, podemos usar el siguiente teorema,

Teorema 1.8

Si n Z+ admite la factorizacin n = ab, con a, b Z+ entonces a n o b n.


Prueba. Procedemos por contradiccin, si a > n y b> n entonces ab > n n = n
lo cual, por hiptesis, no es cierto.

Del teorema anterior se puede deducir que


Si n no tiene factores d con 1 < d n , entonces n es primo.


Al menos uno de los factores de n es menor que n(no necesariamente todos). Por
ejemplo 14 = 2 7 solo tiene un factor menor que 14 3.74166).
18 ALGORITMOS EN TEORIA DE NUMEROS.

De acuerdo al teorema fundamental de la aritmtica, Cualquier nmero natural > 1 fac-


toriza, de manera nica (excepto por el orden) como producto de primos. Esto nos dice
que la estrategia ptima de factorizacin sera probar con los primos menores que n . El
problema es que si n es muy grande el primer problema sera que el clculo de los primos
de prueba durara siglos (sin considerar los problemas de almacenar estos nmeros).

Recientemente (2005) se factoriz un nmero de 200 cifras4 (RSA-200). Se tard cerca


de 18 meses en completar la factorizacin con un esfuerzo computacional equivalente a 53
aos de trabajo de un CPU 2.2 GHz Opteron.

1.7.3 Algoritmo.

Identificar si un nmero es primo es generalmente fcil, pero factorizar un nmero (grande)


arbitrario no es sencillo. El mtodo de factorizacin de un nmero N probando con di-
visores primos (trial division) consiste en probar dividir N con primos pequeos. Para
esto se debe previamente almacenar una tabla suficientemente grande de nmeros primos
o generar la tabla cada vez. Como ya vimos en la criba de Eratstenes, esta manera de
proceder trae consigo problemas de tiempo y de memoria. En realidad es ms ventajoso
proceder de otra manera.

Para hacer la pruebas de divisibilidad usamos los enteros 2, 3 y la sucesin 6k 1, k =


1, 2, ... .

Esta eleccin cubre todos los primos e incluye divisiones por algunos nmeros com-
puestos (25,35,...) pero la implementacin es sencilla y el programa suficientemente
rpido (para nmeros no muy grandes) que vale la pena permitirse estas divisiones
intiles.

En general, debemos decidir un lmite G en la bsqueda de divisores. Si se divide


nicamente por divisores primos G, se haran (G) G/ ln G divisiones. Si
se divide por 2 , 3 y 6k 1 se haran aproximadamente G/3 divisiones5 de las
G/3
cuales = 3/ ln G son divisiones tiles. Si G = 106 , tendramos 22%
G/ ln G
divisiones tiles. En este caso, un ciclo probando divisiones por primos nicamente
es 1/0.22 = 4.6 veces ms lento6 .

Cuando se juzga la rapidez de un programa se toma en cuenta el tiempo de corrida


en el peor caso o se toma en cuenta el tiempo promedio de corrida (costo de corrida
del programa si se aplica a muchos nmeros). Como ya sabemos (por el Teorema

4 Se trata del caso ms complicado, un nmero que factoriza como producto de dos primos (casi) del mismo
tamao.
5 Pues los nmeros naturales ( G ) son de la forma 6k + m con m {0, 1, ..., 5} y solo estamos considerando

m = 1, 5, es decir una tercera parte.


6 An si se almacena previamente una tabla de primos en forma compacta, esto consume tiempo [Rie94]
FACTORIZACIN POR ENSAYO Y ERROR. 19

de Mertens) hay un porcentaje muy pequeo de nmeros impares sin divisores G,


as que en promedio, nuestra implementacin terminar bastante antes de alcanzar
el lmite G (el peor caso no es muy frecuente) por lo que tendremos un programa
con un comportamiento deseable.

Detalles de la implementacin.

Para la implementacin necesitamos saber cmo generar los enteros de la forma


6k 1. Alternando el 1 y el 1 obtenemos la sucesin

5, 7, 11, 13, 17, 19, ...

que iniciando en 5, se obtiene alternando los sumandos 2 y 4. Formalmente, si


mk = 6k 1 y si sk = 6k + 1 entonces, podemos poner la sucesin como

7, 11, 13, ..., mk , sk , mk+1 , sk+1 , ...

Ahora, notemos que sk = mk + 2 y que mk+1 = sk + 4 = mk + 6. La sucesin es

7, 11, 13, ..., mk , mk + 2, mk + 6, mk+1 + 2, mk+1 + 6, ...

En el programa debemos probar si el nmero es divisible por 2 , por 3 y ejecutamos


el ciclo

p = 5;
While p G Do {
Probar divisibilidad por p
Probar divisibilidad por p + 2
p = p+6 }


En cada paso debemos verificar si el divisor de prueba p alcanz el lmite Mn {G, N }.
Si se quiere evitar el clculo de la raz, se puede usar el hecho de que si p > N
entonces p > N/p.
20 ALGORITMOS EN TEORIA DE NUMEROS.

Algoritmo 1.7: Factorizacin por Ensayo y Error.



Entrada: N N, G N
Salida: Un factor p G de N si hubiera.
1 p = 5;
2 if N es divisible por 2 o 3 then
3 Imprimir factor;
4 else
5 while p G do
6 if N es divisible por p o p + 2 then
7 Imprimir factor;
8 break;
9 end
10 p = p+6
11 end
12 end

1.7.4 Implementacin en Java.

Creamos una clase que busca


factores primos de un nmero N hasta un lmite G. En el
programa, G = Mn { N , G}.

Usamos un mtodo reducir(N,p) que verifica si p es factor, si es as, continua dividiendo


por p hasta que el residuo no sea cero. Retorna la parte de N que no ha sido factorizada.

El mtodo Factzar_Ensayo_Error(N, G) llama al mtodo reducir(N,p) para cada


p = 2, 3, 7, 11, 13, .... hasta que se alcanza el lmite G.

import java.util.Vector;
import java.math.BigInteger;

public class Ensayo_Error


{
private Vector salida = new Vector(1);
static BigInteger Ge = new BigInteger("10000000");//10^7
BigInteger UNO = new BigInteger("1");
BigInteger DOS = new BigInteger("2");
BigInteger TRES = new BigInteger("3");
BigInteger SEIS = new BigInteger("4");
BigInteger Nf;
int pos = 1; //posicin del exponente del factor

public Ensayo_Error(){}

public BigInteger reducir(BigInteger Ne, BigInteger p)


FACTORIZACIN POR ENSAYO Y ERROR. 21

{
int exp = 0, posAct = pos;
BigInteger residuo;
residuo = Ne.mod(p);

if(residuo.compareTo(BigInteger.ZERO)==0)
{
salida.addElement(p); //p es objeto BigInteger
salida.addElement(BigInteger.ONE); //exponente
pos = pos+2; //posicin del siguiente exponente (si hubiera)
}

while(residuo.compareTo(BigInteger.ZERO)==0)
{
Ne = Ne.divide(p); // Ne = Ne/p
residuo = Ne.mod(p);
exp=exp+1;
salida.set(posAct, new BigInteger(""+exp)); //p es objeto BigInteger
}

return Ne;
}//

public Vector Factzar_Ensayo_Error(BigInteger Ne, BigInteger limG)


{
BigInteger p = new BigInteger("5");

Nf = Ne;
Nf = reducir(Nf, DOS);
Nf = reducir(Nf, TRES);

while(p.compareTo(limG)<=0)
{
Nf= reducir(Nf, p); //dividir por p
Nf= reducir(Nf, p.add(DOS)); //dividir por p+2
p = p.add(SEIS); //p=p+6
}

if(Nf.compareTo(BigInteger.ONE)>0)
{
salida.addElement(Nf); //p es objeto BigInteger
salida.addElement(BigInteger.ONE); //exponente
}
return salida;
}
// Solo un argumento.
public Vector Factzar_Ensayo_Error(BigInteger Ne)
{
BigInteger limG = Ge.min(raiz(Ne));
22 ALGORITMOS EN TEORIA DE NUMEROS.

return Factzar_Ensayo_Error(Ne, limG);


}

//raz cuadrada
public BigInteger raiz(BigInteger n)
{
BigInteger xkm1 = n.divide(DOS);
BigInteger xk = n;

if(n.compareTo(BigInteger.ONE)< 0)
return xkm1=n;
while(xk.add(xkm1.negate()).compareTo(BigInteger.ONE)>0)
{
xk=xkm1;
xkm1=xkm1.add(n.divide(xkm1));
xkm1=xkm1.divide(DOS);
}
return xkm1;
}
//Imprimir
public String print(Vector lista)
{
String tira="";
for(int p = 0; p < lista.size(); p++)
{
if(p%2==0) tira= tira+lista.elementAt(p);
else tira= tira+"^"+lista.elementAt(p)+" * ";
}
return tira.substring(0,tira.length()-3);
}

public static void main(String[] args)


{
BigInteger limG;
BigInteger Nduro = new BigInteger("2388005888439481");
BigInteger N = new BigInteger("27633027771706698949");
Ensayo_Error Obj = new Ensayo_Error();
Vector factores;

factores = Obj.Factzar_Ensayo_Error(N); //factoriza

//Imprimir vector de factores primos


System.out.println("\n\n");
System.out.println("N = "+N+"\n\n");
System.out.println("Hay " +factores.size()/2+" factores primos <= " + Ge+"\n\n");
System.out.println("N = "+Obj.print(factores)+"\n\n");
System.out.println("\n\n");
}
MTODO DE FACTORIZACIN RHO DE POLLARD. 23

Al ejecutar este programa con N = 367367653565289976655797, despus de varios se-


gundos la salida es

N = 27633027771706698949
Hay 3 factores primos <= 100000
N = 7^2 * 3671^3 * 408011^1

1.8 MTODO DE FACTORIZACIN RHO DE POLLARD.

1.8.1 Introduccin.
En el mtodo de factorizacin
por ensayo y error, en su versin ms cruda, probamos con
todos los nmeros entre 2 y N para hallar un factor de N. Si no lo hallamos, N es primo.

En vez de hacer estos N pasos
(en el peor caso), vamos a escoger una lista aleatoria
de nmeros, ms pequea que N , y probar con ellos.

A menudo se construyen sucesiones seudo-aleatorias x0 , x1 , x2 , ... usando una iteracin de


la forma xi+1 = f (xi ) (mod N), con x0 = random(0, N 1). Entonces {x0 , x1 , ...} ZN .
Por lo tanto los xi s se empiezan a repetir en algn momento.

La idea es esta: Supongamos que ya calculamos la sucesin x0 , x1 , x2 , ... y que es sufi-


cientemente aleatoria. Si p es un factor primo de N y si


xi x j (mod p)
xi
/ x j (mod N)

entonces, como xi x j = kp, resulta que MCD(xi x j , N) es un factor no trivial de N.

Claro, no conocemos p, pero conocemos los xi s, as que podemos revelar la existencia de


p con el clculo del MCD: En la prctica se requiere comparar, de manera eficiente, los xi
con los x j hasta revelar la presencia del factor p va el clculo del MCD(xi x j , N).


xi x j (mod p)
= MCD(xi x j , N) es factor no trivial de N

xi
/ x j (mod N)

Si x0 , x1 , x2 , ... es suficientemente aleatoria, hay una probabilidad muy alta de que en-
contremos pronto una repeticin del tipo xi x j (mod p) antes de que esta repeticin
ocurra (mod N).
24 ALGORITMOS EN TEORIA DE NUMEROS.

Antes de entrar en los detalles del algoritmo y su eficiencia, veamos un ejemplo.

EJEMPLO 1.6

Sea N = 1387. Para crear una sucesin seudoaleatoria usamos f (x) = x2 1 y


x1 = 2. Luego,

x0 = 2
xi+1 = xi2 1 (mod N)

es decir,

{x0 , x1 , x2 , ...} = {2, 3, 8, 63, 1194,


1186, 177, 814, 996, 310, 396, 84, 120, 529, 1053, 595, 339,
1186, 177, 814, 996, 310, 396, 84, 120, 529, 1053, 595, 339, ...}

Luego, por inspeccin logramos ver que 1186 / 8 (mod N) y luego usamos el
detector de factores: MCD(1186 8, N) = 19. Y efectivamente, 19 es un factor de
1387. En este caso detectamos directamente un factor primo de N.

Por supuesto, no se trata de comparar todos los xi s con los x j s para j < i. El mtodo
de factorizacin rho de Pollard, en la variante de R. Brent, usa un algoritmo para de-
tectar rpidamente un ciclo en una sucesin ([Bre79]) y hacer solo unas cuantas com-
paraciones. Es decir, queremos detectar rpidamente xi x j (mod p) usando la sucesin
xi+1 = f (xi ) (mod N) (que alcanza un ciclo un poco ms tarde) y el test MCD(xi x j , N).

Tpicamente necesitamos unas O( p ) operaciones. El argumento es heurstico. Bsica-
mente lo que se muestra es que, como en el problema del cumpleaos, dos nmeros xi y
x j , tomados de manera aleatoria, son congruentes mdulo p con probabilidad mayor que

1/2, despus de que hayan sido seleccionados unos 1.177 p nmeros.

Aunque la sucesin xi+1 = f (xi ) (mod N) cae en un ciclo en unas O( N ) operaciones,


es muy probable que detectemos xi x j (mod p) en unos O( p ) pasos. Si p N
entonces encontraramos un factor de N en unos O(N 1/4 ) pasos. Esto nos dice que el
algoritmo rho de Pollard factoriza N 2 con el mismo esfuerzo computacional con el que
el mtodo de ensayo y error factoriza N.

1.8.2 Algoritmo e Implementacin.


La algoritmo original de R. Brent compara x2k 1 con x j , donde 2k+1 2k1 j 2k+1 1.
Los detalles de cmo esta manera de proceder detectan rpidamente un ciclo en una suce-
MTODO DE FACTORIZACIN RHO DE POLLARD. 25

sin no se ven aqu pero pueden encontrarse en [Bre79] y [Yan95].

EJEMPLO 1.7

Sean N = 3968039, f (x) = x2 1 y x0 = 2. Luego,

MCD(x1 x3 , N) = 1
MCD(x3 x6 , N) = 1
MCD(x3 x7 , N) = 1
MCD(x7 x12 , N) = 1
MCD(x7 x13 , N) = 1
MCD(x7 x14 , N) = 1
MCD(x7 x15 , N) = 1
.. .. ..
. . .
MCD(x63 x96 , N) = 1
MCD(x63 x97 , N) = 1
MCD(x63 x98 , N) = 1
MCD(x63 x99 , N) = 1
MCD(x63 x100 , N) = 1
MCD(x63 x101 , N) = 1
MCD(x63 x102 , N) = 1
MCD(x63 x103 , N) = 1987

N = 1987 1997.

En general, hay muchos casos en los que MCD(xi x j , N) = 1. En vez de calcular


todos estos MCD(z1 , N), MCD(z2 , N), ..., calculamos unos pocos MCD(Qk , N), donde
Qk = kj=1 z j (mod N). Brent sugiere escoger k entre ln N y N 1/4 pero lejos de cualquiera
de los dos extremos ([Bre79]). Riesel ([Rie94]) sugiere tomar k como un mltiplo de 100.

EJEMPLO 1.8

Sean N = 3968039, f (x) = x2 1 y x0 = 2. Luego, tomando k = 30

30
Q30 = z j (mod N) = 3105033, MCD(Q30 , N) = 1
j=1
60
Q60 = z j (mod N) = 782878, MCD(Q60 , N) = 1987
j=31
26 ALGORITMOS EN TEORIA DE NUMEROS.

El algoritmo que vamos a describir aqu es otra variante del algoritmo de Brent ([Bre81])
que es ms sencillo de implementar.

Se calcula MCD(xi x j , N) para i = 0, 1, 3, 7, 15, ... y j = i + 1, ..., 2i + 1 hasta que, o


xi = x j (mod N) (en este caso se debe escoger una f diferente o un x0 diferente) o que un
factor no trivial de N sea encontrado.

Observe que si i = 2k 1 entonces j = 2i + 1 = 2k+1 1, es decir el ltimo j ser el


nuevo i. Por tanto, en el algoritmo actualizamos xi al final del For, haciendo la asig-
nacin xi = x2i+1 = x j .

Algoritmo 1.8: Mtodo rho de Pollard (variante de R. Brent)


Entrada: N N, f , x0
Salida: Un factor p de N o mensaje de falla.
1 salir=false;
2 k = 0, x0 = Random(2, N 1);
3 xi = x0 ;
4 while salir=False do
5 i = 2k 1 ;
6 for j = i + 1, i + 2, ..., 2i + 1 do
7 x j = f (x0 ) (mod N) ;
8 if xi = x j then
9 salir=True;
10 Imprimir El mtodo fall. Reintentar cambiando f o x0 ;
11 Exit For;
12 g = MCD(xi x j , N) ;
13 if 1 < g < N then
14 salir=True;
15 Imprimir N = N/g g ;
16 Exit For;
17 x0 = x j ;
18 xi = x j ;
19 k ++;

1.8.2.1 Implementacin en Java. La implementacin sigue paso a paso el algo-


ritmo.

import java.math.BigInteger;
public class rhoPollard
{
rhoPollard(){}

public BigInteger f(BigInteger x)


{
return x.multiply(x).add(BigInteger.ONE);//x^2+1
MTODO DE FACTORIZACIN RHO DE POLLARD. 27

public void FactorPollard(BigInteger N)


{
int i, k;
BigInteger xi,xj;
BigInteger g = BigInteger.ONE;
BigInteger x0 = new BigInteger(""+2);;
boolean salir = false;

k = 0;
xi= x0;
xj= x0;
while(salir==false)
{ i=(int)(Math.pow(2,k)-1);
for(int j=i+1; j<=2*i+1; j++)
{
xj=f(x0).mod(N);
if(xi.compareTo(xj)==0)//si son iguales
{salir=true;
System.out.print("Fallo"+"\n\n");
break;
}
g= N.gcd(xi.subtract(xj));
if(g.compareTo(BigInteger.ONE)==1 && g.compareTo(N)==-1)//1<g<N
{salir=true;
System.out.print("Factor = "+g+"\n\n");
break;
}
x0=xj;
}
xi=xj;
k++;
}
System.out.print(N+" = "+g+" . "+N.divide(g)+"\n\n");
}

public static void main(String[] args)


{
System.out.print("\n\n");
rhoPollard obj = new rhoPollard();
BigInteger N = new BigInteger("39680399966886876527");
obj.FactorPollard(N);
System.out.print("\n\n");
}
}//
28 ALGORITMOS EN TEORIA DE NUMEROS.

EJERCICIOS

1.3 Implementar una variante con el producto Qk = kj=1 z j (mod N).

1.9 PRUEBAS DE PRIMALIDAD.

1.9.1 Introduccin.
Para decidir si un nmero n pequeo es primo, podemosusar el mtodo de ensayo y error
para verificar que no tiene divisores primos inferiores a n .

Para un nmero un poco ms grande, la estrategia usual es primero verificar si tiene divi-
sores primos pequeos, sino se usa el test para seudoprimos fuertes de Miller-Rabin con
unas pocas bases pi (con pi primo) y usualmente se combina con el test de Lucas. Esta
manera de proceder decide de manera correcta si un nmero es primo o no, hasta cierta
cota 10M . Es decir, la combinacin de algoritmos decide de manera correcta si n < 10M
Sino, decide de manera correcta solamente con una alta probabilidad y cabe la (remota)
posibilidad de declarar un nmero compuesto como primo.

Aqu solo vamos a tratar rpidamente la prueba de Miller-Rabin.

1.9.2 Prueba de primalidad de Miller Rabin.

Iniciamos con test de primalidad de Fermat, por razones histricas. Esta prueba se basa el
el teorema,

Teorema 1.9 (Fermat)


Sea p primo. Si MCD(a, p) = 1 entonces a p1 1 (mod p).

Este teorema nos dice que si n es primo y a es un entero tal que 1 a n 1, entonces
an1 1 (mod n).

Por tanto, para probar que n es compuesto bastara encontrar 1 a n 1 tal que
an1 / 1 (mod n).

Definicin 1.3 Sea n compuesto. Un entero 1 a n 1 para el que an1


/ 1 (mod n),
se llama testigo de Fermat para n.
PRUEBAS DE PRIMALIDAD. 29

Un testigo de Fermat para n sera un testigo de no-primalidad. De manera similar, un


nmero 1 a n 1 para el que an1 1 (mod n), apoya la posibilidad de que n sea
primo,

Definicin 1.4 Sea n un entero compuesto y sea a un entero para el cual 1 a n 1


y an1 1 (mod n). Entonces se dice que n es un seudoprimo respecto a la base a. Al
entero a se le llama un embaucador de Fermat para n.

Por ejemplo, n = 645 = 3 5 43 es un seudoprimo en base 2 pues 2n1 1 (mod n).

Es curioso que los seudoprimos en base 2 sean muy escasos. Por ejemplo, hay 882 206 716
primos inferiores a 2 1010 y solo hay 19685 seudoprimos en base 2 inferiores a 21010 .
Esto nos dice que la base 2 parece ser muy poco embaucadora en el sentido de que si
tomamos un nmero grande n de manera aleatoria y si verificamos que 2n1 1 (mod n),
entonces es muy probable que n sea primo. Tambin los seudoprimos en base 3 son
muy escasos y es altamente improbable que si tomamos un nmero grande n de manera
aleatoria, este sea compuesto y que a la vez sea simultneamente seudoprimo en base 2 y
base 3.

Es decir, si un nmero n pasa los dos test 2n1 1 (mod n) y 3n1 1 (mod n); es muy
probable que sea primo.

Sin embargo, hay enteros n compuestos para los cuales an1 1 (mod n) para todo a que
cumpla MCD(a, n) = 1. A estos enteros se les llama nmeros de Carmichael.

Por ejemplo, n = 561 = 3 11 17 es nmero de Carmichael. Aunque este conjunto de


nmeros es infinito, son ms bien raros (poco densos). En los primeros 100 000 000
nmeros naturales hay 2051 seudoprimos en base 2 y solo 252 nmeros de Carmichael.

Nuestra situacin es esta: Es poco probable que un nmero compuesto pase varios test de
primalidad an1 1 (mod n) excepto los nmeros de Carmichael, que son compuestos
y pasan todos estos test.

Hay otro test, llamado test fuerte de seudo-primalidad en base a el cual los nmeros de
Carmichael no pasan. Adems, si tomamos k nmeros de manera aleatoria a1 , a2 , ..., ak y
si n pasa este test en cada una de las bases ai , podemos decir que la probabilidad de que
nos equivoquemos al declarar n como primo es menor que 1/4k . Por ejemplo, si k = 200
la probabilidad de que nos equivoquemos es < 10120

Teorema 1.10 Sea n un primo impar y sea n 1 = 2s r con r impar. Sea a un entero
j
tal que MCD(a, n) = 1. Entonces, o ar 1(mod n) o a2 r 1(mod n) para algn
j, 0 j s 1.
30 ALGORITMOS EN TEORIA DE NUMEROS.

Con base en el teorema anterior, tenemos

Definicin 1.5 Sea n impar y compuesto y sea n1 = 2s r con r impar. Sea 1 a n1.

j
(i) Si ar /1(mod n) y si a2 r / 1(mod n) para 0 j s 1, entonces a es llamado
un testigo fuerte (de no-primalidad) de n.

j
(ii) Si ar 1( mod n) y si a2 r 1(mod n) para 0 j s 1, entonces n se dice
un seudoprimo fuerte en la base a. Al entero a se le llama embaucador fuerte.

As, un seudoprimo fuerte n en base a es un nmero que acta como un primo en el sentido
del teorema 1.10.

Teorema 1.11 (Rabin)


1
Si n es un entero compuesto, a lo sumo de todos los nmeros a, 1 a n 1, son
4
embaucadores fuertes de n.

Supongamos que tenemos un nmero compuesto n. Tomamos k nmeros {a1 , a2 , ..., ak }


de manera aleatoria y aplicamos el test fuerte de seudo-primalidad a n con cada uno de
de estas bases ai . Entonces, hay menos que un chance en cuatro de que a1 no sea testigo
de no-primalidad de n, y menos que un chance en cuatro de que a2 no sea testigo de
no-primalidad de n, etc. Si n es primo, pasa el test para cualquier a < n. Si cada ai falla
en probar que n es compuesto, entonces la probabilidad de equivocarnos al decir que n es
1
primo es inferior a k .
4
PRUEBAS DE PRIMALIDAD. 31

1.9.3 Algoritmo e Implementacin.

Algoritmo 1.9: Miller-Rabin


Entrada: n 3 y un parmetro de seguridad t 1.
Salida: n es primo o n es compuesto.
1 Calcule r y s tal que n 1 = 2s r, r impar;
2 for i = 1, 2, ...,t do
3 a = Random(2, n 2);
4 y = ar (mod n);
5 if y 6= 1 y y 6= n 1 then
6 j = 1;
7 while j s 1 y y 6= n 1 do
8 y = y2 (mod n);
9 if y = 1 then
10 return Compuesto;
11 j = j +1;
12 if y 6= n 1 then
13 return Compuesto;

14 return Primo;

El algoritmo 1.9 verifica si en cada base a se satisface la definicin 1.5. En la lnea 9, si


j j1
y = 1, entonces a2 r 1 (mod n). Puesto que este es el caso cuando a2 r / 1 (mod
n) entonces n es compuesto. Esto es as pues si x2 y2 (mod n) pero si x / y (mod n),
entonces MCD(x y, n) es un factor no trivial de n. En la lnea 12, si y 6= n 1, entonces
a es un testigo fuerte de n.

Si el algoritmo 1.9 declara compuesto a n entonces n es definitivamente compuesto, por


el teorema 1.10. Si n es primo, es declarado primo. Si n es compuesto, la probabilidad de
que el algoritmo lo declare primo es inferior a 1/4t .

El algoritmo 1.9 requiere, para n 1 = 2 j r con r impar, t(2 + j) ln n pasos. t es el nmero


de bases.

Una estrategia que se usa a veces es fijar las bases. Se toman como base algunos de los
primeros primos en vez de tomarlas de manera aleatoria. El resultado importante aqu es
este: Si p1 , p2 , ..., pt son los primeros t primos y si t es el ms pequeo entero com-
puesto el cual es seudoprimo para todas las bases p1 , p2 , ..., pt , entonces el algoritmo de
Miller-Rabin, con las bases p1 , p2 , ..., pt , siempre responde de manera correcta si n < t .
Para 1 t 8 tenemos
32 ALGORITMOS EN TEORIA DE NUMEROS.

t t
1 2047
2 1 373 653
3 25 326 001
4 3 215 031 751
5 2 152 302 898 747
6 3 474 749 660 383
7 341 550 071 728 321
8 341 550 071 728 321

1.9.3.1 Implementacin en Java. En la clase BigInteger de Java ya viene im-


plementado el mtodo this.modPow(BigInteger r, BigInteger N) para calcular
y = ar (mod N). Para calcular r y s solo se divide N 1 por dos hasta que el residuo sea
diferente de cero.

En esta implementacin usamos los primeros ocho primos como bases. As el algoritmo
responde de manera totalmente correcta si 19 < N < 341550071728321. En todo caso,
tambin podemos usar el mtodo this.isProbablePrime(int c) que responde correc-
1
tamente con una probabilidad que excede 1 c .
2

import java.math.BigInteger;
import java.util.*;

public class Miller_Rabin


{
public Miller_Rabin(){}

public boolean esPrimoMR(BigInteger N)


{
//n>3 e impar. Respuesta 100% segura si N <341 550 071 728 321
BigInteger N1 = N.subtract(BigInteger.ONE);//N-1
BigInteger DOS = new BigInteger("2");
int[] primo = {2,3,5,7,11,13,17,19};
int s = 0;
boolean esPrimo = true;
BigInteger a,r,y;
int j;

//n-1 = 2^s r
while(N1.remainder(DOS).compareTo(BigInteger.ZERO)==0)
{ N1=N1.divide(DOS);
s=s+1;
}
r = N1;
N1 = N.subtract(BigInteger.ONE);
EJERCICIOS 33

for(int i=0; i<=7; i++)


{
a = new BigInteger(""+primo[i]);
y = a.modPow(r, N);
if( y.compareTo(BigInteger.ONE)!=0 && y.compareTo(N1)!=0)
{
j=1;
while(j<= s-1 && y.compareTo(N1)!=0 )
{
y = y.modPow(DOS, N);
if(y.compareTo(BigInteger.ONE)==0) esPrimo=false;
j++;
}
if(y.compareTo(N1)!=0) esPrimo = false;
}

}
return esPrimo;
}

public static void main(String[] args)


{
System.out.println("\n\n");
BigInteger N = new BigInteger("6658378974");
Miller_Rabin obj = new Miller_Rabin();

System.out.println(N+" es primo = "+obj.esPrimoMR(N)+"\n\n");

System.out.println("\n\n");
}
}

EJERCICIOS
1.4 Esta implementacin falla para n = 2, 3, 5, 7, 11, 13, 17, 19. Porqu?

1.5 Mejore la implementacin anterior.

1.10 ALGORITMO CHINO DEL RESTO.

El problema clsico conocido como problema chino del resto puede ser establecido como
sigue:

Dados los mdulos m0 , m1 , ..., mk Z y los residuos correspondientes ui Zmi con


i = 0, 2..., k; encontrar un entero u tal que
34 ALGORITMOS EN TEORIA DE NUMEROS.

u ui (mod mi ) , 0 i k. (1.1)

Las condiciones bajo las cuales se puede garantizar la existencia de una solucin nica para
este problema se establecen en el siguiente teorema,

Teorema 1.12 (Chino del Resto)

Sean m0 , m1 , ..., mk Z primos relativos dos a dos, i.e. MCD(mi , m j ) = 1 si i 6= j, y


consideremos los k residuos ui Zmi , 0 i k. Para cada entero fijo a existe un nico
entero u Z que satisface las condiciones
k


a u < a+m con m = mi ;
i=0



u ui (mod mi ) , 0 i k

EJEMPLO 1.9

u 49 (mod) 99
Consideremos u 21 (mod) 97

u 30 (mod) 95

Aqu m = 912285. Si a = 0 tenemos u = 639985.

La unicidad es mdulo m, es decir, el problema chino del resto tiene infinitas soluciones
k
en Z pero tiene solucin nica en Zm , con m = mi .
i=0
Para ver la idea de la prueba, vamos a introducir una notacin que nos va a servir ms
adelante.

Consideremos el Homomorfismo modular m : Z[x] Zm [x] (m 2), definido por

m (x) = x

m (a) = rem(a, m) para todo a Z.

De acuerdo a la definicin, m (A(x)) solo cambia los coeficientes ai por los nuevos coe-
ficientes ai mod m. Por ejemplo, 5 (3x6 x4 + 6x3 + x2 3x) = 3x6 + 4x4 + x3 + x2 + 2x.
ALGORITMO CHINO DEL RESTO. 35

Si u Zm entonces (m0 (u), m1 (u), ...mn (u)) es uno de los m = ni=0 mi distintos
n +1 tuples posibles en Zm0 Zm1 ...Zmn . Si calculamos (m0 (u), m1 (u), ...mn (u))
para cada u Zm , en algn momento encontraramos un u Zm tal que

(m0 (u), m1 (u), ...mn (u)) = (u0 , u1 , ..., un ).

As, la unicidad de u debe entenderse en el sentido de que u es nico en Zm no en Z, es


decir u es nico mdulo m.

La idea de la prueba del teorema chino del resto nos dice cmo encontrar u. Lamentable-
mente no es prctico buscar u de esta manera pues m puede ser muy grande.

1.10.1 Algoritmo e Implementacin.


El algoritmo usual para resolver este tipo de problemas se llama algoritmo de Garner
(por H.L. Garner). La idea central del mtodo de Garner es, a la manera del polinomio
interpolante de Newton, representar u como una combinacin lineal de base mixta,

!
n1
u = v0 + v1 (m0 ) + v2 (m0 m1 ) + ... + vn mi (1.2)
i=0

con vk Zmk , k = 0, 1, ..., n.

Si m es impar y positivo, la representacin simtrica de Zm es

m1 m1
Zm = { , ..., 1, 0, 1, ..., }
2 2

La representacin de u como una combinacin lineal de base mixta tiene sentido si


cada vk Zmk tiene el mismo tipo de representacin, es decir siempre (para cada k )
Zmk = {0, 1, ..., mk 1} o siempre Zmk = {v : mk /2 < v mk /2}.

Se puede probar que u siempre se puede representar en la forma (1.2) y, escogida un


representacin igual para todos los Zmk , los coeficientes vi son nicos.

EJEMPLO 1.10

Sean m0 = 99, m1 = 97, y m2 = 95. Si u = 639985,

u = 49 + 62 (m0 ) + 66 (m0 m1 ).
36 ALGORITMOS EN TEORIA DE NUMEROS.

Encontrar u es lo mismo que encontrar v0 , v1 , ..., vn .

Para i = 0, de la representacin (1.2) se deduce u v0 (mod m0 ). As que u u0 (mod m0 )


tiene solucin u = u0 .

Para k 1, si se han obtenido los coeficientes v0 , v1 , ..., vk1 , entonces de (1.2),

!
k1
u v0 + v1 (m0 ) + ... + vk mi (mod mk )
i=0

satisface el caso i = k del sistema de congruencias u ui (mod mi ), 0 i n, si se toma


vk de tal manera que

!
k1
v0 + v1 (m0 ) + ... + vk mi uk (mod mk )
i=0

Esta ecuacin la podemos resolver para un nico vk Zmk (k 1),

" !#! !1
k2 k1
vk uk v0 + v1 (m0 ) + ... + vk1 mi mi (mod mk )
i=0 i=0

k1
El inverso se puede tomar pues i=0 mi y mk son primos relativos.
En el algoritmo que viene a continuacin, la solucin u entra representada en trminos
de los n + 1 residuos u0 , u1 , ..., un respecto a los n + 1 mdulos m0 , m1 , ..., mn . Luego se
pasa a una representacin v0 , v1 , ..., vn respecto a la base mixta 1, m0 , ..., n1
i=0 mi y, como
paso final, se reconstruye u en base 10.
ALGORITMO CHINO DEL RESTO. 37

Algoritmo 1.10: Problema Chino del Resto en Z. Algoritmo de Garner


Entrada: (u0 , u1 , ...un ), (m0 , m1 , ..., mn ) con mi Z positivos y primos relativos dos a
dos y ui Zmi .
Salida: u Zm con m = ni=0 mi tal que u ui (mod mi ), i = 0, 1, ..., n.
1 Clculo de inversos;
2 for k = 1 to n do
3 producto = mk (m0 ) ;
4 for i = 1 to k 1 do
5 producto = mk (producto mi ) ;
6 k = (producto)1 (md mk ) ;

7 Clculo de los vk ;
8 v0 = u0 ;
9 j = 0;
10 for k = 1 to n do
11 temp = vk1 ;
12 j = k 2;
13 while j 0 do
14 temp = mk (temp m j + v j ) ;
15 j = j 1;
16 vk = mk ((uk temp)k ) ;

17 Pasar u a base 10 ;
18 u = vn ;
19 j = n1;
20 while j 0 do
21 u = umj +vj ;
22 j = j 1;

23 return u ;

Para obtener u en el paso final, se usa una multiplicacin anidada

u = v0 + m0 (v1 + m1 (v2 + ... + mn2 (vn1 + mn1 (vn ))...))

En este ltimo paso, cada iteracin actualiza u como u = u m j + v j con j =


n 1, n 2, ..., 0.

Aqu no es necesario poner u = m (u m j + v j ) pues estas sumas estn en el rango


correcto, es decir u m j + v j Zm no importa la representacin que se haya usado.
mk
En efecto, como |vk | entonces, de acuerdo a (1.2), |u| (ni=0 mi ) /2 = m/2.
2
Si usamos la representacin {0, ..., m 1} de Zm obtenemos de manera similar,
u m 1.
38 ALGORITMOS EN TEORIA DE NUMEROS.

EJEMPLO 1.11

u 49 (mod) 99
El algoritmo de Garner aplicado a u 21 (mod) 97 produce la salida

u 30 (mod) 95
u = 639985.

1.10.1.1 Implementacin en Java.


import java.math.BigInteger;
class PCR
{
PCR(){}
public BigInteger reprSimetrica(BigInteger m, BigInteger p)
{
BigInteger salida;
BigInteger DOS = new BigInteger("2");

salida = m.mod(p);
//representaci\on sim\etrica de Z_p =]-p/2,...-1,0,1,...,p/2]
//si salida > p/2 -> salida= - p = -p/2 +i.
if(salida.compareTo(p.divide(DOS))==1)
salida = salida.add(p.negate());
return salida;
}

//Algoritmo Chino del Resto


public static BigInteger Z_ACR(BigInteger Uis[], BigInteger Ms[])
{
//Requiere Ms[i]>2.
int n = Ms.length-1; //Ms[0],...,Ms[n]
BigInteger u = BigInteger.ZERO;
BigInteger producto = BigInteger.ONE;
BigInteger temp;
BigInteger gamma[] = new BigInteger[n+1]; //gamma[1],...,gamma[n]
BigInteger v[] = new BigInteger[n+1];

//para k=1,2,...,n, gamma_k = (Prod mi_{i=0}^{k-1})^{-1} Mod m_k.


for(int k=1; k<=n; k++)
{ producto = Ms[0].mod(Ms[k]);
for(int i=1; i<= k-1; i++)
producto = (producto.multiply(Ms[i])).mod(Ms[k]);
gamma[k] = producto.modInverse(Ms[k]);
}
int j;
v[0]=Uis[0];
for(int k=1; k<=n; k++)
{ temp = v[k-1];
IMPLEMENTACIN DE UNA CLASE BIGRATIONAL. 39

j=k-2;
while(j>=0)
{temp = ((temp.multiply(Ms[j])).add(v[j])).mod(Ms[k]);
j=j-1;
}
v[k]= (Uis[k].subtract(temp)).multiply(gamma[k]).mod(Ms[k]);
}
u = v[n];
j = n-1;
while(j >= 0)
{u = (u.multiply(Ms[j])).add(v[j]);
j = j-1;
}
return u;
}
public static void main(String[] args)
{
System.out.print("\n\n");
PCR obj = new PCR();

BigInteger uis[]={new BigInteger("49"),new BigInteger("-21"),


new BigInteger("-30")};
BigInteger mis[]={new BigInteger("99"),new BigInteger("97"),
new BigInteger("95")};

System.out.println(""+obj.Z_ACR(uis, mis));
System.out.print("\n\n");
}
}

1.11 IMPLEMENTACIN DE UNA CLASE BIGRATIONAL.

En esta seccin implementaremos una clase BigRational para operar con fracciones con
numerador y denominador BigInteger. Esta clase viene con una implementacin de la
aproximacin racional de un nmero real usando fracciones continuas.

1.11.1 Aproximacin Racional de un Nmero Real


Sea x un nmero real. x se puede escribir en la forma

1
x = b0 + =: [b0 ; b1 , b2 , ...]
1
b1 +
1
b2 +
40 ALGORITMOS EN TEORIA DE NUMEROS.

donde b0 es un entero y los denominadores parciales b1 , b2 , ... son enteros positivos. El


clculo de los b0i s se hace de la siguiente manera,
1
b0 = bxc, x1 = ,
x b0
1
b1 = bx1 c, x2 = ,
x1 b1
1
b2 = bx2 c, x3 = ,
x2 b2
.. ..
. .
1
bn = bxn c, xn+1 =
xn bn

El clculo termina si x es racional, sino la expansin es infinita.

Para aproximar un nmero real con una fraccin racional, se usan los convergentes (o
An
cocientes parciales) . Estos convergentes se definen como
Bn

An
= [b0 ; b1 , b2 , ..., bn ]
Bn

Tenemos el siguiente teorema de aproximacin,

Teorema 1.13 Si x R y x = [b0 ; b1 , b2 , ...] entonces Bn es creciente,




x An < 1 y x = lim
An
Bn B2n n Bn

Si A1 = 1, B1 = 0, A0 = b0 y B0 = 1, An /Bn se puede calcular con la siguiente frmula


recursiva:

As = bs As1 + As2

Bs = bs Bs1 + Bs2

1.11.2 Algoritmo.

Como los convergentes aproximan tanto como queramos al nmero x, en este algoritmo
nos detenemos hasta que tengamos los 15 dgitos decimales que nos permite la aritmtica
EJERCICIOS 41

del computador (actual).

Algoritmo 1.11: Aproximacin Racional con Fracciones Continuas.


Entrada: x R, x 6= 0.
An An
Salida: tal que x < 1015 .
Bn Bn
1 X = x;
2 A1 = 1 ;
3 B1 = 0 ;
4 A2 = 0 ;
5 B2 = 1 ;
6 An = 1 ;
7 Bn = 1 ;
8 if x 6= 0 then

An
while x > 1015 do
9 Bn
10 b = bXc ;
11 An = bA1 + A2 ;
12 Bn = bB1 + B2 ;
13 A2 = A1 ;
14 B2 = B1 ;
15 A1 = An ;
16 B1 = Bn ;
17 X = X b;
18 X = 1/X ;

19 return An /Bn

EJERCICIOS
1.6 Implemente el algoritmo ??.

1.11.3 Implementacin de la Clase en Java


Cuando recibimos una fraccin a/b, lo primero que hacemos es simplificar. Esto lo
hacemos en el constructor principal,

private BigInteger num; // numerador


private BigInteger den; // denominador

public BigRational(BigInteger numerador, BigInteger denominador)


{
BigInteger g = numerador.gcd(denominador);
num = numerador.divide(g);
den = denominador.divide(g);
}
42 ALGORITMOS EN TEORIA DE NUMEROS.

Los constructores que vamos a definir son

Constructores
public BigRational(BigInteger numerador, BigInteger denominador)
public BigRational()
public BigRational(String s)
public BigRational(int numerador, int denominador)
public BigRational(int numerador)

Los mtodos que vamos a implementar son,

Mtodos
public String toString()
public int compareTo(BigRational b)
public BigRational times(BigRational b)
public BigRational plus(BigRational b)
public BigRational negate()
public BigRational minus(BigRational b)
public BigRational reciprocal()
public BigRational divide(BigRational b)
public BigRational pow(int exponent)
public static BigRational parse(String s)
public double toDouble()
public static BigRational RatAprox(double vx)

El mtodo parse lo utilizamos para leer las fracciones.

Implementacin de la clase

import java.math.BigInteger;
public class BigRational
{
public final static BigRational ZERO = new BigRational(0);
public final static BigRational ONE = new BigRational(1);
//campos
private BigInteger num; // numerador
EJERCICIOS 43

private BigInteger den; // denominador

//Constructores
public BigRational(BigInteger numerador, BigInteger denominador)
{ // manejo de x / 0
if (denominador.equals(BigInteger.ZERO))
{throw new RuntimeException("Denominador es cero");
}
//Simplificar fracci\on. gcd est\a implementado en BigInteger
BigInteger g = numerador.gcd(denominador);
num = numerador.divide(g);
den = denominador.divide(g);
// Asegura invariante den >0
if (den.compareTo(BigInteger.ZERO) < 0)
{
den = den.negate();
num = num.negate();
}
}

public BigRational()
{
BigRational r = new BigRational(BigInteger.ZERO, BigInteger.ONE );
num = r.num;
den = r.den;
return;
}

//enteros
public BigRational(int numerador, int denominador)
{
this(new BigInteger("" + numerador), new BigInteger("" + denominador));
}

//un entero
public BigRational(int numerador)
{
this(numerador, 1);
}

public BigRational(String s) throws NumberFormatException


{
if ( s.length() == 0 || s== null )
{
BigRational r = new BigRational();
num = r.num;
den = r.den;
return;
}
44 ALGORITMOS EN TEORIA DE NUMEROS.

BigInteger n;
BigInteger d;
int i = s.indexOf(/);
int j = s.indexOf(.);
if ( i < 0 && j < 0)
{
n = new BigInteger(s);
BigRational r = new BigRational( n, BigInteger.ONE );
num = r.num;
den = r.den;
return;
} else if(i > 0 && j<0)
{
n = new BigInteger( s.substring(0,i) );
d = new BigInteger( s.substring( i+1, s.length() ) );
BigRational r = new BigRational( n, d );
num = r.num;
den = r.den;
return;
}else if(i < 0 && j>-1)
{
BigRational ra= new BigRational(1);
Double sxd = new Double(s);
double sx = sxd.doubleValue();
ra= RatAprox(sx);//Ejercicio
num=ra.num;
den=ra.den;
return;
}
}

public String toString()


{
if (den.equals(BigInteger.ONE)) return num + "";
else return num + "/" + den;
}

// retorna { -1, 0, + 1 } if a < b, a = b, o a > b.


public int compareTo(BigRational b)
{
BigRational a = this;
return a.num.multiply(b.den).compareTo(a.den.multiply(b.num));
}

// Es this igual a y?
public boolean equals(Object y)
{
if (y == this) return true;
if (y == null) return false;
EJERCICIOS 45

if (y.getClass() != this.getClass()) return false;


BigRational b = (BigRational) y;
return compareTo(b) == 0;
}

// hashCode consistente con equals() y compareTo()


public int hashCode()
{
return this.toString().hashCode();
}

// retorna a * b
public BigRational times(BigRational b)
{
BigRational a = this;
return new BigRational(a.num.multiply(b.num), a.den.multiply(b.den));
}

// retorna a + b
public BigRational plus(BigRational b)
{
BigRational a = this;
BigInteger numerador = a.num.multiply(b.den).add(b.num.multiply(a.den));
BigInteger denominador = a.den.multiply(b.den);
return new BigRational(numerador, denominador);
}

// retorna -a
public BigRational negate()
{
return new BigRational(num.negate(), den);
}

// retorna a - b
public BigRational minus(BigRational b)
{
BigRational a = this;
return a.plus(b.negate());
}

// retorna 1 / a
public BigRational reciprocal()
{
return new BigRational(den, num);
}

// retorna a / b
public BigRational divide(BigRational b)
{
46 ALGORITMOS EN TEORIA DE NUMEROS.

BigRational a = this;
return a.times(b.reciprocal());
}

// retorna a^n
public BigRational pow(int exponent)
{
BigRational a = this;
BigInteger numerador = a.num.pow(exponent);
BigInteger denominador = a.den.pow(exponent);
return new BigRational(numerador, denominador);
}//

//parse. Ya se hace en BigRational(String s)


public BigRational parse(String s)
{
return new BigRational(s);
}
// toDouble
public double toDouble()
{
return (double) num.doubleValue() /den.doubleValue() ;
}

public static void main(String[] args)


{ System.out.println("\n\n");
BigRational Obj = new BigRational();
BigRational r2 = new BigRational();
BigRational r3;
BigRational r4 = new BigRational();

r2 = Obj.parse("575/45");
r3 = new BigRational("585809/8878783");
System.out.println("r1 = "+r1+" r2 = "+r2+" r3 = "+r3);
r3 = r2.plus(r2);
r3 = r3.times(r3);
r2 = r2.divide(r3);
System.out.println("\n\n");
}
}
CAPITULO 2

ALGORITMOS EN ALGEBRA.

2.1 UNA CLASE PARA OPERACIONES CON POLINOMIOS

Vamos a crear una clase para operar con polinomios con coeficientes enteros (BigInteger).
En esta clase implementaremos los mtodos de MCD de polinomios y factorizacin.

2.1.1 Operaciones Bsicas en Z[x] .


n
En el anillo de polinomios Z[x] podemos sumar, restar y multiplicar. Si A(x) = ai xi y
i=0
m
B(x) = bi xi , entonces
i=0

Mx{n,m}
A(x) B(x) = (ai bi )xi donde a j = 0 si j > grado(A) y b j = 0 si
i=0
j > grado(B).

m+n
A(x) B(x) = dk xk donde dk = ai b j .
k=0 i+ j=k

Algoritmos en Teora de Nmeros y Algebra.. Walter Mora F. 47


Derechos Reservados c 2008 Revista digital Matemtica, Educacin e Internet (www.cidse.itcr.ac.cr)
48 ALGORITMOS EN ALGEBRA.

2.1.2 Evaluacin de Polinomios. Mtodo de Horner.


Supongamos que queremos evaluar P(x) = x4 + x3 + 2. Si evaluamos cada potencia por
separado, iniciando en x3 , cuando evaluamos la potencia x4 no usaramos la potencia x3
previamente calculada y volveramos a repetir este clculo para evaluar x4 . Este proced-
imiento nos llevara a hacer siete productos y tres sumas.

Con solo expresar el polinomio como 2+x(x2 +x3 ) la evaluacin se reduce a seis productos
y dos sumas. Este procedimiento, llevado hasta sus ltimas consecuencias, nos permite
expresar este polinomio como

x4 + x3 + 2 = 2 + x(x(x(x + 1)))

Y evaluando en el orden (x + 1), x(x + 1), etc, nos permite hacer el clculo con cuatro
productos y dos sumas.

Esta es una vieja tcnica, inventada por Newton y redescubierta y popularizada por W.G.
Horner en 1819. Si A(x) = a0 +a1 x+ +an xn , la secuencia de operaciones es como sigue,

vn = an
vn1 = an1 + x vn
vn2 = an2 + x vn1
..
.
v0 = a0 + x v1

2.1.3 Algoritmo

Algoritmo 2.1: Evaluacin de Polinomios. Mtodo de Horner.


Entrada: Un polinomio A(x) = a0 + ... + an xn y x0 R
Salida: A(x0 )
1 valor= an ;
2 j = n 1;
3 while j 0 do
4 valor= a j + x0 valor;
5 j = j 1;
6 return valor

2.1.4 Implementacin de la Clase BPolinomio.


Crear una clase para polinomios con coeficientes enteros requiere no solo programar la
suma, la resta y el producto, tambin requiere implementar la lectura y un mtodo para
imprimir.
UNA CLASE PARA OPERACIONES CON POLINOMIOS 49

n
La manera obvia de representar un polinomio A(x) = ai xi es con una arreglo de coefi-
i=0
cientes A = (a0 a1 ... an ). A esta representacin se le llama representacin densa.

En muchas aplicaciones, los polinomios son en su mayora ralos, es decir con muchos coe-
ficientes nulos: por ejemplo A(x) = x1000 1. Esto no parece bueno para la representacin
densa. Hay varias maneras de representar polinomios. Por ejemplo con listas. Por ejemplo,
el polinomio A(x) = x1000 1 se representa con la lista (1 1000 1 0) y a(x, y) = (2x3 +
1)y7 + (4x5 5x2 + 9)y4 + 1 se representa con la lista ((2 3 1 0) 7 (4 5 5 2 9 0) 4 (1 0) 0).

En esta primera parte usamos la representacin densa porque las operaciones con poli-
nomios son fciles de implementar, nos permite ver mejor los algoritmos y es adecuada
para nuestros propsitos iniciales. Ms adelante tendremos que recurrir a alguna repre-
sentacin rala.

En esta primera implementacin, un polinomio es un arreglo de coeficientes

BigInteger[] coef.

El grado lo almacenamos en una variable int deg y lo determinamos con un mtodo


degree(). Para sumar, restar y multiplicar, llamamos a este mtodo degree() y recor-
remos el arreglo de coeficientes. Esto tiene un inconveniente, el grado del polinomio cero
es cero y no lo podramos poner negativo (pues este polinomio se debe poder sumar, etc.)
y el grado de cualquier polinomio constante ser cero. En la divisin hay que tomar esto
en cuenta: si dividimos por una constante y el resto es cero, el ciclo no se detendra. La
solucin es agregar un poco de cdigo para este caso especial.

La lectura de un polinomio la haremos usando el mtodo StringTokenizer. Bsi-


camente, el mtodo de lectura recibe el polinomio en una tira (String). Separamos el
polinomio usando primero los tokens +y y leemos los coeficientes y el exponente,
en las tiras ai xi , para crear el polinomio ai xi (aqu separamos con los tokens * y ^).
Luego reconstruimos el polinomio sumando los polinomios ai xi .

El mtodo toString toma un polinomio (a0 a1 ... an ) y lo imprime como a_0+a_1 x+...+a_nx^n.
Una vez implementado, la instruccin System.out.print(""+A) imprime el polinomio
A.

Los constructores que vamos a definir son

Constructores
BPolinomio(BigInteger a, int b)
BPolinomio()
BPolinomio(int a, int b)

Los mtodos que vamos a implementar son,


50 ALGORITMOS EN ALGEBRA.

Mtodos
int degree()
BPolinomio plus(BPolinomio B) A(x) + B(x)
BPolinomio times(BPolinomio B) A(x) B(x)
BPolinomio negate() A(x)
BPolinomio minus(BPolinomio B) A(x) B(x)
evaluate(BigInteger x0) A(x0 )
BPolinomio pow(int k) [A(x)]k
int compareTo(BPolinomio B)
String toString()
BPolinomio leer(String txt)

import java.math.BigInteger;
final class BPolinomio
{
public final static BPolinomio ZERO = new BPolinomio(BigInteger.ZERO, 0);
public final static BPolinomio ONE = new BPolinomio(BigInteger.ONE, 0);

BigInteger[] coef; // coeficientes


int deg; // deg del polinomio (0 para el polinomio nulo)

// a * x^b
public BPolinomio(BigInteger a, int b)
{
coef = new BigInteger[b+1];// 0+a_1x+a_2x^2+...+a_nx^n
for (int i = 0; i < b; i++)
{
coef[i] = BigInteger.ZERO;
}
coef[b] = a;
deg = degree();
}
// a * x^b
public BPolinomio(int a, int b)
{
coef = new BigInteger[b+1];// 0+a_1x+a_2x^2+...+a_nx^n
for (int i = 0; i < b; i++)
{
coef[i] = BigInteger.ZERO;
}
coef[b] = new BigInteger(""+a);
deg = degree();
}
UNA CLASE PARA OPERACIONES CON POLINOMIOS 51

//0
public BPolinomio()
{
coef = new BigInteger[1];
coef[0] = BigInteger.ZERO;
deg = degree();
}//

//degree
public int degree()
{
int d = 0;
for (int i = 0; i < coef.length; i++)
if (coef[i].compareTo(BigInteger.ZERO) != 0) d = i;

return d;
}//

//-this
public BPolinomio negate()
{
BPolinomio a = this;
return a.times(new BPolinomio(new BigInteger(-1+""), 0));
}//

//Evaluar
public BigInteger evaluate(BigInteger xs)
{
BigInteger brp = BigInteger.ZERO;
for (int i = deg; i >= 0; i--)
brp = coef[i].add(xs.multiply(brp));

return brp;
}//

//return c = a + b en Z[x].
public BPolinomio plus(BPolinomio b)
{
BPolinomio a = this;
//0 +...+ 0*x^max(a.deg.b.deg)
BPolinomio c = new BPolinomio(BigInteger.ZERO, Math.max(a.deg, b.deg));
for (int i = 0; i <= a.deg; i++) c.coef[i] = c.coef[i].add(a.coef[i]);
for (int i = 0; i <= b.deg; i++) c.coef[i] = c.coef[i].add(b.coef[i]);
c.deg = c.degree();
return c;
}//

//return c = a - b en Z[x].
52 ALGORITMOS EN ALGEBRA.

public BPolinomio minus(BPolinomio b)


{
BPolinomio a = this;
BPolinomio c = new BPolinomio(BigInteger.ZERO, Math.max(a.deg, b.deg));
for (int i = 0; i <= a.deg; i++)
c.coef[i] = c.coef[i].add(a.coef[i]);
for (int i = 0; i <= b.deg; i++)
c.coef[i] = c.coef[i].add(b.coef[i].negate());
c.deg = c.degree();
return c;
}//

// return (a * b) en Z[x].
public BPolinomio times(BPolinomio b)
{
BPolinomio a = this;
BPolinomio c = new BPolinomio(BigInteger.ZERO, a.deg + b.deg);
for (int i = 0; i <= a.deg; i++)
{
for (int j = 0; j <= b.deg; j++)
c.coef[i+j] = c.coef[i+j].add(a.coef[i].multiply(b.coef[j]));
}
c.deg = c.degree();
return c;
}//

//return k*a (k constante) en Z[x].


public BPolinomio times(BigInteger k)
{
BPolinomio a = this;
BPolinomio c = new BPolinomio(BigInteger.ZERO, a.deg);
for (int i = 0; i <= a.deg; i++)
{
c.coef[i] = c.coef[i].add(a.coef[i].multiply(k));
}
c.deg = c.degree();
return c;
}//

//return a^k en Z[x].


public BPolinomio pow(int k)
{
BPolinomio a = this;
BPolinomio c = a;
for (int i = 1; i <= k; i++)
{
c=c.times(c);
}
c.deg = c.degree();
UNA CLASE PARA OPERACIONES CON POLINOMIOS 53

return c;
}//

public int compareTo(BPolinomio b)


{
int si =0; //0 es true, 1 false.
BPolinomio a = this;

if(a.deg != b.deg)
{
si= 1;
}else{
for (int i = 0; i <= a.deg; i++)
{
if(a.coef[i].compareTo(b.coef[i])!=0 )
{ si = 1;
break;
}
}
}
return si;
}//

//Imprimir
public String toString()
{
int j= deg;
String salida="";
while(j>=0)
{
if(coef[j].compareTo(BigInteger.ZERO)!=0)
{
if(coef[j].compareTo(BigInteger.ONE)==1){
salida=salida+"+"+coef[j];
}else if(coef[j].compareTo(BigInteger.ONE)==0){
salida=salida+"+";
}else if(coef[j].compareTo(new BigInteger("-1"))==0){
salida=salida+"-";
}else if(coef[j].compareTo(new BigInteger("-1"))==-1){
salida=salida+"-"+coef[j].negate();}
salida = salida+"x^"+j;
}
j--;
}
return salida;
}
//lectura
public static BPolinomio leer(String txt)
{
54 ALGORITMOS EN ALGEBRA.

BPolinomio salida = new BPolinomio();


String polyStr;

polyStr= normalizar(txt);

StringTokenizer termStrings = new StringTokenizer(polyStr, "+-", true);


boolean nextTermIsNegative = false;

while (termStrings.hasMoreTokens())
{
String termToken = termStrings.nextToken();

if (termToken.equals("-"))
{
nextTermIsNegative = true;
}else if (termToken.equals("+"))
{
nextTermIsNegative = false;
} else{
StringTokenizer numberStrings = new StringTokenizer(termToken, "*^", false);
BigInteger coeff = new BigInteger(""+1);
int expt;
String c1 = numberStrings.nextToken();
if (c1.equals("x"))
{
// ==> "x" or "x^n"
if (numberStrings.hasMoreTokens())
{
// ==> "x^n"
String n = numberStrings.nextToken();
expt = Integer.parseInt(n);
} else {
// ==> "x"
expt = 1;
}
} else {
// ==> "a_i" o "a_i*x" o "a_i*x^n"
String ai=c1;
coeff = new BigInteger(ai);

if (numberStrings.hasMoreTokens())
{
// ==> "a_i*x" o "a_i*x^n"
String x = numberStrings.nextToken();

if (numberStrings.hasMoreTokens())
{
// ==> "a_i*x^n"
String n = numberStrings.nextToken();
UNA CLASE PARA OPERACIONES CON POLINOMIOS 55

expt = Integer.parseInt(n);
} else {
// ==> "a_1*x"
expt = 1;
}
} else {
// ==> "a_0"
expt = 0;
}
}
//Si coeff es precedido por -
if (nextTermIsNegative)
{
coeff = coeff.negate();
}
//Acumular trminos en "salida"
int cmp = coeff.compareTo(BigInteger.ZERO);
if( cmp !=0)
{
salida = salida.plus(new BPolinomio(coeff,expt));
}
}
}
return salida;
}//

static String normalizar(String expr)


{
String str="";
//limpiaBlancos y prepara (2x^n ---> 2*x^n)
for(int j = 0; j < expr.length(); j++)
{
if(expr.charAt(j) == x)
{ str+= "*x";
}else if(expr.charAt(j) == )
{
}else str+= expr.charAt(j);
}//

return str;
}//

public static void main(String[] args)


{
System.out.print("\n\n");
BPolinomio A = new BPolinomio();
BPolinomio B = new BPolinomio();

A = B.leer("2x^3+x+1");
56 ALGORITMOS EN ALGEBRA.

B = B.leer("x^2-x+2");
System.out.println("A(x)+B(x) =" +A.plus(B)+"\n");
System.out.print("\n\n");
}
}

EJERCICIOS
2.1 La implementacin del mtodo ToString no es muy fina. Por ejemplo, el polinomio
3x3 2x2 + x1 + 2 se imprime como +3x^3-2x^2+x^1+2x^0, el resultado de sumar x2 + 1
con 1 sera x^2+0, etc. Afine este mtodo de tal manera que la impresin sea la deseable.

2.2 Implementar una clase QPolinomio para polinomios con coeficientes BigRational
con mtodos similares a la clase BPolinomio. Las operaciones con los coeficientes usan
los mtodos de la clase BigRational. El cdigo podra ser algo como

import java.util.*;
import java.math.BigInteger;
final class QPolinomio
{
public final static QPolinomio ZERO = new QPolinomio(BigRational.ZERO, 0);
public final static QPolinomio ONE = new QPolinomio(BigRational.ONE, 0);

BigRational[] coef; // coeficientes


int deg; // grado del polinomio (0 for the zero polynomial)

// a * x^b
public QPolinomio(BigRational a, int b)
{
coef = new BigRational[b+1];// 0+a_1x+a_2x^2+...+a_nx^n
for (int i = 0; i < b; i++)
{
coef[i] = BigRational.ZERO;
}
coef[b] = a;
deg = degree();
}
...

public int degree()


{
int d = 0;
for (int i = 0; i < coef.length; i++)
if (coef[i].compareTo(BigRational.ZERO) != 0) d = i;

return d;
}//
EJERCICIOS 57

//return c = a + b
public QPolinomio plus(QPolinomio b)
{
Qpolinomio a = this;
//0 +...+ 0*x^max(a.deg.b.deg)
Qpolinomio c = new Qpolinomio(BigRational.ZERO, Math.max(a.deg, b.deg));
for (int i = 0; i <= a.deg; i++) c.coef[i] = c.coef[i].plus(a.coef[i]);
for (int i = 0; i <= b.deg; i++) c.coef[i] = c.coef[i].plus(b.coef[i]);
c.deg = c.degree();
return c;
}//

...

2.1.5 Algoritmo de Euclides. Divisin de Polinomios.

Recordemos que si A, B K[x] son polinomios con coeficientes en el campo K, entonces


existen dos polinomios Q y R tales que

A = BQ + R con grado(R) < grado(B).

La divisin usual de polinomios requiere, en general, que K sea un campo pues requerimos
divisiones. Por ejemplo, podemos hacer la divisin usual de polinomios en Q[x] y Z p [x]
con p primo.

En la divisin de polinomios aparecen tres sucesiones: residuos parciales Ri (que son los
nuevos dividendos), cocientes parciales Qi y los monomios Mi que se obtienen de dividir
el monomio de mayor exponente del nuevo dividendo por el monomio de mayor exponente
en B. El proceso de dividir A(x) por B(x) se puede esquematizar as,

R0 = A(x) B(x), Q0 = 0
B M1 M1 , Q1 = Q0 + M 1
R1 = R0 B M1 M2 , Q2 = Q1 + M 2
B M2
R2 = R1 B M2 M3 , Q3 = Q2 + M 3
.. .. .. ..
. . . .
Rl = Rl1 B Ml Ml , Ql = Ql1 + Ml

M j es el resultado de dividir el monomio de mayor exponente del nuevo dividendo R j1


por el monomio de mayor exponente en B.
58 ALGORITMOS EN ALGEBRA.

El proceso termina cuando el residuo parcial Rk tiene grado menor que el divisor B. En
este caso A(x) = Qk (x) B(x) + Rk (x).

EJEMPLO 2.1

Dividir A(x) = 2x5 x4 + 3x3 + 4x2 x + 1 por B(x) = x3 + 2x + 3,

R0 = 2x5 x4 + 3x3 + 4x2 x + 1 x3 + 2x + 3, Q0 = 0


2x5 4x3 6x2 M1 = 2x2 , Q1 = Q0 + M1
R1 = R0 B M1 = x4 x3 2x2 x + 1 M2 = x, Q2 = Q1 + M2
x4 + 2x2 + 3x
R2 = R1 B M2 = x3 + 2x + 1 M3 = 1, Q3 = Q2 + M3
x3 + 2x + 3
R3 = R2 B M3 = 4x + 4

Entonces, A(x) = Q3 (x)B(x) + R3 (x) = (2x2 x 1)(x3 + 2x + 3) + 4x + 4

2.1.6 Algoritmo e Implementacin.


En este algoritmo, cp(A) es el coeficiente principal de A(x), es decir cp(A) = an . El
algoritmo asume que el grado del polinomio nulo no es ni positivo ni cero, esto permite
salir del ciclo while cuando dividimos por un polinomio de grado cero.

Algoritmo 2.2: Divisin de Polinomios en K[x], K campo.


Entrada: Polinomios A, B en K[x]
Salida: Polinomios Q, R tal que A = Q B + R con grado(R)< grado(B)
1 R = A;
2 Q = 0;
3 while grado(R) grado(B) do
4 = grado(R) grado(B);
cp(R)
M= x ;
5 cp(B)
6 Q = Q + M;
7 R = R B M;
8 return Q, R.

2.1.7 Implementacin en Java para el caso Q[x].

Lo que deberamos implementar son dos mtodos, uno que devuelva el cociente Q(x) y otro
que devuelva el residuo R(x). Estos mtodos deber ser agregados a la clase QPolinomio
EJERCICIOS 59

establecida en el ejercicio 2.2.

Implementacin del mtodo QuoQPolinomio

El mtodo QuoQPolinomio de la clase QPolinomio recibe dos polinomios con coeficientes


en Q y devuelve el cociente Q(x).

El ciclo while se detiene cuando grado(R) grado(B); hay que agregar un caso especial
pues esta condicin no se cumple si B es un polinomio constante. El residuo sera R 0 y
la clase QPolinomio le asignara grado cero a R, el resultado es que el ciclo no se detendra.

//M\etodo de la clase QPolinomio


public QPolinomio QuoQPolinomio(QPolinomio B)
{ QPolinomio A = this;
int n = A.deg;
int m = B.deg;
BigRational d,bn;
QPolinomio Q,R,M;

R = A; Q=Qpolinomio.ZERO;
bn = B.coef[B.deg];

if(B.compareTo(QPolinomio.ZERO) == 0)
throw new RuntimeException("Divisi\on por cero");
// B es constante
if(B.deg == 0) //reciprocal es un m\etodo de BigRational
{ M= new QPolinomio((B.coef[B.deg]).reciprocal(),0);
return A.times(M);
}

if(m > n) return QPolinomio.ZERO;


//else

while(R.deg >= B.deg)


{
d = (R.coef[R.deg]).divide(bn);
M = new QPolinomio(d, R.deg-B.deg);
Q = Q.plus(M);
R =R.minus(B.times(M));
}
return Q;
}

EJEMPLO 2.2

Consideremos A(x) = 2x5 x4 +3x3 +4x2 x+1, B(x) = x3 +2x+3. A.QuoQPolinomio(B)


devuelve el cociente Q(x) = 2x2 x 1.
60 ALGORITMOS EN ALGEBRA.

EJERCICIOS
2.3 En la clase QPolinomio, implementar el mtodo RemQPolinomio que devuelve el
resto de la divisin.

2.4 Implemente un mtodo en la clase BPpolinomio que aplicado a un polinomio


A(x) = ai xi Z devuelve el polinomio (ai mod p) xi Z p [x]. Para referencia futura,
el mtodo ser BPolinomio toMod(BigInteger p)
2.5 En la clase BPolinomio, implementar el mtodo QuoBPolinomio(B,p) que de-
vuelve el cociente de la divisin de A(x) por B(x), A, B Z p [x] con p primo. La
implementacin es casi una copia al carbn de la implementacin que hicimos para Q[x].

Si cpA y cpB denotan los coeficientes principales del nuevo dividendo y del polinomio B,
respectivamente, entonces la divisin de coeficientes se hace con el mtodo modInverse:
cpA.multiply(cpB.modInverse(p)). Recordemos que, por ejemplo, 5 no tiene in-
verso en Z5 [x]. Para efectos de dividir polinomios, si el divisor es 5x3 + 2x + 1, se debe
usar 2x + 1. Por esta razn, se debe aplicar el mtodo toMod al divisor y a cada nuevo
dividendo (residuo) R.

Es deseable retornar Q.toMod(p).

2.2 MXIMO COMN DIVISOR (MCD) DE DOS POLINOMIOS.

2.2.1 Teora Preliminar


Recordemos que un campo es un lugar donde usted puede sumar, restar, multiplicar y
dividir. Para calcular el mximo comn divisor de dos polinomios en F[x] necesitamos
que este dominio sea al menos un dominio de factorizacin nica (algo menos que un
campo pero ms que un dominio integral). Luego, el clculo eficiente de el MCD de
dos polinomios requiere descomponer stos en una parte unitaria y una parte normal.
Seguidamente, la parte normal se separa en una parte puramente numrica (el contenido)
y una parte puramente polinomial (la parte primitiva). Tenemos que establecer todos estos
conceptos.

Un anillo conmutativo (R, +, ) es un conjunto no vaco R cerrado bajo las operaciones


binarias + y , tal que (R, +) es un grupo abeliano, es asociativa, conmutativa y
tiene una identidad y satisface la ley distributiva.

Un dominio integral D es un anillo conmutativo con la propiedad adicional (ley de can-


celacin).

a b = a c y a 6= 0 = b = c.

Un dominio Euclidiano D es un dominio integral dotado de divisin euclidiana: Para


cada a D, (a 6= 0) se puede definir una medida denotada v(a) que corresponde a un
entero no negativo tal que
MXIMO COMN DIVISOR (MCD) DE DOS POLINOMIOS. 61

1. v(a) v(ab) si b 6= 0;

2. Para todo a, b 6= 0 en D, existe q, r D (el cociente y el residuo) tal que

a = qb + r con r = 0 o v(r) < v(b).

EJEMPLO 2.3

Z es un dominio euclidiano con v(n) = |n|.

Si F es campo, F[x] es un dominio euclidiano con v(P) = grado P.

Z[x] es un dominio integral pero no un dominio euclidiano (el problema es la divisin


de coeficientes en la divisin de polinomios)

Para hablar de un dominio de factorizacin nica debemos definir la parte normal, parte
unitaria y los irreducibles (primos).

2.2.1.1 Parte normal y Parte Unitaria. Sea D un dominio integral.

1. u D es una unidad si es invertible, es decir si u1 D.

2. En D, decimos que a divide a b si existe c D tal que b = ca.

3. Dos elementos a, b D se dicen asociados si a divide a b y b divide a a, simbli-


camente a|b y b|a.

4. a, b D son asociados si y slo si existe una unidad u D tal que a = ub (y


entonces au1 = b ).

5. La relacin es asociado de es una relacin de equivalencia. Decimos que esta


relacin descompone D en clases de asociados.

6. En cada dominio particular D, se define una manera de escoger el representante de cada clase.
A cada representante de clase se le llama una unidad normal.

EJEMPLO 2.4

Puede haber confusin con los conceptos unidad y unidad normal, as que se debe
tener un poco de cuidado para no confundir las cosas.
62 ALGORITMOS EN ALGEBRA.

En Z, la particin que induce la relacin es asociado de es

{0}, {1, 1}, {2, 2}, ...

Definimos las unidades normales (representantes de clase) como los enteros no neg-
ativos 0, 1, 2, ...

As, 0 no es una unidad, pero es una unidad normal.

En los dominios de inters que tratamos aqu, siempre 0 es una unidad normal. Por
otra parte, 1 es la unidad normal que representa a la clase de las unidades. Tambin
el producto de unidades normales es una unidad normal.

Definicin 2.1 En un dominio de integridad D, en el que se ha definido cmo elegir las


unidades normales, la parte normal es la unidad normal (representante) de la clase que
contiene a a D y se se denota n(a)

La parte unitaria de a D {0} se denota u(a) y se define como la nica unidad en D


tal que a = u(a)n(a).

Lo que hacemos ahora es definir las unidades normales en varios conjuntos,

1. En Z, n(a) = |a| y u(a) = sign(a) pues a = n(a)u(a).

2. En Q, las unidades normales son 0 y 1. Esto es as pues todo elemento no nulo en


Q es una unidad y por lo tanto est asociado al 1. Por otra parte, u(a) = a.

A(x)
3. Si F es campo y A(x) = an xn + ... + a0 F[x], n(A(x)) = y u(A(x)) = an . Es
an
decir, si F es campo, las unidades normales son polinomios mnicos.

4. Las unidades normales en D[x] son polinomios cuyo coeficiente principal es una
unidad normal en D.

5. Si A(x) = an xn +...+a0 Z[x] entonces u(A(x)) = sign(an ) y n(A(x)) = u(A(x))A(x),


es decir, las unidades normales en Z[x] son polinomios con coeficiente principal en
{1, 2, ...}
MXIMO COMN DIVISOR (MCD) DE DOS POLINOMIOS. 63

EJEMPLO 2.5

1. Si A(x) = 4x3 10x2 + 44x 30 Z[x], n(A(x)) = 4x3 + 10x2 44x + 30.

2. Si A(x) = 4x3 10x2 + 44x 30 Q[x], n(A(x)) = x3 + 5/2x2 11x + 15/2 y


u(a(x)) = 4.

2.2.1.2 Mximo Comn Divisor

Definicin 2.2 En un dominio Integral D, si a|bi , i = 1, 2, ..., n, a se dice un comn di-


visor de los bi s. Finalmente, si d es un divisor comn de b1 , b2 , ...bn , y si cualquier otro
divisor comn de los bi s divide a d, entonces d se dice un mximo comn divisor de
b1 , b2 , ...bn .

Esta definicin no garantiza unicidad del mximo comn divisor. Por ejemplo, 14 y 14
cumplen con la definicin de mximo comn divisor de 84, 140 y 210 en Z. Claro, en
los enteros logramos la unicidad pidiendo que el mximo comn divisor sea positivo.

Desde le punto de vista de la matemtica, la no-unicidad del mximo comn divisor puede
ser fastidioso pero de ninguna manera daino. Desde el punto de vista del software s
necesitamos unicidad, pues necesitamos implementar un funcin MCD(a,b) con una nica
salida.

La unicidad la logramos agregando una propiedad ms en la definicin. Solo hay que notar
que si a, a0 D son divisores comunes de b1 , b2 , ..., bn entonces a y a0 son asociados, es
decir a = ub y b = u1 a para alguna unidad u D. Como la relacin es asociado de es
una relacin de equivalencia, pedimos que, adicionalmente, el mximo comn divisor sea
el representante de su clase (unidad normal).

En el ejemplo anterior, 14 y 14 son asociados, pertenecen a la clase 14, 14, y elegimos


como representante de clase a los positivos.

Definicin 2.3 En un dominio de integridad D, en el que se ha definido cmo elegir las


unidades normales, un elemento d se llama mximo comn divisor normalizado de a y b
si es una unidad normal y cumple la definicin 2.2.

De ahora en adelante, esta ser nuestra definicin de MCD (a, b) .

Una manera sencilla (pero ineficiente) para calcular el MCD de dos polinomios es obtener
la descomposicin de cada polinomio como un producto normalizado de irreducibles .
64 ALGORITMOS EN ALGEBRA.

Definicin 2.4 Sea D un dominio de integridad. Un elemento p D {0} se dice primo


(o irreducible) si p no es una unidad y si p = ab entonces o a es una unidad o b es una
unidad. Dos elementos a, b D se dicen primos relativos si MCD (a, b) = 1

Definicin 2.5 Un dominio integral D se dice dominio de factorizacin nica (DFU) si


para todo a D {0}, o a es una unidad o a se puede expresar como un producto de
primos (irreducibles) tal que esta factorizacin es nica excepto por asociados y reorde-
namiento.

La factorizacin normalizada de a es a = u(a)pe11 pe22 penn donde los pi s son unidades


normales irreducibles diferentes dos a dos y los exponentes son enteros positivos.

EJEMPLO 2.6

Sean A(x) = 48x3 84x2 + 42x 36 y B(x) = 4x3 10x2 + 44x 30.

En Z[x] las factorizaciones normalizadas son,

A(x) = (2)(3)(2x 3)(4x2 x + 2),


B(x) = (1)(2)(2x 3)(x 1)(x + 5),
entonces, en Z[x], MCD (A(x), B(x)) = 4x 6.

En Q[x] las factorizaciones normalizadas son,

A(x) = 48(x 3/2)(x2 x/4 + 1/2),


B(x) = 4(x 3/2)(x 1)(x + 5),
entonces, en Q[x], MCD (A(x), B(x)) = x 3/2.

2.2.1.3 Existencia del MCD. El ejemplo 2.6 es curioso, aunque Z[x] no es un do-
minio euclidiano, se puede calcular el MCD de manera muy natural: Solo necesitamos la
factorizacin normalizada de ambos polinomios. Para asegurar la existencia del MCD solo
se necesita que el dominio D[x] sea un dominio de factorizacin nica.

Teorema 2.1 Sea D un dominio euclidiano. Si a, b D no ambos cero, entonces


MCD (a, b) existe y es nico. Adems, existen t, s D tal que MCD (a, b) = sa + tb.

Sea D un DFU. Si a, b D no ambos cero, entonces MCD (a, b) existe y es nico.

En particular, un dominio euclidiano es un DFU.


MXIMO COMN DIVISOR (MCD) DE DOS POLINOMIOS. 65

2.2.2 MCD en Dominios Euclidianos. Algoritmo de Euclides y Algoritmo


Extendido de Euclides.

2.2.2.1 Introduccin Podemos ver el algoritmo de Euclides para calcular el MCD


de dos polinomios A(x) y B(x), con coeficientes en un campo, como una construccin de
una sucesin de residuos. Si grado A(x) grado B(x), entonces el algoritmo de Euclides
construye una sucesin de polinomios R0 (x), R1 (x), ..., Rk (x). La inicializacin de esta
sucesin es R0 (x) = A(x), R1 (x) = B(x). Luego,

R0 (x) = R1 (x)Q1 (x) + R2 (x) grado R2 (x) < grado R1 (x)


R1 (x) = R2 (x)Q2 (x) + R3 (x) grado R3 (x) < grado R2 (x)

Rk2 (x) = Rk1 (x)Qk1 (x) + Rk (x) grado Rk (x) < grado Rk1 (x)
Rk1 (x) = Rk (x)Qk (x) + 0

Entonces n(Rk (x)) = MCD (A(x), B(x))

EJEMPLO 2.7

Sean A(x) = x5 32 y B(x) = x3 8, polinomios en Q[x].

El proceso requiere divisin usual de polinomios.

1. R0 (x) = x5 32

2. R1 (x) = x3 8

x5 32 x3 8
Dividimos R0 (x) por R1 (x), x2
residuo: 8x2 32

3. R2 (x) = 8x2 32.

x3 8 8x2 32
Ahora, dividimos R1 (x) por R2 (x), x/8
residuo: 4x 8

4. R3 (x) = 4x 8.
66 ALGORITMOS EN ALGEBRA.

8x2 32 4x 8
Ahora, dividimos R2 (x) por R3 (x), 2x + 4
residuo: 0

5. R4 (x) = 0.


Finalmente, MCD x5 32, x3 8 = n(R3 (x)) = R3 (x)/4 = x 2.

EJEMPLO 2.8

Sean A(x) = x8 + x6 3x4 3x3 + 8x2 + 2x 5 y B(x) = 3x6 + 5x4 4x2 9x + 21.
Si aplicamos el algoritmo de Euclides para calcular el MCD (A(x), B(x)) , se obtiene
la siguiente sucesin de residuos,

R3 (x) = 117/25x2 9x + 441/25,

R4 (x) = 233150/19773x 102500/6591,

R5 (x) = 1288744821/543589225.

Por lo tanto, MCD (A, B) = 1 (pues el mximo comn divisor es un unidad en Q[x] ).

El algoritmo de Euclides no solo requiere que el dominio de coeficientes sea un campo,


adems en este ltimo ejemplo se puede observar uno de los problemas de este algoritmo:
El crecimiento de los coeficientes.

2.2.3 Algoritmo e Implementacin.

Algoritmo 2.3: Algoritmo de Euclides


Entrada: Polinomios A, B en F[x] , F campo.
Salida: MCD (A, B) normalizado
1 C = n(A);
2 D = n(B);
3 while D 6= 0 do
4 R = rem(C, D) ;
5 C = D;
6 D = R;
7 return n(C)
MXIMO COMN DIVISOR (MCD) DE DOS POLINOMIOS. 67

De ms inters para nosotros es el algoritmo extendido de Euclides,

Algoritmo 2.4: Algoritmo Extendido de Euclides


Entrada: Polinomios A, B en F[x] , F campo.
Salida: MCD(A, B) normalizado, T, S F[x] tal que MCD (A, B) = SA + T B
1 C = n(A), D = n(B) ;
2 C1 = 1, D1 = 0 ;
3 C2 = 0, D2 = 1 ;
4 while D 6= 0 do
Q = quo(C, D), R = C QD,
R1 = C1 QD1 , R2 = C2 QD2 ,
C = D, C1 = D1 , C2 = D2 ,
5 D = R, D1 = R1 , D2 = R2 ,
6 G = n(C)
;
C1 C2
return MCD = G, S = T= ;
7 u(A) u(C), u(B) u(C)

Recordemos que n(A) = A/an y u(A) = an .

Este algoritmo lo implementamos como un mtodo de la clase QPolinomio.

public static QPolinomio[] QuoQPolinomioExt(QPolinomio a, QPolinomio b)


{
QPolinomio[] salida1 = new QPolinomio[3];
QPolinomio an = new QPolinomio(a.coef[a.deg],0);
QPolinomio bn = new QPolinomio(b.coef[b.deg],0);
QPolinomio cn;
QPolinomio c,d,c1,d1,c2,d2,q,r,r1,r2,s,t;

c = a.QuoQPolinomio(an); //A(x)/a_n
d = b.QuoQPolinomio(bn);
c1 = QPolinomio.ONE;
d1 = QPolinomio.ZERO;
c2 = QPolinomio.ZERO;
d2 = QPolinomio.ONE;

int j=1;
while(d.compareTo(QPolinomio.ZERO)!=0)
{
q = c.QuoQPolinomio(d); //quo(c,d)
r = c.plus(q.times(d).negate());
r1= c1.plus(q.times(d1).negate());
r2= c2.plus(q.times(d2).negate());
c = d;
68 ALGORITMOS EN ALGEBRA.

c1= d1;
c2= d2;
d = r;
d1= r1;
d2= r2;
j++;
}
cn = new QPolinomio(c.coef[c.deg],0);
c = c.QuoQPolinomio(cn);
salida1[0]=c;
salida1[1]=c1.QuoQPolinomio(an.times(cn));
salida1[2]=c2.QuoQPolinomio(bn.times(cn));
return salida1;
}

EJEMPLO 2.9

Si A(x) = x2 + 2x + 1 y B(x) = 3x3 + x 2. QuoQPolinomioExt(A, B) devuelve

{1, 5x2 /6 x/3 + 1/9, 5x/18 4/9}

EJERCICIOS
2.6 Implementar, en la clase BPolinomio, el algoritmo 2.4 con F = Z p . Para referencia
futura, el mtodo debe llamarse

QuoBPolinomioExt(BPolinomio a, BPolinomio b, BigInteger p)

2.2.4 MCD en un DFU. Algoritmo Primitivo de Euclides


Sea D[x] un DFU, lo que queremos es encontrar el MCD usando solo aritmtica en este
dominio ms bien que trabajar en el campo cociente de D como nuestro campo de coefi-
cientes. Porqu?, bueno ya vimos que si queremos encontrar el MCD de dos polinomios
en Z[x] no podemos recurrir del todo a Q[x] porque aqu el MCD de los mismos polinomios
da otro resultado.

Si A(x) y B(x) son polinomios en el DFU D[x], una manera de usar solo aritmtica en
D para calcular el MCD es construir una sucesin de seudo-residuos (denotados prem)
usando la llamada seudo-divisin: Si grado(A(x)) = m y grado(B(x)) = n ( m n ),
aplicamos el algoritmo de Euclides, a los polinomios mn+1 A(x) y B(x) donde es el
coeficiente principal de B(x).

Tomamos mn+1 A(x) pues los dividendos (seudo-residuos) tendrn grados iguales o in-
feriores a m, m 1, ..., y nos detendremos en el seudo-residuo de grado igual o inferior a
n 1. Esto nos da a lo sumo m (n 1) = m n + 1 divisiones de coeficientes principales.
EJERCICIOS 69

De esta manera, la divisin del coeficiente principal de cada seudo-residuo con el coefi-
ciente principal de B siempre es exacta en D, hasta que llegamos al final de la divisin.

Teorema 2.2 (Propiedad de Seudo-Divisin en un DFU)

Sea D[x] un dominio de polinomios sobre un DFU D. Para todo A(x), B(x) D[x]
con B(x) no nulo y grado (A(x)) = m grado (B(x)) = n, existen polinomios nicos
Q(x), R(x) D[x] (llamados seudo-cociente y seudo-residuo) tal que

mn+1 A(x) = B(x)Q(x) + R(x), grado (R(x)) < grado (B(x))

donde es el coeficiente principal de B(x).

EJEMPLO 2.10

Sean A(x) = x8 + x6 3x4 3x3 + 8x2 + 2x 5 y B(x) = 3x6 + 5x4 4x2 9x + 21


en Z[x].

En este caso = 3 y m n + 1 = 3. Entonces, en vez de dividir A(x) por B(x) (que


no se puede en Z[x] ), dividimos

33 (x8 + x6 3x4 3x3 + 8x2 + 2x 5) por 3x6 + 5x4 4x2 9x + 21.

La divisin es exacta aunque el dominio de coeficientes sea Z. Obviamente, el prob-


lema del crecimiento de los coeficientes en los residuos va a empeorar, de hecho los
coeficientes de los residuos crecen exponencialmente. En este caso, los dos ltimos
seudo-residuos (usando seudo-divisin) son

i Ri (x)
4 125442875143750 x 1654608338437500
5 125933387955500743100931141992187500

El resultado al que llegamos es MCD (A(x), B(x)) = 1.

La seudo-divisin resuelve el problema de aplicar el algoritmo de Euclides en un DFU.

El problema del crecimiento de los coeficientes lo podemos resolver en una primera in-
stancia y a un costo relativamente alto, dividiendo el seudo-residuo i + 1 por el mximo
comn divisor de sus coeficientes.
70 ALGORITMOS EN ALGEBRA.

Definicin 2.6 Un polinomio no nulo A(x) D[x], con D[x] DFU, se dice primitivo si es
una unidad normal en D[x] y si sus coeficientes son primos relativos. En particular, si
A(x) solo tiene un trmino no nulo entonces es primitivo si y slo si es mnico.

El contenido de A(x) se denota cont(A(x)) y se define como el MCD de los coeficientes de


A(x)

La parte primitiva de A(x) se denota pp(A(x)) y se obtiene de la relacin

A(x) = u (A(x)) n (A(x)) = u (A(x)) cont (A(x)) pp (A(x))

Es conveniente definir cont(0) = 0 y pp(0) = 0.

Observe que si D es un campo y si A(x) no es el polinomio nulo,

el MCD de los coeficientes de A es 1, es decir, cont(A(x)) = 1.

pp(A(x)) = n (A(x)).

EJEMPLO 2.11

1. Consideremos A(x) = 48x3 84x2 + 42x 36 y B(x) = 4x3 10x2 + 44x 30.

En Z[x],

(a) u(A) = 1, cont(A) = 6 y pp(A) = 8x3 14x2 + 7x 6

(b) u(B) = 1, cont(B) = 2 y pp(B) = 2x3 + 5x2 22x + 15.

En Q[x],

(a) u(A) = 48, cont(A) = 1 y pp(A) = x3 7/4x2 + 7/8x 3/4.


(b) u(B) = 4, cont(B) = 1 y pp(B) = x3 + 5/2x2 11x + 15/2.

El MCD lo podemos calcular en trminos del MCD de los contenidos y el MCD de las
partes primitivas.
EJERCICIOS 71

Teorema 2.3 (Lema de Gauss)

1. El producto de polinomios primitivos es primitivo

2. MCD (A, B) = MCD (cont(A), cont(B)) MCD (pp(A), pp(B))

El clculo de MCD (cont(A), cont(B)) se hace en D, as que nos podemos concentrar en el


clculo del MCD de polinomios primitivos, es decir, en el clculo de MCD (pp(A), pp(B))

Para efectos de implementacin, usamos la notacin pquo (A, B) para el seudo-cociente


y prem (A, B) para el seudo-residuo.

Es conveniente extender la definicin de pquo y prem para el caso grado (A) <
grado (B), haciendo pquo (A, B) = 0 y prem (A, B) = a(x).

pquo y prem se obtienen haciendo la divisin de polinomios usual (entre mn+1 A(x)
y B(x) ), solo que ahora la divisin es exacta en el dominio de coeficientes D.

La propiedad de seudo-divisin nos da, de manera directa, un algoritmo para calcular el


MCD en D[x] con D DFU. Como habamos notado antes, basta con restringir nuestra
atencin a la parte primitiva de los polinomios, es decir nos restringimos al clculo del
MCD para polinomios primitivos.

Teorema 2.4 Sea D[x] un dominio de polinomios sobre un DFU. Dados polinomios prim-
itivos A(x), B(x) D[x] con B(x) no nulo y grado (A(x)) grado (B(x)), sean Q(x) y
R(x) el seudo-cociente y el seudo-residuo, entonces

MCD (A, B) = MCD (B, pp(R)) (2.1)

2.2.4.1 Algoritmo e Implementacin. La ecuacin 2.1 define un mtodo de it-


eracin para calcular el MCD de dos polinomios primitivos en D[x] y esta iteracin es
finita pues grado (R) < grado (B) en cada paso.
72 ALGORITMOS EN ALGEBRA.

Algoritmo 2.5: Algoritmo Primitivo de Euclides.


Entrada: Polinomios A(x), B(x) D[x], D DFU.
Salida: G(x) = MCD (A(x), B(x))
1 C(x) = pp(A(x)) ;
2 D(x) = pp(B(x)) ;
3 while D(x) 6= 0 do
4 R(x) = prem(C(x), D(x)) ;
5 C(x) = D(x) ;
6 D(x) = pp(R(x)) ;

7 = MCD(cont(A(x)), cont(B(x))) ;
8 G(x) = C(x) ;
9 return G(x) ;

EJEMPLO 2.12

Sean A(x) = x8 + x6 3x4 3x3 + 8x2 + 2x 5 y B(x) = 3x6 + 5x4 4x2 9x + 21.

En el DFU Z[x], la sucesin de valores calculada por el algoritmo para R(x), C(x) y
D(x) es

Tabla 2.1 Algoritmo Primitivo de Euclides aplicado a A(x) y B(x)

n R(x) C(x) D(x)


0 x8 + x6 3x4 3x3 + 8x2 + 2x 5 3x6 + 5x4 4x2 9x + 21
1 27x8 + 27x6 81x4 81x3 + 216x2 + 54x 135 x6 + 5x4 4x2 9x + 21
2 5x4 x2 + 3 375x6 + 625x4 500x2 1125x + 2625 5x4 x2 + 3
3 13x2 + 25x 49 10985x4 2197x2 + 6591 13x2 + 25x 49
4 4663x 6150 282666397x2 + 543589225x 1065434881 4663x 6150
5 0 4663x 6150 1

Lo que retorna el algoritmo es 1. Observe que A(x) y B(x) son primitivos, as que
no hay cambio en la iteracin n = 0.

La implementacin la haremos como un mtodo de la clase BPolinomio. Requiere tres


mtodos iniciales: QuoBPolinomio para hacer la divisin de polinomios en Z[x], cont
para el contenido y toPP para calcular la parte primitiva. QuoBPolinomio es la divisin
de polinomios usual solo que en vez de calcular an /bn Q, calcula quo(an , bn ) Z. As
que la divisin entre mn+1 A(x) y B(x) le queda perfecta.
EJERCICIOS 73

En Z[x] el contenido es el mximo comn divisor de los coeficientes y u(A) = sgn(an ),


A
entonces A = u (A) cont (A) pp (A) = pp (A) = u(A) .
cont(A)
Finalmente, recordemos que MCD (A, B) = MCD (cont(A), cont(B)) MCD (pp(A), pp(B)) .

//Divisi\on usual en Z[x]


public BPolinomio QuoBPolinomio(BPolinomio B)
{ BPolinomio A = this;
int m = A.deg;
int n = B.deg;
BigInteger d,bn;
BPolinomio Q,R,M;

R = A; Q=BPolinomio.ZERO;
bn = B.coef[n];

if(B.compareTo(BPolinomio.ZERO) == 0)
throw new RuntimeException("Divisin por cero");

if(B.deg == 0)
{for (int i = 0; i <= m; i++)
A.coef[i] = (A.coef[i]).divide(bn);//divisi\on entera
return A;
}

if(n>m) return BPolinomio.ZERO;


//else
while(R.deg >= B.deg)
{
d = (R.coef[R.deg]).divide(bn);
M = new BPolinomio(d, R.deg-B.deg);
Q= Q.plus(M);
R=R.minus(B.times(M));
}
return Q;
}//

//Contenido en Z[x]
public BigInteger cont()
{
BPolinomio a = this;
BigInteger mcd = BigInteger.ZERO;
int dega = a.deg;

if(dega==0)
{ mcd= a.coef[dega]; //0 si 0, k si kx^0.
}else{
mcd = a.coef[0].gcd(a.coef[1]);
74 ALGORITMOS EN ALGEBRA.

if(dega >1)
for (int i = 2; i <= dega; i++)
mcd = mcd.gcd(a.coef[i]);
}
return mcd;
}

//Parte primitiva pp(A), A en Z[x]


public BPolinomio toPP()
{
BPolinomio a = this;
int dega = a.deg;
BPolinomio c = new BPolinomio(a.coef[dega], dega);
BigInteger contenido = a.cont();
BigInteger sgn = new BigInteger(""+(a.coef[dega]).signum());

if(dega==0)
{if(a.coef[dega].compareTo(BigInteger.ZERO)==0)
return BPolinomio.ZERO;
if(a.coef[dega].compareTo(BigInteger.ZERO)!=0)
return BPolinomio.ONE;
}else{
for (int i = 0; i <= dega; i++)
{c.coef[i] = a.coef[i].divide(contenido);
c.deg = c.degree();
}
}
return c.times(sgn);
}

//A, B en Z[x] y devuelve MCD(A,B)


public BPolinomio BPolinomioMCD_Primitivo(BPolinomio b)
{
BPolinomio a = this;
BigInteger lda = (a.cont()).gcd(b.cont());//BigInteger
BPolinomio c = a.toPP();
BPolinomio d = b.toPP();
BPolinomio r,q;
BigInteger cpd;
int degc,degd;

while(d.compareTo(BPolinomio.ZERO)!=0)
{ degc = c.deg;
degd = d.deg;
cpd = new BigInteger(""+d.coef[degd]);
c = c.times(cpd.pow(Math.abs(degc-degd)+1));
q = c.QuoBPolinomio(d);
r = c.minus(q.times(d)); //prem(c,d)-> r=c-dq
EJERCICIOS 75

c = d;
d = r.toPP();
}
return c.times(lda);
}//

Este algoritmo, por supuesto, tambin se puede usar en el dominio Euclidiano F[x] (F
campo).

2.2.5 MCD en un DFU. Algoritmo PRS y el Algoritmo PRS Subresultante

En el algoritmo primitivo de Euclides, en cada iteracin calculamos

Ri (x) = prem(C(x), D(x))

es decir, en cada iteracin se hace seudo-divisin de pp(Ri1 (x)) por pp(Ri (x)) .

As, el algoritmo construye una sucesin de seudo-residuos R0 (x), R1 (x), ..., Rk (x) con ini-
cializacin R0 (x) = pp(A(x)), R1 (x) = pp(B(x)) y

1 R0 (x) = R1 (x)Q1 (x) + Q2 (x)


2 R1 (x) = R2 (x)Q2 (x) + R3 (x)

k1 Rk2 (x) = Rk1 (x)Qk1 (x) + Rk (x)
k Rk1 (x) = Rk (x)Qk (x).

con i = rii +1 donde ri = cp(Ri (x)) (cp =coeficiente principal) y i = grado Ri1 (x) grado Ri (x).

EJEMPLO 2.13

Usando como referencia el ejemplo 2.12, en la tabla 2.2 se puede observar la relacin
entre los coeficientes principales de los residuos Ri (x) y Ri1
76 ALGORITMOS EN ALGEBRA.

Tabla 2.2 Ri (x) = prem (C(x), D(x))


i Ri (x) i Ri1
0
1 33 R0 (x) = 27x8 + 27x6 81x4 81x3 + 216x2 + 54x 135
2 5x4 x2 + 3 53 3 R1 (x) = 375x6 + 625x4 500x2 1125x + 2625
3 13x2 + 25x 49 133 5 R 2 (x) = 10985x4 2197x2 + 6591
4 4663x 6150 46633 13 R3 (x) = 282666397x2 + 543589225x 1065434881

Esta sucesin de residuos es un caso particular de un caso ms general, las llamadas Suce-
siones de Residuos Polinomiales (PRS).

Definicin 2.7 (Sucesin de Residuos Polinomiales)


Sean A(x), B(x) D[x] DFU, con grado A(x) grado B(x). Una sucesin de residuos
polinomiales (PRS, por sus siglas en ingls) para A(x) y B(x) es una sucesin de poli-
nomios R0 (x), R1 (x), ..., Rk (x) R[x] que satisfacen

1. R0 (x) = A(x), R1 (x) = B(x) (inicializacin).

2. i Ri1 (x) = Qi (x)Ri + i Ri+1 (x)

3. prem(Rk1 (x), Rk (x)) = 0 .

El principal objetivo en la construccin de PRS para dos polinomios dados es, adems de
mantener todas las operaciones en el dominio D, escoger i de tal manera que los coe-
ficientes de los residuos se mantengan tan pequeos como sea posible y que este proceso
sea lo ms barato (menor costo) posible. Inicialmente la teora fue desarrollada por
Sylvester y Trudi en el siglo diecinueve (mientras desarrollaban la teora de ecuaciones) y
el algoritmo PRS Subresultante es una variacin perfeccionada desarrollada por Collins y
Brown a finales de los sesentas (ver [Ged92]).

Si i = cpi i +1 donde cpi = coeficiente principal de Ri (x) y i = 1, obtenemos el llamado


PRS Euclidiano. El resultado es un crecimiento exponencial (en el nmero de bits) de los
coeficientes.

En el otro extremo,

i = cpii +1 y i = cont(prem(Rk1 (x), Rk (x)))

es decir, dividimos los seudo-residuos por el mximo comn divisor de sus coeficientes.
Esta escogencia tiene xito en mantener los coeficientes los ms pequeos posibles pero
EJERCICIOS 77

el costo de calcular los MCDs generalmente no es bajo. Esta variacin es llamada PRS
primitiva.

El siguiente paso fue tratar de hallar divisores del contenido sin calcular MCDs. Cerca
de 1970 Collins, Brown y Traub, reinventaron la teora de polinomios subresultantes como
variantes de las matrices de Sylvester y hallaron que coincidan con los residuos en el al-
goritmo de Euclides, excepto por un factor. Ellos dieron frmulas para calcular este factor
e introdujeron el concepto de PRS. El resultado final es el Algoritmo PRS Subresultante
que permite un crecimiento lineal de los coeficientes y mantiene el clculo en el dominio D .

En el Algoritmo PRS Subresultante,

i = cpi i +1 , 1 = (1)1 +1 , i = cpi1 i i , 2 i k

donde cpi es el coeficiente principal de Ri (x), i = grado Ri1 (x) grado Ri (x) y i se
1
define de manera recursiva: 1 = 1, i = (cpi1 )i1 i1 i1 ; 2 i k

Si ponemos Qi (x) = pquo(Ri1 (x), Ri (x)), entonces el siguiente residuo se calcula como

Ri+1 = quo(i Ri1 (x) Qi (x)Ri (x), i )

1
Hay que notar que en i = (cpRi1 )i1 i1 i1 se tiene 1 i1 0, pero se trata de
una divisin exacta si 1 i1 < 0 (esto fue un problema abierto hasta el 2003, [2]).

El tiempo estimado de ejecucin de este algoritmo, en algunos casos, es similar al del


algoritmo primitivo de Euclides pero con un crecimiento lineal de los coeficientes.

2.2.5.1 Algoritmo e Implentancin. En el algoritmo que sigue, se pone quo(a, b)


para indicar el cociente de la divisin usual. Hay que notar que pquo(ri1 (x), ri (x)) =
quo(i ri1 (x), ri (x))
78 ALGORITMOS EN ALGEBRA.

Algoritmo 2.6: Algoritmo PRS Subresultante.


Entrada: Polinomios a(x), b(x) D[x], grado a(x) grado b(x), D DFU.
Salida: c(x) = MCD (a(x), b(x))
1 r0 = a(x) ;
2 r1 = b(x) ;
3 deg0 = grado(r0 ), deg1 = grado(r1 ), cp0 = coeficiente principal de r0 ;
4 cp1 = coeficiente principal de r1 , 1 = deg0 deg1 , 0 = 1 ;
5 1 = cp11 +1 , 1 = (1)1 +1 , 1 = 1, 0 = 1 ;
6 while r1 6= 0 do
7 c = 1 r0 , q = quo(c, r1 ), r0 = r1 w r1 = quo(c q r1 , 1 ) ;
8 deg0 = grado(r0 ), deg1 = grado(r1 ) ;
9 cp0 = coeficiente principal de r0 ;
10 cp1 = coeficiente principal de r1 ;
11 0 = 1 , 1 = deg0 deg1 ;
12 1 = cp11 +1 , 0 = 1 ;
13 if 0 > 0 then
1
14 1 = quo(cp00 , 00 ) ;
15 else

16 1 = cp00 0
17 1 = cp0 11 ;

18 Normalizar salida;
19 r0 = MCD (cont(a(x)), cont(b(x))) pp(r0 );
20 return r0 ;

Hay que agregar la lnea 32 pues antes de esta lnea, r0 (x) es solo un asociado del
MCD (a(x), b(x)) .

EJEMPLO 2.14

Sean a(x) = x8 + x6 3x4 3x3 + 8x2 + 2x 5 y b(x) = 3x6 + 5x4 4x2 9x + 21.

Al aplicar el algoritmo PRS Subresultante, tal y como est descrito ms arriba


obtenemos los restos

i ri (x)
2 15x4 3x2 + 9
3 65x2 + 125x 245
4 9326x 12300
5 260708

EJEMPLO 2.15
ALGORITMO HEURSTICO. 79

Sean a(x) = 48x3 84x2 + 42x 36 y b(x) = 4x3 10x2 + 44x 30.

Al aplicar el algoritmo PRS Subresultante, tal y como est descrito ms arriba, antes
de la lnea 32, tenemos r0 = 1232640x 1848960. Con la normalizacin queda
r0 = MCD (6, 2) (2x 3) == 4x 6.

2.3 ALGORITMO HEURSTICO.

Primero vamos a ver una idea muy general.

Consideremos dos polinomios a(x), b(x) Z[x]. Sea Z y calculemos MCD (a(), b()) =
. Veamos que podra pasar

Sean a(x) = 4x3 16x2 + 4x + 24 y b(x) = 8x3 152x + 240.


a(x) = 4(x 3)(x 2)(x + 1),
b(x) = 8(x 3)(x 2)(x + 5),
MCD (a(x), b(x)) = 4(x 3)(x 2) = 4x2 20x + 24 en Z[x].
Sea g(x) = MCD (a(x), b(x)) .
si = 5 entonces g(5) = 48 6= 24 = MCD (a(5), b(5)) = ,
si = 10 entonces g(10) = 224 = MCD (a(10), b(10)) = ,
Si = u0 + u1 + ... + un n , ui Z , para un suficientemente grande, podra pasar que
el polinomio g(x) = u0 + u1 x + ... + un xn sea exactamente g(x).

En nuestro ejemplo,

si = 10, = 224.
224 = 4 + 2 10 + 2 102
g(x) = 4 + 2x + 2x2 pero no divide a a(x) ni a b(x).
si = 50, = 9024.
9024 = 24 20 50 + 4 502
g(x) = 24 20x + 4x2 que si divide a(x) y a b(x) y de hecho es el MCD.

La idea es reconstruir g(x) = MCD (a(x), b(x)) usando una representacin -dica de
= MCD (a(), b()) . Claro, tenemos el problema de escoger un lo ms pequeo que
se pueda de tal manera que nos produzca polinomios con el grado adecuado. En polinomios
de varias variables se debe hacer esto para cada variable, as que cada debe mantenerse
pequeo.
80 ALGORITMOS EN ALGEBRA.

2.3.1 Representacin -dica de un nmero y de un polinomio.

La representacin -dica de Z es

= u0 + u1 + ... + ud d (2.2)

con ui Z . Aqu d es el ms pequeo entero tal que d+1 > 2||.

Para el clculo de los ui s es ms conveniente la representacin simtrica de Z , a saber

Z = {i Z : /2 < i /2}

Por ejemplo, Z5 = {2, 1, 0, 1, 2} y Z6 = {2, 1, 0, 1, 2, 3}.

Esta representacin permite valores de negativos.

En el algoritmo que sigue, rem (a, b) denota el residuo de la divisin de a por b.

Algoritmo 2.7: Imagen mdulo p de un nmero en la representacin simtrica de Z p .


Entrada: m, p Z
Salida: u = m (md p) en la representacin simtrica de Z p , es decir p/2 < u p/2
1 u = rem(m, p) ;
2 if u > p/2 then
3 u = u p
4 return u ;

Para ir introduciendo notacin que usaremos en el futuro, sea p : Z Z p el homomor-


fismo definido por p (a) = a (md p), es decir el residuo de la divisin por p pero en la
representacin simtrica.

De la ecuacin 2.2, u0 (mod ) y entonces,

u0 = () (2.3)

u0 divide por lo que, de acuerdo al item anterior,

u0
= u1 + u2 + ... + un d1


u0
de donde u1 =

ALGORITMO HEURSTICO. 81

Continuando de esta manera


(u0 + u1 + ... + ui1 i1 )
ui = , i = 1, ..., d (2.4)
i

Ejemplo. = 50, y = 9024 entonces 9024 = 24 20 50 + 4 502 . Es decir


u0 = 24, u1 = 20 y u2 = 4.

La representacin dica de un polinomio a(x) Z[x] es

n
a(x) = ue xe con ue = ue,i i , ue,i Z .
e i=0

con n el entero ms pequeo tal que n+1 > 2|umax |, donde umax = maxe |ue |.
!
n
a(x) = ue xe = ue,i i xe = u0 (x) + u1 (x) + ... + un (x)n .
e e i=0

Las frmulas para el caso entero permanecen vlidas. Si : Z[x] Z [x] es el ho-
momorfismo que aplicado sobre a(x) = ai xi devuelve (a(x)) = ai (md ) xi ,
entonces
u0 (x) = (u(x))


u(x) (u0 (x) + u1 (x) + ... + ui1 (x)i1 )
ui (x) = , i = 1, ..., n
i

Ejemplo.

1. Sea a(x) = 4 + 7x 9x3 y = 6.

a(x) = 3x3 + x 2 + (2x3 + x + 1) 61


u0 (x) = 3x3 + x 2 y u1 (x) = (2x3 + x + 1).

2. Sea a(x) = 4 + x y = 4.

a(x) = x + 1 41
u0 (x) = x y u1 (x) = 1.
82 ALGORITMOS EN ALGEBRA.

El algoritmo para calcular la representacin dica de Z, sera (recuerde la definicin


de ),

Algoritmo 2.8: Representacin -dica de .


Entrada: , Z
Salida: u0 , u1 , ..., ud tal que = u0 + u1 + ... + ud d con d+1 > 2|| y
/2 < ui /2 .
1 e = ;
2 i = 0;
3 while e 6= 0 do
4 ui = (e) ;
5 e = (e ui )/ ;
6 i = i+1;

7 return u0 , u1 , ..., ud ;

Cuando necesitemos la reconstruccin de g(x), hacemos una pequea modificacin,


agregamos g = 0 y en el while actualizamos g = g + ui xi

Es necesario implementar la versin polinomial tambin. En este caso lo que se recon-


struye es un polinomio, pero en otra variable. Ms adelante veremos esto.

2.3.2 Reconstruccin del Mximo Comn Divisor

Aunque en esta primera parte solo vamos a implementar el algoritmo en una indeterminada,
vale la pena enunciar los teoremas de manera general.

Teorema 2.5
Sean P y Q dos polinomios dependiendo de las variables x1 , x2 , ..., xk con coeficientes
enteros. Sea Z tal que 2 Mn{kP|| , ||Q|| } + 2 donde ||P|| denota el co-
eficiente numrico ms grande de P (en valor absoluto). Si G es la parte primi-
tiva de la reconstruccin que se obtiene a partir de la representacin dica de =
MCD (P(x1 , ...xk1 , ), Q(x1 , ...xk1 , )) y si G divide a P y Q entonces G = MCD (P, Q) .

Prueba. Ver [?].

En realidad, para suficientemente grande (posiblemente demasiado grande), G|P


y G|Q siempre (tomando G como se indica en el teorema).

El problema es que, para que el algoritmo que se deriva sea til, debe ser razon-
ablemente pequeo. As que se admite cierta posibilidad de falla y se mantiene solo
ALGORITMO HEURSTICO. 83

la condicin esencial de divisibilidad.

Con la escogencia de , segn el teorema, se tiene un punto de partida para ir


probando polinomios. El algoritmo heurstico es un algoritmo tipo Las Vegas:
siempre entrega la respuesta correcta, pero no garantiza el tiempo total de ejecucin,
aunque con alta probabilidad, ste ser bajo.

El teorema nos dice que con esta escogencia de , reconocer un resultado correcto
o incorrecto (a partir de la reconstruccin) es solo una cuestin de dividir.

En el teorema se toma la parte primitiva de la reconstruccin pues hay polinomios


que al evaluarlos, no importa si se evalan en nmeros grandes, siempre tienen un
factor comn que es ajeno a la factorizacin y por tanto, sin remover el contenido, el
criterio de divisibilidad por P y Q fallara siempre. An removiendo el contenido,
el teorema garantiza que el test de divisibilidad decide si G es correcto o no.

Ejemplo.

Sea a(x) = x3 3x2 + 2x = (x 2)(x 1)x y b(x) = x3 + 6x2 + 11x + 6 =


(x + 1)(x + 2)(x + 3). 6 | a(), y 6 | b() para todo Z. Observe sin em-
bargo que estos polinomios son primos relativos.

Como se seal antes, en estimaciones en trminos de nmero de operaciones de bits


sobre polinomios de grado
a lo sumo n y con coeficientes de longitud a lo sumo n,
se obtuvo tiempos de O n4 para este algoritmo ([2], [1]).

Hay que recordar que este algoritmo es muy eficiente en problemas pequeos (no
ms de cuatro variables) y se usa como complemento de otros algoritmos.

En el algoritmo que sigue, usamos el homomorfismo de evaluacin (x1 ) : Z[x1 , ...xk ]


Z[x2 , ..., xk ] definido por

(x1 ) (a(x1 , ..., xn )) = a(, ..., xn )

Por ejemplo, si a(x, y) = (y2 + 1)x2 + (y + y3 ) x, entonces (x) = (y2 + 1)2 + (y + y3 ) .

Tambin, si a(x1 , x2 , ..., xn ) = ai1 ,i2 ,...,in x1i1 x2i2 xnin , entonces

||a(x1 , x2 , ..., xn )|| = Mxi1 ,i2 ,...,in {|ai1 ,i2 ,...,in |}


84 ALGORITMOS EN ALGEBRA.

Algoritmo 2.9: MCDHEU(A, B).


Entrada: A, B Z[x1 , ..., xk ]
Salida: G = MCD (A, B) si el resultado de la bsqueda heurstica da resultado, sino
retorna 1 S
1 vars = Indeterminadas(A) Indeterminadas(B) ;
2 if Card(vars) = 0 then
3 return = MCD (A, B) Z
4 else
5 x = vars[1]
6 = 2 Mn{||A|| , ||B|| } + 2 ;
7 i = 0;
8 while i < 7 do
9 if length() mx{gradox A, gradox B, } > 5000 then
10 return 1 (MCD no puede ser negativo, 1 se usa como indicador de fallo)

11 = MCDHEU((x) (A), (x) (B)) (llamada recursiva);


12 if 6= 1 then
13 Generar G a partir de la expansin dica de

14 if G|A y G|B then


15 return G

16 Crear un nuevo punto de evaluacin;


17 = quo( 73794, 27011)

18 return 1 ;

En al lnea 9 se impone una restriccin sobre la longitud de (longitud en nmero


de bits). Despus de todo, el algoritmo es heurstico, as que se trata de asegurar que
el clculo no sea demasiado costoso.

En la lnea 14, la divisin se hace en Q, es decir usando el mtodo de divisin para


polinomios con coeficientes en Q. Esto tiene como efecto remover el contenido del
divisor, resultando en un test de divisibilidad sobre los enteros. Solo hay que tener el
cuidado de dividir por el contenido de G a la hora de retornar G (en caso de xito).

La lnea 17 lo que procura es tener algn grado de aleatoriedad en la escoegencia


del siguiente punto de evaluacin de tal manera que si hay un fallo en la primera
escogencia, no haya una tendencia a que esto se repita ([?]).

El algoritmo que se presenta aqu es la versin optimizada que aparece en [?]. Aunque
manejamos una versin en Z[x1 , ..., xk ], en al implementacin solo consideramos, en
esta primera parte, el caso de polinomios en una indeterminada.

Ejemplo en Z[x, y]. Sean a(x, y) = (y2 + 1)x2 + (y + y3 ) x y b(x, y) = yx2 + (y2 +
1)x + y. El algoritmo heurstico es recursivo. En este ejemplo pasara lo siguiente:
... 85

Llamada 1. Entran A(x, y) = (y2 + 1)x2 + (y + y3 ) x y B(x, y) = yx2 + (y2 + 1)x + y.


vars = {x, y}
1 = 2 1 + 2 = 4, pues ||A|| = 1 y ||B|| = 1.
var = x

Llamada 2. 2 = MCDHEU(A1 = 16(y2 + 1) + 4(y + y3 ), B1 = 16y + 4(y2 + 1) + y)
vars = {y}
2 = 2 17 + 2 = 36, pues ||A1 || = 16 y ||B1 || = 17.
var = y

Llamada 3.. 3 = MCDHEU(A1 (36) = 207520, B1 (36) = 5800)
vars={}
Retorna 3 = 40.
Nos devolvemos hacia Llamada 2.
Llamada 2. Entra a reconstruccin de G2 con 3 = 40 y 2 = 36
var = y
Representacin 2 dica
40 = 4 + 1 361
G2 = 4 + y
Test: ( G2 | A1 (y) y G2 | B1 (y) ) true
Retorna G2 = 4 + y
Nos devolvemos hacia Llamada 1.
Llamada 1. Entra a reconstruccin de G1 con 2 = 4 + y y 1 = 4
var = x
Representacin 1 dica
4 + y = y 40 + 1 41
G1 = y + 1x
Test: ( G1 | A(x, y) y G1 | B(x, y) ) true
Retorna G = x + y

MCD (a(x, y), b(x, y)) = x + y

2.4 ...

2.4.1 Algoritmo e Implementacin.


2.4.1.1 Implementacin en Java.

2.5 ...

2.5.1 Algoritmo e Implementacin.


2.5.1.1 Implementacin en Java.
86 ALGORITMOS EN ALGEBRA.

2.6 ...

2.6.1 Algoritmo e Implementacin.


2.6.1.1 Implementacin en Java.

REFERENCIAS

[Kac59] M. Kac. Statistical Independence in Probability, Analysis and Number Theory. Wiley,
New York, 1959.
[Kob94] N. Koblitz A course in number theory and cryptography. 2ed., Springer,1994.

[Har38] G.H. Hardy, J.E. Littlewood. An Introduction to Theory of Numbers. Oxford Univ. Press.
1938.
[Bre79] R. Brent. An Improved Monte Carlo Factorization Algorithm. BIT 20 (1980), 176-184.
(http://wwwmaths.anu.edu.au/~brent/pub/pubsall.html).
[Bre81] R. Brent, J. M. Pollard. Factorization of the Eighth Fermat Number. Mathematics of
Computation, vol 36, n 154 (1981), 627-630.
(http://wwwmaths.anu.edu.au/~brent/pub/pubsall.html).
[Edw01] Harold M. Edwards. Riemanns Zeta Function. Dover Publications Inc. 2001.
[Che04] P.L. Chebyshev. The Theory of Probability. Translated by Oscar Sheynin
(www.sheynin.de) 2004. Versin en internet:
http://www.sheynin.de/download/4_Chebyschev.pdf. Consultada Diciembre 16,
2006.
[Chi95] Lindsay N. Childs. A Concrete Introduction to Higher Algebra. Springer-Verlag New
York, 1995.
[Rie94] Hans Riesel. Prime Numbers and Computer Methods for Factorization. Springer; 2 edition.
1994.
[Ged92] K.O. Geddes, S.R. Czapor, G.Labahn. Algorithms for Computer Algebra.
Kluwer Academic Publishers. 1992.
[1] Joachim von zur Gathen, Jrgen Gerhard. Modern Computer Algebra. Cambridge
University Press, 2003.

[Sto03] J. Stopple. A Primer of Analytic Number Theory. From Pythagoras to Riemann.


Cambridge. 2003.
[RSA06] RSA, Inc. http://www.rsasecurity.com/. Consultada Noviembre 11, 2006.
[Ser00] Raymond Sroul, Programming for Mathematicians. Springer, 2000.
[Len06] ArjenK. Lenstra. Integer Factoring.
http://modular.fas.harvard.edu/edu/Fall2001/124/misc/
Consultada: Octubre, 2006.
[Mon87] P. Montgomery. Speeding the Pollard and Elliptic Curve Method.
Mathematics of Computation. Vol 48, Issue 177. Jan 1987. 243-264.
http://modular.fas.harvard.edu/edu/Fall2001/124/misc/
Consultada: Octubre, 2006.
[Gat03] Joachim von zur Gathen, Jrgen Gerhard. Modern Computer Algebra. Cambridge Uni-
versity Press, 2003.
... 87

[2] J. von zur Gathen y T. Lcking. Subresultants Revisited. Theoretical Computer Science,
297(1-3):199-239,2003.
[Mig92] Maurice Mignotte. Mathematics for Computer Algebra. Springer,1992.
[Men92] A. Menezes, P. van Oorschot, S. Vanstone. Handbook of Applied Cryptography. Vanstone,
CRC Press, 1996.
(www.cacr.math.uwaterloo.ca/hac)
[Gau97] Gautschi, W. Numerical Analysis. An Introduction. Birkhuser, 1997.
[Ten95] G. Tenenbaum. Introduction to Analytic and Probabilistic Number Theory. Cambridge
Studies in Advanced Mathematics.1995.
[Yan95] S. Y. Yan. Number Theory for Computing. 2nd edition. Springer. 2001.
[Rib95] Paulo Ribenboim. The New Book of Prime Number Records. 3rd ed. Springer. 1995.

Algoritmos en Teora de Nmeros y Algebra.. Walter Mora F.


Derechos Reservados c 2008 Revista digital Matemtica, Educacin e Internet (www.cidse.itcr.ac.cr)

Anda mungkin juga menyukai