You are on page 1of 25

Races de un polinomio.

Mtodo de
Graeffe
En esta pgina, se describe un procedimiento matemtico ingenioso para hallar
las races de un polinomio con gran exactitud. El mtodo de Graeffe se presta
especialmente a ser programado en el ordenador, constituyendo de por s un
ejercicio relevante, en lo que concierne a los aspectos generales del lenguaje
Java: sentencias condicionales e iterativas, arrays unidimensionales y
bidimensionales, descomposicin de un problema en tareas que se codifican
en forma de funciones, y finalmente, la encapsulacin de los datos y las
funciones para formar una clase.
En muchos campos de las matemticas es necesario hallar las races de un
polinomio, por ejemplo, para calcular la integral de una funcin racional, para
calcular las races del polinomio caracterstico que son los valores propios de
una matriz, etc. Solamente existen frmulas si el polinomio tiene un grado
igual o inferior a cuatro. Excepto para los polinomios de primer y segundo
grado, las frmulas son complicadas, por lo que se emplean procesos de
aproximacin numrica. Entre los numerosos mtodos que existen, el ms
conocido es quiz el mtodo de Newton. Sin embargo, describiremos un
mtodo, realmente ingenioso, que nos proporciona gran exactitud en las races
de un polinomio.
Sea el polinomio
a0xn+a1xn1+a2xn2+a3xn3+ ... an1x+an=0
(1)a0xn+a1xn1+a2xn2+a3xn3+ ... an1x+an=0 (1)
Hacemos el polinomio ms simple dividiendo todos los coeficientes por el
primer trmino de modo que a0 es siempre 1. Supongamos que sus races
reales y distintas son
-r1, -r2, -r3, ...-rn
Al elevar al cuadrado el polinomio y agrupar los trminos se obtiene un
polinomio de grado 2n
a20(xn)2(a212a2a0)(xn1)2+(a222a1a3+2a4a0)(xn2)2(a232a2a4+2a1a52a6a0)(xn3)2+
...=0}
(2)a02(xn)2(a122a2a0)(xn1)2+(a222a1a3+2a4a0)(xn2)2(a322a2a4+2a1a52a
6a0)(xn3)2+ ...=0} (2)
Cuyas races sern
r21,r22,r23 ... r2nr12,r22,r32 ... rn2
Hemos construido as una nueva ecuacin cuyas races son numricamente
iguales a los cuadrados de las races de la ecuacin original. Repitiendo el
proceso, se pueden obtener ecuaciones cuyas races sean numricamente
iguales a las potencias cuarta, octava, decimosexta, etc. de las races de la
ecuacin original. El efecto de este proceso de elevar al cuadrado es el de
producir ecuaciones cuyas races estn cada vez ms separadas. Por ejemplo,
si dos races de la ecuacin original estn entre s como 5 : 4 sus potencias 128
estn en la razn 5128 : 4128, o sea, 2.54 1012: 1, lo que es muy deseable ya que
las ecuaciones cuyas races estn muy separadas se pueden resolver
rpidamente con exactitud considerable. Supngase ahora, que reiterando el
proceso de elevacin al cuadrado se llega a un polinomio
0(xn)2m+1(xn1)2m+2(xn2)2m+3(xn3)2m+ ...=0
(3)0(xn)2m+1(xn1)2m+2(xn2)2m+3(xn3)2m+ ...=0 (3)
donde m es el nmero de veces que se repite el proceso de elevacin al
cuadrado. As, si se repite siete veces el proceso de elevacin al cuadrado,
2m =27 =128 sera el exponente al que estaran elevados las sucesivas
potencias xn, xn-1, xn-2, ... del polinomio. Sus races sern las del polinomio
original elevadas al exponente 2m.
r2m1,r2m2,r2m3, ... r2mnr12m,r22m,r32m, ... rn2m
Por las relaciones conocidas entre races y coeficientes del polinomio, se tiene
que
0=11=-
(suma de las races)=r2m1+r2m2+ ... +r2mn2=(suma de las races tomando dos cada vez)
=r2m1r2m2+r2m1r2m3+ ... +r2m2r2m3+ ... +r2mn1r2mn3=(suma de las races tomando tres ca
da vez)=r2m1r2m2r2m3+r2m1r2m2r2m4+ ... +r2m2r2m3r2m4+ ... +r2mn2r2mn1r2mnn=(1)n(product
o de todas las races)=r2m1r2m2r2m3 ... r2mn0=11=-
(suma de las races)=r12m+r22m+ ... +rn2m2=(suma de las races tomando dos cada v
ez)=r12mr22m+r12mr32m+ ... +r22mr32m+ ... +rn12mrn2m3=(suma de las races t
omando tres cada vez)=r12mr22mr32m+r12mr22mr42m+ ... +r22mr32mr42m+ ... +rn
22mrn12mrn2mn=(1)n(producto de todas las races)=r12mr22mr32m ... rn2m
En la suposicin de que
|r1|>|r2|>|r3|> ... |rn||r1|>|r2|>|r3|> ... |rn|
y de que 2m es grande por ejemplo 128 256, se cumplir que
|r1|2m>>>|r2|2m>>>|r3|2m>>> ... |rn|2m|r1|2m>>>|r2|2m>>>|r3|2m>>> ... |rn|2m
donde el smbolo >>> indica mucho mayor que. Las relaciones entre
coeficientes y races quedarn simplificadas con gran aproximacin a las
expresiones.
0=11=r2m12=r2m1r2m23=r2m1r2m2r2m3n=r2m1r2m2r2m3 ... r2mn0=11=r12m2=r12mr22m
3=r12mr22mr32mn=r12mr22mr32m ... rn2m
As, el mdulo de r1 se puede hallar extrayendo la raz 2m-sima de 1 . De la
segunda ecuacin se obtiene r2, y as sucesivamente. La frmula para obtener
el mdulo de la raz ri es
|ri|=ii12m|ri|=ii12m
En la prctica, hallamos el logaritmo de ri, y luego, calculamos el
antilogaritmo del resultado obtenido, de este modo se obtiene el valor absoluto
de la raz ri.
log|ri|=logilogi12m(4)log|ri|=logilogi12m(4)
Para determinar el signo, se halla el valor del polinomio original para los
valores ri, y -ri, uno de los dos har que dicho valor sea prximo a cero y por
tanto, ser la raz buscada.

Clculo de los coeficientes en las sucesivas


iteracciones
Un polinomio cuyas races son reales y distintas es el caso ms simple que se
nos puede presentar. Sea por ejemplo el polinomio x3-4x2+x+6,cuyas races
como se puede comprobar fcilmente por simple sustitucin son 3, 2, y -1. En
la tabla se observa los coeficientes i resultantes del proceso de elevacin del
polinomio a las potencias 2, 4, 8, 16, 32, 64, 128, 256 y 512 respectivamente.
El primer coeficiente 0 es uno
El segundo 1 se obtiene a partir de los coeficientes de la fila anterior
mediante la expresin a212a2a0a122a2a0
El tercer coeficiente 2 mediante la expresin a222a1a3a222a1a3
El cuarto coeficiente 3 mediante la expresin a23a32
m (2m) 0 1 2 3
0 (1) 1.0 -4.0 1.0 6.0
1 (2) 1.0 14.0 49.0 36.0
2 (4) 1.0 98.0 1393.0 1296.0
6
3 (8) 1.0 6818.0 1.6864 10 1.6796 106
4 (16) 1.0 4.3112 107 2.8212 1012 2.8211 1012
5 (32) 1.0 1.8553 1015 7.9587 1024 7.9587 1024
6 (64) 1.0 3.4337 1030 6.334 1049 6.334 1049
7 (128) 1.0 1.179 1061 4.012 1099 4.012 1099
8 (256) 1.0 1.3901 10122 1.6096 10199 1.6096 10199
9 (512) 1.0 1.9323 10244 2.5908 10398 2.5908 10398
Podemos observar en la tabla que cada coeficiente en la iteracin 9, es
aproximadamente el cuadrado del coeficiente en la iteracin precedente,
habindose eliminado el efecto de los dobles productos
A partir de este ejemplo, tenemos que codificar el procedimiento de Graeffe
cualquiera que sea el grado n del polinomio y el nmero m de veces que se
repite el proceso de elevacin al cuadrado, lo que requiere los siguientes
pasos:
1. Crear en memoria un array bidimensional de MAX_ITER filas y n+1
columnas (n es el grado del polinomio), para guardar los coeficientes del
polinomio, tras la aplicacin sucesiva del procedimiento de elevacin al
cuadrado, tal como se ve en la tabla.
2. Obtener los coeficientes de la siguiente fila a partir de la fila anterior,
mediante las expresiones (2)
3. Obtener las races del polinomio, primero, su valor absoluto mediante
la frmula (4) y luego su signo, y guardarlas en un array unidimensional de
dimensin n.
4. Mostrar las races del polinomio.
Reservamos memoria para un array bidimensional a, y guardamos en la
primera fila los coeficientes del polinomio, de mayor a menor grado.
a= new double[MAX_ITER][n+1];
//la primera fila de la tabla guarda los coeficientes del polinomio
for(int j=0; j<n+1; j++){
a[0][j]=coef[j];
}
for(int m=1; m<MAX_ITER; m++){
for(int j=0; j<n+1; j++){
a[m][j]=0.0;
}
}

Donde MAX_ITER es el nmero mximo de iteracciones, o de veces que se


repite el proceso de elevacin al cuadrado.
En una funcin miembro denominada tabla, codificaremos el procedimiento
de elevacin al cuadrado de un polinomio de grado n. Partiendo del polinomio
original (1), obtenemos el polinomio resultante del procedimiento de elevar al
cuadrado (3) segn el esquema (2). En la expresin (2) observamos que el
coeficiente de grado i del nuevo polinomio i se obtiene efectuando las
siguientes operaciones entre los coeficientes del polinomio original: se calcula
el cuadrado de ai y se halla el doble producto de los elementos
equidistantes ak y al, siendo los ndices k=i-s y l=i+s, con s=1, 2, 3... hasta
que se llegue al final del polinomio. Por ejemplo, los elementos equidistantes
a a3 en un polinomio de 6 grado son (a2, a4), (a1, a5) y (a0, a6). Por tanto, el
nuevo coeficiente i del polinomio elevado al cuadrado se calcular mediante
la frmula
3=a232a2a4+2a1a52a0a63=a322a2a4+2a1a52a0a6
Sorprendentemente, el lenguaje Java, no produce error por "overflow", es
decir, cuando se supera el lmite mximo o mnimo para un tipo de dato
bsico: int, long, o double. Por ejemplo, podemos guardar nmeros enteros en
una variable tipo int siempre que est en el intervalo -2147483648 a
2147483647. Las clases que envuelven a los tipos primitivos de
datos, Integer, Double, etc. nos proporcionan funciones miembro que nos
notifican cuando se sobrepasen los lmites especificados para un tipo de dato
dado.
En el cdigo de la funcin tabla, cuando se supera el valor mximo que puede
guardar un dato de tipo double, se interrumpe el proceso de elevacin al
cuadrado, y se sale fuera del bucle. La funcin esttica isInfinite de la
clase Double se encarga de verificarlo devolviendo true si hemos superado
dicho lmite permitido.
exterior:
do{
for(int i=0; i<n+1; i++){
a[m][i]=a[m-1][i]*a[m-1][i];
if(Double.isInfinite(a[m][i])){
break exterior;
}
}
//....
m++;
}while(m<MAX_ITER);

Es necesario emplear un break con una etiqueta para salir al bucle


exterior do...while, e interumpir el proceso de elevacin al cuadrado. Si
solamente empleamos break salimos del bucle interior for y se continuara en
el bucle do...while el proceso de clculo.
Nos queda ahora, la determinacin el signo de cada uno de los dobles
productos. Si el ndice s es impar, entonces el signo es negativo y si es par, el
signo es positivo. En vez de elevar -1 a la potencia s, empleamos el operador
mdulo % en conjuncin con la macro if ... else, que se leer: si s es impar (el
resto de dividir s entre 2 es cero), entonces el valor de la variable
entera signo es +1 en caso contrario es -1.
signo=(s%2==0)? +1: -1;

Los coeficientes del polinomio original se guarda en el array a[0][i], (i=0


... n). Cuando se eleva al cuadrado los coeficientes del nuevo polinomio se
guardan en el array a[1][i], (i=0 ... n), y as sucesivamente. Los
coeficientes a[m][i], (i=0 ... n) corresponden al polinomio que se ha elevado a
la potencia 2m. Dicha potencia se calcula mediante un bucle for.
pot2=1;
for(int i=1; i<=m; i++){
pot2*=2;
}

El cdigo de la funcin tabla, que calcula los coeficientes polinomio resultante


del proceso de elevar el polinomio original sucesivamente al
cuadrado m veces, es el siguiente
private void tabla(){
int k,l, signo;
//divide el polinomio por el primer coeficiente, las races no cambian
for(int i=1; i<n+1; i++){
a[0][i]/=a[0][0];
}
a[0][0]=1.0;
m=1;
exterior: //etiqueta
do{
//cuadrados
for(int i=0; i<n+1; i++){
a[m][i]=a[m-1][i]*a[m-1][i];
if(Double.isInfinite(a[m][i])){
break exterior;
}
}
//dobles productos
for(int i=1; i<n; i++){
for(int s=1; s<n/2+1; s++){
k=i-s; l=i+s;
if((k<0)||(l>n)) break; //trminos simtricos
signo=(s%2==0)? +1: -1;
a[m][i]+=signo*2*a[m-1][k]*a[m-1][l];
if(Double.isInfinite(a[m][i])){
break exterior;
}
}
}
m++;
}while(m<MAX_ITER);

m--;
//potencia de m de 2
pot2=1;
for(int i=1; i<=m; i++){
pot2*=2;
}
}

Races reales y distintas


Un polinomio cuyas races son reales y distintas es el caso ms simple que se
nos puede presentar. Volvemos a estudiar el polinomio x3-4x2+x+6, cuyas
races como se puede comprobar fcilmente por simple sustitucin son 3, 2, y
-1. En la tabla que creamos en el apartado anterior, nos fijamos en la ltima
fila.
m (2m) 0 1 2 3
0 (1) 1.0 -4.0 1.0 6.0
9 (512) 1.0 1.9323 10244 2.5908 10398 2.5908 10398
Los mdulos de las races reales, se calculan mediante la frmula (4). Para
hallar las races con gran exactitud tomaremos los coeficientes que figuran en
la ltima fila, resultado de elevar el polinomio a la potencia 512.
log r0=(log(1)-log(0))/2m
log r0=(log(1.9323 10244)-log(1))/512
r0 sale 3
log r1=(log(2)-log(1))/2m
log r1=(log(2.5908 10398)-log(1.9323 10244))/512
r1 sale 2
log r2=(log(3)-log(2))/2m
log r2=(log(2.5908 10398)-log(2.5908 10398))/512
r2 sale 1
Para codificar estos clculos, emplearemos las funciones Math.log que halla el
logaritmo de la raz, y la funcin recproca Math.exp, para hallar la raz. El
valor absoluto de la raz real se guarda en la variable local raiz.
private void raizRealSimple(int j){
//valor absoluto de la raz
double logaritmo=(Math.log(a[m][j])-Math.log(a[m][j-1]))/pot2;
double raiz=Math.exp(logaritmo);
//determinacin del signo
raicesReales[numReales]=(Math.abs(valorPolinomio(raiz))
<Math.abs(valorPolinomio(-raiz)))? raiz : -raiz;
numReales++;
}

Para determinar el signo de la raz real, hallaremos el valor del polinomio para
dos valores raiz y -raiz, uno de los dos tiene que ser cero o muy prximo a
cero. Una vez hallada la raz real, su valor absoluto y su signo, se guarda en el
array raicesReales.

Races reales dobles


En el apartado anterior, hemos supuesto que las races de un polinomio son
reales y distintas, por lo que la aplicacin del mtodo de Graeffe es inmediata.
Supongamos el polinomio x3-7x2+16x-12 que tiene una raz doble 2, y una
simple 3. Examinemos el comportamiento de sus coeficientes en el proceso de
elevacin al cuadrado en la tabla. Observaremos que el segundo
coeficiente 1 se comporta como hemos descrito en el apartado anterior, cada
coeficiente en una iteracin es aproximadamente el cuadrado de la iteracin
precedente. Sin embargo, este comportamiento no se produce en el tercer
coeficiente 2, ya que se obtiene la mitad del valor esperado. Por ejemplo, el
valor de 2 en la sptima iteracin es 8.024 1099 y su cuadrado es 6.4384 10199,
sin embargo, se obtiene la mitad 3.2192 10199. Lo mismo ocurre en octava
iteracin el cuadrado de 3.2192 10199 es 1.0363 10399, sin embargo, obtenemos
la mitad de este valor 5.1817 10398. Al tercer coeficiente, 2(ndice 2), se
denomina excepcional, y seala la presencia de races reales dobles.
m (2m) 0 1 2 3
0 (1) 1.0 -7.0 16.0 -12.0
1 (2) 1.0 17.0 88.0 144.0
2 (4) 1.0 113.0 2048.0 20736.0
3 (8) 1.0 7073.0 3.4248 106 4.2998 108
4 (16) 1.0 4.3178 107 5.6465 1012 1.8488 1017
5 (32) 1.0 1.853 1015 1.5917 1025 3.4182 1034
6 (64) 1.0 3.4337 1030 1.2668 1050 1.1684 1069
7 (128) 1.0 1.179 1061 8.024 1099 1.3652 10138
8 (256) 1.0 1.3901 10122 3.2192 10199 1.8638 10276
9 (512) 1.0 1.9323 10244 5.1817 10398 3.4737 10552
Para obtener la raz doble, se ha de aplicar la siguiente frmula que damos sin
justificar. La raz repetida se puede hallar calculando la raz 22m de la razn
de los coeficientes que inmediatamente preceden y siguen al coeficiente
excepcional. Si i es el coeficiente excepcional, el mdulo de la raz doble se
calcula mediante la frmula
|ri|=2m+1i+1i1 (5)|ri|=2m+1i+1i1 (5)
La codificacin de la funcin raizRealDoble es similar a la
funcin raizRealSimple. Primero, halla el mdulo de la raz aplicando la
frmula (5), y posteriormente, determina su signo. La raz buscada se guarda
dos veces en el array raicesReales, y se incrementa dos unidades el contador
de races reales numReales.
private void raizRealDoble(int j){
//valor absoluto de la raz
double logaritmo=(Math.log(a[m][j+1])-Math.log(a[m][j-1]))/(2*pot2);
double raiz=Math.exp(logaritmo);
//determinacin del signo
boolean bPositiva=false, bNegativa=false;
if (Math.abs(valorPolinomio(raiz))<CERO){
raicesReales[numReales]=raiz;
numReales++;
bPositiva=true;
}
if (Math.abs(valorPolinomio(-raiz))<CERO){
raicesReales[numReales]=-raiz;
numReales++;
bNegativa=true;
}
if(bPositiva && !bNegativa){
raicesReales[numReales]=raiz;
numReales++;
}
if(!bPositiva && bNegativa){
raicesReales[numReales]=-raiz;
numReales++;
}
}

Una raz compleja y su conjugada


Los polinomios pueden tener tambin races complejas y sus respectivas
conjugadas. El caso ms simple es el del un polinomio x2+1 que tiene una raz
compleja y su correspondiente conjugada. Sea el polinomio x3-7x2+16x-12 que
tiene las siguientes races exactas: 3, 2-3i, 2+3i.
Examinando en la tabla los valores y los signos de los coeficientes en las
sucesivas iteraciones, vemos que el segundo coeficiente 1 cambia de signo en
la tercera iteracin, adems el valor del coeficiente en una iteracin no es
aproximadamente igual al cuadrado de su valor en la siguiente iteracin, sino
la mitad de dicho valor, un comportamiento similar al de las races dobles. Al
coeficiente 1 le denominaremos coeficiente excepcional.
m (2m) 0 1 2 3
0 (1) 1.0 -7.0 25.0 -39.0
1 (2) 1.0 -1.0 79.0 1521.0
2 (4) 1.0 -157.0 9283.0 2.3134 106
3 (8) 1.0 6083.0 8.1259 108 5.352 1012
4 (16) 1.0 -1.5882 109 5.952 1017 2.8644 1025
5 (32) 1.0 1.3319 1018 4.4524 1035 8.2048 1050
6 (64) 1.0 8.8358 1035 1.9606 1071 6.7319 10101
7 (128) 1.0 3.886 1071 3.8437 10142 4.5318 10203
8 (256) 1.0 7.4134 10142 1.4774 10285 2.0537 10407
9 (512) 1.0 2.5411 10285 2.1827 10570 4.2177 10814
Para conocer si un polinomio tiene races complejas y sus correspondientes
conjugadas basta examinar el cambio de signo de los coeficientes en sucesivas
iteraciones, a partir, por ejemplo, de la segunda. Para ello, declaramos y
definimos una funcin miembro denominada cambiaSigno que explora cada
columna buscando cambios de signo en el coeficiente de orden j, tras
sucesivas iteraciones. Si no detecta ningn cambio de signo devuelve false y si
encuentra un cambio de signo, quiere decir, que ha encontrado una raz
compleja y su correspondiente conjugada, aadindose una unidad al
contador numComplejas. Posteriormente, calcula el mdulo de dicha raz, y
por ltimo, devuelve true.
private boolean cambiaSigno(int j){
for(int k=2; k<=m; k++){
if(a[k][j]>0) continue;
//.....
return true;
}
return false;
}

El mdulo de las races complejas se determina mediante la misma frmula


que las races dobles., calculando la raz 22m de la razn de los coeficientes
que inmediatamente preceden y siguen al coeficiente excepcional.
Para calcular una raz compleja hemos de determinar su parte real, ya que la
parte imaginaria se obtiene a partir de su mdulo y de su parte real.
Denotemos las races complejas como u+vi y u-vi, si conocemos la parte
real u, la parte imaginaria v se determina mediante la
frmula v=r2u2v=r2u2 , donde r es el mdulo obtenido a partir de la
frmula (5). Queda, por tanto, determinar la parte real u, mediante la frmula
que daremos sin justificar.
u=12(coeficiente de xn1+suma de todas las races reales)u=12(coeficiente de xn-
1+suma de todas las races reales)
El cdigo de la funcin denominada unaRaizCompleja, calcula primero la
suma de todas las races reales. Luego, se determina u, y a partir de u se
calcula v. La raz compleja y su correspondiente conjugada se guardan el
array raicesComplejas.
private void unaRaizCompleja(){
double suma=0.0;
for(int i=0; i<numReales; i++){
suma+=raicesReales[i];
}
double u, v;
u=-(a[0][1]+suma)/2;
v=Math.sqrt(moduloComplejas[0]*moduloComplejas[0]-u*u);
raicesComplejas[0]=new Complejo(u, v);
raicesComplejas[1]=new Complejo(u, -v);
}

En esta porcin de cdigo, hemos empleado un nuevo tipo de dato,


denominado Complejo. Se trata de una clase cuyos miembros dato son la parte
real y la parte imaginaria del nmero complejo y como funciones miembro,
tiene los constructores, las funciones que suman, restan, multiplican o dividen
dos numeros complejos.

Dos races complejas y sus conjugadas


Supongamos ahora el polinomio x5-4x4+11x3+4x2-10x-52 que tiene una raz
real 2 y dos races complejas y sus correspondientes conjugadas, 2+3i, 2-3i, -
1+i, -1-i. Examinando la tabla vemos que los coeficientes segundo 1 y quinto
4 cambian de signo y adems, se comportan de modo similar al de las races
dobles, signo inequvoco de una raz compleja. Por tanto, el polinomio tendr
dos races complejas y sus respectivas conjugadas, en total cuatro races que es
el mximo nmero que permite este procedimiento.
m (2m) 0 1 2 3 4 5
0 (1) 1.0 -4.0 11.0 4.0 -10-0 -52.0
1 (2) 1.0 -6.0 133.0 652.0 516.0 2704.0
2 (4) 1.0 -230.0 2.6545 104 2.554 105 -3.2598 106 7.3116 106
3 (8) 1.0 -190.0 8.156 108 2.3493 1011 6.8913 1012 5.346 1013
4 (16) 1.0 -1.6312 109 6.6531 1017 4.3949 1022 2.2371 1025 2.8579 1027
5 (32) 1.0 1.3301 1018 4.4278 1035 1.9018 1045 2.4926 1050 8.1678 1054
6 (64) 1.0 8.8357 1035 1.9605 1071 3.6165 1090 3.1066 10100 6.6714 10109
7 (128) 1.0 3.886 1061 3.8437 10142 1.3079 10181 4.8255 10200 4.4507 10219
8 (256) 1.0 7.4134 10142 1.4774 10275 1.7107 10362 1.1643 10401 1.9809 10439
9 (512) 1.0 2.5411 10285 2.1827 10570 2.9265 10724 6.7774 10801 3.9238 10878
Denotaremos las dos races complejas y sus respectivas conjugadas
como u1v1i y u2v2i. Conocidos u1 y u2 se calcula v1 y v2 mediante las
frmulas
v1=r21u21 v2=r22u22v1=r12u12 v2=r22u22
Para determinar u1 y u2 se emplean las siguientes frmulas
u1+u2=12(coeficiente de xn1+suma de todas las races reales)u1r22+u2r21=(1)n1an12(pro
ducto de las n4 races reales)r21r222(suma de las inversas de las n4 races reales)u1+u2=12(c
oeficiente de xn-
1+suma de todas las races reales)u1r22+u2r12=(1)n1an12(producto de las n-
4 races reales)r12r222(suma de las inversas de las n-4 races reales)
Se trata de un sistema de dos ecuaciones con dos incgnitas de las que se
despeja u1 y u2
La funcin denominada dosRaicesComplejas, calcula la suma de todas las
races reales, el producto y la suma de las inversas de dichas races. Despus
calcula los segundos miembros de cada una de las dos ecuaciones y los guarda
en las variables locales y y z
La solucin del sistema de dos ecuaciones con dos incgnitas es
u1=yr21zr21r22 u2=yr22+zr21r22u1=yr12zr12r22 u2=yr22+zr12r22
lo que nos permite calcular u1 y u2y posteriormente, v1 y v2
Finalmente, guardamos los valores obtenidos en el array raicesComplejas
private void dosRaicesComplejas(){
double suma=0.0;
double producto=1.0;
double inversa=0.0;
for(int i=0; i<numReales; i++){
suma+=raicesReales[i];
producto*=raicesReales[i];
inversa+=1/raicesReales[i];
}

double r1=moduloComplejas[0];
double r2=moduloComplejas[1];
double y=-(a[0][1]+suma)/2;
int signo=((n-1)%2==0)? +1: -1;
double z=signo*a[0][n-1]/(2*producto)-r1*r1*r2*r2*inversa/2;

double u1=(y*r1*r1-z)/(r1*r1-r2*r2);
double u2=(-y*r2*r2+z)/(r1*r1-r2*r2);
double v1=Math.sqrt(r1*r1-u1*u1);
double v2=Math.sqrt(r2*r2-u2*u2);

raicesComplejas[0]=new Complejo(u1, v1);


raicesComplejas[1]=new Complejo(u1, -v1);
raicesComplejas[2]=new Complejo(u2, v2);
raicesComplejas[3]=new Complejo(u2, -v2);
}

La funcin discriminadora
La funcin pblica hallaRaices se encarga de llamar a la funcin tabla para
calcular los coeficientes polinomio resultante del proceso de elevar el
polinomio original sucesivamente al cuadrado. Los coeficientes se guardan en
el array bidimensional a[m][i] (i=0... n) donde m representa la iteracin, e i el
coeficiente: 0 es el ndice del coeficiente de mayor grado y n es el ndice del
trmino independiente.
private boolean cambiaSigno(int j){
double logaritmo;
for(int k=2; k<=m; k++){
if(a[k][j]>0) continue;
numComplejas++;
//mximo dos races complejas, 4 contando sus respectivas conjugadas
if(numComplejas<3){
logaritmo=(Math.log(a[m][j+1])-Math.log(a[m][j-1]))/(2*pot2);
moduloComplejas[numComplejas-1]=Math.exp(logaritmo);
return true;
}
}
return false;
}

Ya que a[m][0] es siempre la unidad analizaremos cada uno de


los n coeficientes restantes a[m][i] (i=1 ... n). En primer lugar, miraremos si
hay cambios de signo en cada uno de los coeficientes i, llamando a la
funcin cambiaSigno, y pasndole el ndice i. Si la funcin devuelve true,
quiere decir que se ha detectado una raz compleja y su correspondiente
conjugada.
tabla();
//el pimer coeficiente a[m][0] es siempre 1
for(int i=1; i<n+1; i++){ //i es la raz
if(cambiaSigno(i)){
//raz compleja y su correspondiente conjugada
i++;
continue;
}

Posteriormente, analiza si hay una raz doble, comprobando la relacin entre


los valores del coeficiente i en las dos ltimas iteraciones. Recurdese
que a[m][i] es habitualmente el cuadrado de a[m-1][i], salvo para los
coeficientes excepcionales. Cuando hay presentes races dobles a[m][i] es el
cuadrado de a[m-1][i] dividido entre dos. La variable local cociente valdr
habitualmente cero, salvo en el caso de races dobles. Cuando la
variable cociente de la funcin cambiaSigno es cero (el logaritmo de la unidad
es cero) o un valor muy prximo a cero (menor que la constante CERO fijada
de antemano), se llama a la funcin raizRealSimple para hallar la raz
correspondiente al coeficiente i del polinomio. En caso contrario, tendremos
una raz doble y se llamar a la funcin raizRealDoble para calcularla.
//races simple y dobles
double logaritmo=Math.log(a[m][i])-2*Math.log(a[m-1][i]);
if(Math.abs(logaritmo)<CERO){
raizRealSimple(i);
}else{
raizRealDoble(i);
i++;
continue;
}
}

La primera verificacin se realiza sobre las races complejas ya que cumplen


una condicin ms estricta que las races dobles, es decir, se comportan como
las races dobles pero adems presentan cambios de signo en las primeras
iteraciones, mientras el trmino cuadrado no domina sobre los dobles
productos.
Una vez concluido el anlisis de cada uno de los coeficientes del polinomio en
las sucesivas iteraciones que lo elevan al cuadrado y se han calculado las
races reales simples y dobles se procede al clculo de las races complejas. Si
solamente hay una raz compleja y su conjugada, se llama a la
funcin unaRaizCompleja.
if(numComplejas==1){
unaRaizCompleja();
}

Si hay dos races complejas y sus conjugadas, se llama a la


funcin dosRaicesComplejas.
if(numComplejas==2){
dosRaicesComplejas();
}

El cdigo completo de la funcin miembro hallarRaices que calcula todas las


races de un polinomio se muestra a continuacin
public void hallarRaices(){
tabla();
//el pimer coeficiente a[m][0] es siempre 1
for(int i=1; i<n+1; i++){
if(cambiaSigno(i)){
//raz compleja y su correspondiente conjugada
i++;
continue;
}
//races simple y dobles
double logaritmo=Math.log(a[m][i])-2*Math.log(a[m-1][i]);
if(Math.abs(logaritmo)<CERO){
raizRealSimple(i);
}else{
raizRealDoble(i);
i++;
continue;
}
}
if(numComplejas==1){
unaRaizCompleja();
}
if(numComplejas==2){
dosRaicesComplejas();
}
}

Mostrar las races calculadas


Primero, se muestran las races reales con dos cifras decimales
(double)Math.round(raicesReales[i]*100)/100

A continuacin, se muestran las races complejas


System.out.println(raicesComplejas[2*i]);

La funcin println no solamente imprime objetos de la clase String, sino que


tambin imprime datos numricos de los tipos bsicos y como vemos
convierte automticamente un nmero complejo en su representacin textual,
pero para ello hemos de redefinir en dicha clase la funcin toString miembro
de la clase base Object de la cual derivan todas las clases en Java. La llamada
implcita a dicha funcin muestra la parte real seguida de la imaginaria ambas
con dos cifras decimales y seguida de la unidad imaginaria i. El cdigo
completo de la funcin mostrarRaices es el siguiente.
public void mostrarRaices(){
hallarRaices();
//races reales
System.out.println("Races reales");
for(int i=0; i<numReales; i++){
System.out.println((double)Math.round(raicesReales[i]*100)/100+" ---> "+
valorPolinomio(raicesReales[i]));
}
System.out.println("");
//races complejas
System.out.println("Races complejas");
for(int i=0; i<numComplejas; i++){
System.out.println(raicesComplejas[2*i]+" ---> "+
valorPolinomio(raicesComplejas[2*i]));
System.out.println(raicesComplejas[2*i+1]+" ---> "+
valorPolinomio(raicesComplejas[2*i]));
}
System.out.println("");
}

Comprobacin de las races


La funcin valorPolinomio halla el valor de un polinomio para un valor real
de su variable x. Dicha funcin calcula primero las sucesivas potencias de x,
las guarda en el array pot_x, y luego, las multiplica por sus respectivos
coeficientes y suma los resultados.
public double valorPolinomio(double x){
double y=0.0;
//sucesivas potencias de x, se puede utilizar tambien la funcion Math.pow
double[] pot_x=new double[n+1];
pot_x[0]=1.0;
for(int i=1; i<n+1; i++){
pot_x[i]=pot_x[i-1]*x;
}
//valores de los sucesivos trminos
for(int i=0; i<n+1; i++){
y+=a[0][i]*pot_x[n-i];
}
return y;
}

Es algo ms complicado hallar el valor del polinomio cuando la variable es


compleja x. Tendremos que hallar las sucesivas potencias de la variable
compleja, multiplicar por los coeficientes y sumar los resultados. La
clase Complejo que describe dicha entidad matemtica ha definir las siguientes
operaciones: la suma de dos nmeros complejos, el producto de un nmero
real por un nmero complejo y la potencia de un nmero complejo.
La funcin valorPolinomio devolver un nmero complejo, cuando se le pasa
en su nico argumento otro nmero complejo. Su definicin en trminos de las
funciones mencionados es la siguiente.
public Complejo valorPolinomio(Complejo x){
Complejo y=new Complejo();
for(int i=0; i<n+1; i++){
y=Complejo.suma(y, Complejo.producto(a[0][i], Complejo.potencia(x, (n-
i))));
}
return y;
}

Ejemplos
Para hallar las races de un polinomio, primero creamos un array de los
coeficientes, de mayor a menor grado. Para el polinomio x3-4x2+x+6 se
escribir
double[] coef={1, -4, 1, 6};

A continuacin, se crea un objeto g de la clase Graeffe y le pasamos el array


de los coeficientes coef a su constructor. Desde dicho objeto se llama a la
funcin miembro mostrarRaices, para hallar y mostrar las races del
polinomio.
Graeffe g=new Graeffe(coef);
g.mostrarRaices();

Para que practique el lector, se le propone hallar las races de los siguientes
ecuaciones polinmicas:
x3-0x2-3x+1=0
x4+x3-10x2-34x-26=0
x4+0x3+4x2-3x+3=0
x3-6x2+11x-7=0
x3+2x2+2x+2=0
x4-x3-10x2-x+1=0
4x4+16x3+25x2+21x+9=0
16x5-16x4-12x3+12x2+0x-1=0
x5+0x4-5x3+0x2+4x-10=0
x5-8x4+17x3-10x2+0x-1=0
x7+x6-4x5-4x4-2x3-5x2-x-1=0
Nota: Aplicaremos el mtodo de Graeffe a un polinomio cuyo trmino
independiente es distinto de cero. Ya que una ecuacin como x3-4x2+x=0, tiene
una raz x=0 y por tanto, solamente precisamos calcular las races de la
ecuacin x2-4x+1=0.

Referencias
Wylie. Matemticas superiores para la ingeniera. Ediciones del Castillo.
Apndice.
B. P. Demidovich, I.A. Maron. Clculo numrico funadamental. Mtodo
Lobachevski-Graeffe. Edt. Paraninfo (1977) pgs 202-223
ngel Franco.El mtodo de Graeffe. Un procedimiento para hallar las races
de un polinomio. Revista Profesional de Programadores n 39, Abril de 1998,
pgs. 38-46.

Cdigo fuente
public class Graeffe {
public int n;
public double[] raicesReales;
public Complejo[] raicesComplejas=new Complejo[4];
public int numReales;
public int numComplejas;
private double[][] a;
private int pot2=1;
private int m;
private final int MAX_ITER=10;
private static final double CERO=0.0001;
private double[] moduloComplejas=new double[2];

public Graeffe(double[] coef) {


n=coef.length-1;
raicesReales=new double[n];
a= new double[MAX_ITER][n+1];
//la primera fila de la tabla guarda los coeficientes del polinomio
for(int j=0; j<n+1; j++){
a[0][j]=coef[j];
}
for(int m=1; m<MAX_ITER; m++){
for(int j=0; j<n+1; j++){
a[m][j]=0.0;
}
}
numReales=0;
numComplejas=0;
}
private void tabla(){
int k,l, signo;
//divide el polinomio por el primer coeficiente, las races no cambian
for(int i=1; i<n+1; i++){
a[0][i]/=a[0][0];
}
a[0][0]=1.0;
m=1;
exterior:
do{
//cuadrados
for(int i=0; i<n+1; i++){
a[m][i]=a[m-1][i]*a[m-1][i];
if(Double.isInfinite(a[m][i])){
break exterior;
}
}
//dobles productos
for(int i=1; i<n; i++){
for(int s=1; s<n/2+1; s++){
k=i-s; l=i+s;
if((k<0)||(l>n)) break; //trminos simtricos
signo=(s%2==0)? +1: -1;
a[m][i]+=signo*2*a[m-1][k]*a[m-1][l];
if(Double.isInfinite(a[m][i])){
break exterior;
}
}
}
m++;
}while(m<MAX_ITER);

m--;
//potencia de m de 2
pot2=1;
for(int i=1; i<=m; i++){
pot2*=2;
}
}
//valor de un polinomio para una variable real
public double valorPolinomio(double x){
double y=0.0;
//sucesivas potencias de x, se puede utilizar tambin la funcion Math.pow
double[] pot_x=new double[n+1];
pot_x[0]=1.0;
for(int i=1; i<n+1; i++){
pot_x[i]=pot_x[i-1]*x;
}
//valores de los sucesivos trminos
for(int i=0; i<n+1; i++){
y+=a[0][i]*pot_x[n-i];
}
return y;
}
public Complejo valorPolinomio(Complejo x){
Complejo y=new Complejo();
for(int i=0; i<n+1; i++){
y=Complejo.suma(y, Complejo.producto(a[0][i],
Complejo.potencia(x, (n-i))));
}
return y;
}

private void raizRealSimple(int j){


//valor absoluto de la raz
// System.out.println("Raiz simple");
double logaritmo=(Math.log(a[m][j])-Math.log(a[m][j-1]))/pot2;
double raiz=Math.exp(logaritmo);
//determinacin del signo, y1 o y2 tienen que ser casi cero

raicesReales[numReales]=(Math.abs(valorPolinomio(raiz))<
Math.abs(valorPolinomio(-raiz)))? raiz : -raiz;
numReales++;
}

private void raizRealDoble(int j){


//valor absoluto de la raz
double logaritmo=(Math.log(a[m][j+1])-Math.log(a[m][j-1]))/(2*pot2);
double raiz=Math.exp(logaritmo);
boolean bPositiva=false, bNegativa=false;
if (Math.abs(valorPolinomio(raiz))<CERO){
raicesReales[numReales]=raiz;
numReales++;
bPositiva=true;
}
if (Math.abs(valorPolinomio(-raiz))<CERO){
raicesReales[numReales]=-raiz;
numReales++;
bNegativa=true;
}
if(bPositiva && !bNegativa){
raicesReales[numReales]=raiz;
numReales++;
}
if(!bPositiva && bNegativa){
raicesReales[numReales]=-raiz;
numReales++;
}
}

private void unaRaizCompleja(){


double suma=0.0;
for(int i=0; i<numReales; i++){
suma+=raicesReales[i];
}
double u, v;
u=-(a[0][1]+suma)/2;
v=Math.sqrt(moduloComplejas[0]*moduloComplejas[0]-u*u);
raicesComplejas[0]=new Complejo(u, v);
raicesComplejas[1]=new Complejo(u, -v);
}
private void dosRaicesComplejas(){
double suma=0.0;
double producto=1.0;
double inversa=0.0;
for(int i=0; i<numReales; i++){
suma+=raicesReales[i];
producto*=raicesReales[i];
inversa+=1/raicesReales[i];
}
double r1=moduloComplejas[0];
double r2=moduloComplejas[1];
double y=-(a[0][1]+suma)/2;
int signo=((n-1)%2==0)? +1: -1;
double z=signo*a[0][n-1]/(2*producto)-r1*r1*r2*r2*inversa/2;
double u1=(y*r1*r1-z)/(r1*r1-r2*r2);
double u2=(-y*r2*r2+z)/(r1*r1-r2*r2);
double v1=Math.sqrt(r1*r1-u1*u1);
double v2=Math.sqrt(r2*r2-u2*u2);
raicesComplejas[0]=new Complejo(u1, v1);
raicesComplejas[1]=new Complejo(u1, -v1);
raicesComplejas[2]=new Complejo(u2, v2);
raicesComplejas[3]=new Complejo(u2, -v2);
}

private boolean cambiaSigno(int j){


double logaritmo;
for(int k=2; k<=m; k++){
if(a[k][j]>0) continue;
numComplejas++;
//mximo dos races complejas, 4 contando sus respectivas conjugadas
if(numComplejas<3){
logaritmo=(Math.log(a[m][j+1])-Math.log(a[m][j-1]))/(2*pot2);
moduloComplejas[numComplejas-1]=Math.exp(logaritmo);
return true;
}
}
return false;
}

public void hallarRaices(){


tabla();
//el pimer coeficiente a[m][0] es siempre 1
for(int i=1; i<n+1; i++){ //i es la raz
if(cambiaSigno(i)){
//raz compleja y su correspondiente conjugada
i++;
continue;
}
//races simple y dobles
double logaritmo=Math.log(a[m][i])-2*Math.log(a[m-1][i]);
if(Math.abs(logaritmo)<CERO){
raizRealSimple(i);
}else{
raizRealDoble(i);
i++;
continue;
}
}
if(numComplejas==1){
unaRaizCompleja();
}
if(numComplejas==2){
dosRaicesComplejas();
}
}

public void mostrarRaices(){


hallarRaices();
//races reales
System.out.println("Races reales");
for(int i=0; i<numReales; i++){
System.out.println((double)Math.round(raicesReales[i]*100)/100+" --->
"+valorPolinomio(raicesReales[i]));
}
System.out.println("");
//races complejas
System.out.println("Races complejas");
for(int i=0; i<numComplejas; i++){
System.out.println(raicesComplejas[2*i]+" --->
"+valorPolinomio(raicesComplejas[2*i]));
System.out.println(raicesComplejas[2*i+1]+" --->
"+valorPolinomio(raicesComplejas[2*i]));
}
System.out.println("");
}
}
public class RaizPolinomioApp {
public static void main(String[] args) {
//Ecuacin
//a[n]x^n+a[n-1]x^(n-1)+.....+a[3]x^3+a[2]x^2+a[1]x+a[0]=0

double[] coef={1, -4, 1, 6};


Graeffe g=new Graeffe(coef);
g.mostrarRaices();
//de mayor a menor grado
double[] coef1={1, -7, 16, -12};
g=new Graeffe(coef1);
g.mostrarRaices();
double[] coef2={1, -7, 25, -39};
g=new Graeffe(coef2);
g.mostrarRaices();
double[] coef3={1, 0, -5, 0, 4, -10};
g=new Graeffe(coef3);
g.mostrarRaices();

double[] coef4={1, -6, 11, -7};


g=new Graeffe(coef4);
g.mostrarRaices();
double[] coef5={1, 2, 2, 2};
g=new Graeffe(coef5);
g.mostrarRaices();
double[] coef6={1, -1, -10, -1, 1};
g=new Graeffe(coef6);
g.mostrarRaices();

double[] coef7={4, 16, 25, 21, 9};


g=new Graeffe(coef7);
g.mostrarRaices();
double[] coef8={16, -16, -12, 12, 0, 1};
g=new Graeffe(coef8);
g.mostrarRaices();
double[] coef9={1, -8, 17, -10, 0, 1};
g=new Graeffe(coef9);
g.mostrarRaices();
}
}
public class Complejo{
private double real;
private double imag;

public Complejo() {
real=0.0;
imag=0.0;
}
public Complejo(double real, double imag){
this.real=real;
this.imag=imag;
}
public static Complejo conjugado(Complejo c){
return new Complejo(c.real, -c.imag);
}
public static Complejo opuesto(Complejo c){
return new Complejo(-c.real, -c.imag);
}
public double modulo(){
return Math.sqrt(real*real+imag*imag);
}
//devuelve el ngulo en grados
public double argumento(){
double angulo=Math.atan2(imag, real);
if(angulo<0) angulo=2*Math.PI+angulo;
return angulo*180/Math.PI;
}
//suma de dos nmeros complejos
public static Complejo suma(Complejo c1, Complejo c2){
double x=c1.real+c2.real;
double y=c1.imag+c2.imag;
return new Complejo(x, y);
}
//producto de dos nmeros complejos
public static Complejo producto(Complejo c1, Complejo c2){
double x=c1.real*c2.real-c1.imag*c2.imag;
double y=c1.real*c2.imag+c1.imag*c2.real;
return new Complejo(x, y);
}
//producto de un complejo por un nmero real
public static Complejo producto(Complejo c, double d){
double x=c.real*d;
double y=c.imag*d;
return new Complejo(x, y);
}
//producto de un nmero real por un complejo
public static Complejo producto(double d, Complejo c){
double x=c.real*d;
double y=c.imag*d;
return new Complejo(x, y);
}
//cociente de dos nmeros complejos
//excepcin cuando el complejo denominador es cero
public static Complejo cociente(Complejo c1, Complejo c2)
throws ExcepcionDivideCero{
double aux, x, y;
if(c2.modulo()==0.0){
throw new ExcepcionDivideCero("Divide entre cero");
}else{
aux=c2.real*c2.real+c2.imag*c2.imag;
x=(c1.real*c2.real+c1.imag*c2.imag)/aux;
y=(c1.imag*c2.real-c1.real*c2.imag)/aux;
}
return new Complejo(x, y);
}
//cociente entre un nmero complejo y un nmero real
public static Complejo cociente(Complejo c, double d)
throws ExcepcionDivideCero{
double x, y;
if(d==0.0){
throw new ExcepcionDivideCero("Divide entre cero");
}else{
x=c.real/d;
y=c.imag/d;
}
return new Complejo(x, y);
}
//el nmero e elevado a un nmero complejo
public static Complejo exponencial(Complejo c){
double x=Math.cos(c.imag)*Math.exp(c.real);
double y=Math.sin(c.imag)*Math.exp(c.real);
return new Complejo(x, y);
}
//raz cuadrada de un nmero positivo o negativo
public static Complejo csqrt(double d){
if(d>=0) return new Complejo(Math.sqrt(d), 0);
return new Complejo(0, Math.sqrt(-d));
}
//funcin auxiliar para la potencia de un nmero complejo
private static double potencia(double base, int exponente){
double resultado=1.0;
for(int i=0; i<exponente; i++){
resultado*=base;
}
return resultado;
}
//funcin auxiliar para la potencia de un nmero complejo
private static double combinatorio(int m, int n){
long num=1;
long den=1;
for(int i=m; i>m-n; i--){
num*=i;
}
for(int i=2; i<=n; i++){
den*=i;
}
return (double)num/den;
}
//potencia de un nmero complejo
public static Complejo potencia(Complejo c, int exponente){
double x=0.0, y=0.0;
int signo;
for(int i=0; i<=exponente; i++){
signo=(i%2==0)?+1:-1;
//parte real
x+=combinatorio(exponente, 2*i)*potencia(c.real, exponente-2*i)
*potencia(c.imag, 2*i)*signo;
if(exponente==2*i) break;
//parte imaginaria
y+=combinatorio(exponente, 2*i+1)*potencia(c.real, exponente-(2*i+1))
*potencia(c.imag, 2*i+1)*signo;
}
return new Complejo(x, y);
}
//representa un nmero complejo como un string
public String toString(){
if(imag>0) return new String((double)Math.round(100*real)/100
+" + "+(double)Math.round(100*imag)/100+"*i");
return new String((double)Math.round(100*real)/100+" - "
+(double)Math.round(-100*imag)/100+"*i");
}
}

class ExcepcionDivideCero extends Exception {

public ExcepcionDivideCero() {
super();
}
public ExcepcionDivideCero(String s) {
super(s);
}
}

You might also like