Soluciones de una ecuación de tercer grado en Excel. Hace algún tiempo expliqué la manera de resolver, esto es, de encontrar la solución de una ecuación lineal de grado uno (ver ); en aquel entonces utilicé la herramienta Buscar objetivo. Recientemente, a través de un comentario en el blog, un usuario pedía ayuda para saber cómo solucionar una ecuación de tercer grado: ...lo q necesito es encontrar mas de una raíz, tengo un polinomio cubico y necesito las 3 raíces... He de decir que para encontrar las tres raíces/soluciones de un polinomio cúbico partiré de una hipótesis de trabajo, que el polinomio tiene tres raices; sabemos que una ecuación de tercer grado podría tener hasta tres puntos de corte con el eje X, es decir, hasta tres valores de X donde la ecuación devuelve un valor igual a cero. Inicio el trabajo con un ejemplo de ecuación de tercer grado con tres soluciones. y = 2x 3 - 4x 2 + 1 A pesar de mi hipótesis inicial, el método de trabajo es válido para cualquier polinomio cúbico. El primer paso es escribir nuestra ecuación de manera que Excel la entienda: =2*POTENCIA(A3;3)-4*POTENCIA(A3;2)+1 con esta fórmula, en un segundo paso, construiremos una gráfica (forma visual de encontrar las soluciones buscadas para nuestra ecuación): Ya en el gráfico podemos ver cuáles son las raíces de la ecuación, pero parece claro que dependerá mucho de los valores de la x que hayamos insertado para construir el gráfico. Se hace necesario, por tanto, tener buena vista, o probar un par de veces, para conseguir el gráfico adecuado a nuestros propósitos. Sin embargo, con el gráfico sólo podemos intuir la forma de la ecuación y más o menos por donde 'caen' las raíces. Para conocer las soluciones exactas nos basaremos en el gráfico recién construido.
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Soluciones de una ecuación de tercer grado en Excel.Hace algún tiempo expliqué la manera de resolver, esto es, de encontrar la solución de una ecuación
lineal de grado uno (ver); en aquel entonces utilicé la herramienta Buscar objetivo.
Recientemente, a través de un comentario en el blog, un usuario pedía ayuda para saber cómo
solucionar una ecuación de tercer grado:
...lo q necesito es encontrar mas de una raíz, tengo un polinomio cubico y necesito las 3 raíces...
He de decir que para encontrar las tres raíces/soluciones de un polinomio cúbico partiré de una
hipótesis de trabajo, que el polinomio tiene tres raices; sabemos que una ecuación de tercer grado
podría tener hasta tres puntos de corte con el eje X, es decir, hasta tres valores de X donde la
ecuación devuelve un valor igual a cero. Inicio el trabajo con un ejemplo de ecuación de tercer grado
con tres soluciones.
y = 2x3 - 4x2 + 1
A pesar de mi hipótesis inicial, el método de trabajo es válido para cualquier polinomio cúbico.
El primer paso es escribir nuestra ecuación de manera que Excel la entienda:
=2*POTENCIA(A3;3)-4*POTENCIA(A3;2)+1
con esta fórmula, en un segundo paso, construiremos una gráfica (forma visual de encontrar las
soluciones buscadas para nuestra ecuación):
Ya en el gráfico podemos ver cuáles son las raíces de la ecuación, pero parece claro que dependerá
mucho de los valores de la x que hayamos insertado para construir el gráfico. Se hace necesario, por
tanto, tener buena vista, o probar un par de veces, para conseguir el gráfico adecuado a nuestros
propósitos.
Sin embargo, con el gráfico sólo podemos intuir la forma de la ecuación y más o menos por donde
'caen' las raíces. Para conocer las soluciones exactas nos basaremos en el gráfico recién construido.
Toca ahora construir un modelo sobre el que aplicar la herramienta SOLVER, que será quien nos de
Hola, una forma manual de hacerlo a grandes rasgos sería:a) generas 3 celdas con nombre, es decir, X, N y Mb) generas la formula como en la imagenc) activas el cuadro de dialogo "Buscar objetivo" y lo estableces como en la imagen anexa.
En esta página, se describe un procedimiento matemático ingenioso para hallar las raíces de un polinomio con gran exactitud. El método 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, descomposición de un problema en tareas que se codifican en forma de funciones, y finalmente, la encapsulación de los datos y las funciones para formar una clase.
En muchos campos de las matemáticas es necesario hallar las raíces de un polinomio, por ejemplo, para calcular la integral de una función racional, para calcular las raíces del polinomio característico que son los valores propios de una matriz, etc. Solamente existen fórmulas si el polinomio tiene un grado igual o inferior a cuatro. Excepto para los polinomios de primer y segundo grado, las fórmulas son complicadas, por lo que se emplean procesos de aproximación numérica. Entre los numerosos métodos que existen, el más conocido es quizá el método de Newton. Sin embargo, describiremos un método, realmente ingenioso, que nos proporciona gran exactitud en las raíces de un polinomio.
Sea el polinomio
a 0 x n + a 1 x n−1 + a 2 x n−2 + a 3 x n−3 + ... a n−1 x+ a n =0 (1)
Hacemos el polinomio más simple dividiendo todos los coeficientes por el primer término de modo que a0 es siempre 1. Supongamos que sus raíces reales y distintas son
-r1, -r2, -r3, ...-rn
Al elevar al cuadrado el polinomio y agrupar los términos se obtiene un polinomio de grado 2n
a 0 2 ( x n ) 2 −( a 1 2 −2 a 2 a 0 ) ( x n−1 ) 2 +( a 2 2 −2 a 1 a 3 +2 a 4 a 0 ) ( x n−2 ) 2 −( a 3 2 −2 a 2 a 4 +2 a 1 a5 −2 a 6 a 0 ) ( x n−3 ) 2 + ...=0 } (2)
Cuyas raíces serán
− r 1 2 ,− r 2 2 ,− r 3 2 ... − r n 2
Hemos construido así una nueva ecuación cuyas raíces son numéricamente iguales a los cuadrados de las raíces de la ecuación original. Repitiendo el
proceso, se pueden obtener ecuaciones cuyas raíces sean numéricamente iguales a las potencias cuarta, octava, decimosexta, etc. de las raíces de la ecuación original. El efecto de este proceso de elevar al cuadrado es el de producir ecuaciones cuyas raíces están cada vez más separadas. Por ejemplo, si dos raíces de la ecuación original están entre sí como 5 : 4 sus potencias 128 están en la razón 5128 : 4128, o sea, 2.54 1012: 1, lo que es muy deseable ya que las ecuaciones cuyas raíces están muy separadas se pueden resolver rápidamente con exactitud considerable. Supóngase ahora, que reiterando el proceso de elevación al cuadrado se llega a un polinomio
α 0 ( x n ) 2 m + α 1 ( x n−1 ) 2 m + α 2 ( x n−2 ) 2 m + α 3 ( x n−3 ) 2 m + ...=0 (3)
donde m es el número de veces que se repite el proceso de elevación al cuadrado. Así, si se repite siete veces el proceso de elevación al cuadrado, 2m =27 =128 sería el exponente al que estarían elevados las sucesivas potencias xn, xn-1, xn-2, ... del polinomio. Sus raíces serán las del polinomio original elevadas al exponente 2m.
− r 1 2 m ,− r 2 2 m ,− r 3 2 m , ... − r n 2 m
Por las relaciones conocidas entre raíces y coeficientes del polinomio, se tiene que
α 0 =1 α 1 =-(suma de las raíces)= r 1 2 m + r 2 2 m + ... + r n 2 m α 2 =(suma de las raíces tomando dos cada vez)= r1 2 m r 2 2 m + r 1 2 m r 3 2 m + ... + r 2 2 m r 3 2 m + ... + r n−1 2 m r n 2 m α 3=−(suma de las raíces tomando tres cada vez)= r 1 2 m r 2 2 m r 3 2 m + r 1 2 m r 2 2 m r 4 2 m + ... + r 2 2 m r 3 2 mr 4 2 m + ... + r n−2 2 m r n−1 2 m r n 2 m α n = ( −1 ) n (producto de todas las raíces)= r 1 2 m r 2 2 m r 3 2 m ... r n2 m
En la suposición de que
| r 1 |>| r 2 |>| r 3 |> ... | r n |
y de que 2m es grande por ejemplo 128 ó 256, se cumplirá que
| r 1 | 2 m >>> | r 2 | 2 m >>> | r 3 | 2 m >>> ... | r n | 2 m
donde el símbolo >>> indica mucho mayor que. Las relaciones entre coeficientes y raíces quedarán simplificadas con gran aproximación a las expresiones.
α 0 =1 α 1 = r 1 2 m α 2 = r 1 2 m r 2 2 m α 3 = r 1 2 m r 2 2 m r 3 2 m α n = r 1 2 m r 2 2 m r 3 2 m ... r n 2 m
Así, el módulo de r1 se puede hallar extrayendo la raíz 2m-ésima de α1 . De la segunda ecuación se obtiene r2, y así sucesivamente. La fórmula para obtener el módulo de la raíz ri es
| r i |= α i α i−1 2 m
En la práctica, hallamos el logaritmo de ri, y luego, calculamos el antilogaritmo del resultado obtenido, de este modo se obtiene el valor absoluto de la raíz ri.
log | r i |= log α i −log α i−1 2 m (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 próximo a cero y por tanto, será la raíz buscada.
Cálculo de los coeficientes en las sucesivas iteracciones
Un polinomio cuyas raíces son reales y distintas es el caso más simple que se nos puede presentar. Sea por ejemplo el polinomio x3-4x2+x+6,cuyas raíces como se puede comprobar fácilmente por simple sustitución son 3, 2, y -1. En la tabla se observa los coeficientes αi resultantes del proceso de elevación 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 expresión a 1 2 −2 a 2 a 0
El tercer coeficiente α2 mediante la expresión a 2 2 −2 a 1 a 3
El cuarto coeficiente α3 mediante la expresión a 3 2
Podemos observar en la tabla que cada coeficiente en la iteración 9, es aproximadamente el cuadrado del coeficiente en la iteración precedente, habiéndose 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 número m de veces que se repite el proceso de elevación 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 aplicación sucesiva del procedimiento de elevación 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 raíces del polinomio, primero, su valor absoluto mediante la fórmula (4) y luego su signo, y guardarlas en un array unidimensional de dimensión n.
4. Mostrar las raíces 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 número máximo de iteracciones, o de veces que se repite el proceso de elevación al cuadrado.
En una función miembro denominada tabla, codificaremos el procedimiento de elevación al cuadrado de un polinomio de grado n. Partiendo del polinomio original (1), obtenemos el polinomio resultante del procedimiento de elevar al cuadrado (3) según el esquema (2). En la expresión (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 fórmula
α 3 = a 3 2 −2 a 2 a 4 +2 a 1 a 5 −2 a 0 a 6
Sorprendentemente, el lenguaje Java, no produce error por "overflow", es decir, cuando se supera el límite máximo o mínimo para un tipo de dato básico: int, long, o double. Por ejemplo, podemos guardar números 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 límites especificados para un tipo de dato dado.
En el código de la función tabla, cuando se supera el valor máximo que puede guardar un dato de tipo double, se interrumpe el proceso de elevación al cuadrado, y se sale fuera del bucle. La función estática isInfinite de la claseDouble se encarga de verificarlo devolviendo true si hemos superado dicho límite permitido.
solamente empleamos break salimos del bucle interior for y se continuaría en el bucledo...while el proceso de cálculo.
Nos queda ahora, la determinación 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 módulo %en conjunción 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 buclefor.
pot2=1; for(int i=1; i<=m; i++){ pot2*=2; }
El código de la función 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 raíces no cambian for(int i=1; i<n+1; i++){ a[0][i]/=a[0][0]; }
m--;//potencia de m de 2 pot2=1; for(int i=1; i<=m; i++){ pot2*=2; } }
Raíces reales y distintas
Un polinomio cuyas raíces son reales y distintas es el caso más simple que se nos puede presentar. Volvemos a estudiar el polinomio x3-4x2+x+6, cuyas raíces como se puede comprobar fácilmente por simple sustitución son 3, 2, y -1. En latabla que creamos en el apartado anterior, nos fijamos en la última fila.
Los módulos de las raíces reales, se calculan mediante la fórmula (4) . Para hallar las raíces 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 cálculos, emplearemos las funciones Math.log que halla el logaritmo de la raíz, y la función recíprocaMath.exp, para hallar la raíz. El valor absoluto de la raíz real se guarda en la variable local raiz.
private void raizRealSimple(int j){//valor absoluto de la raíz double logaritmo=(Math.log(a[m][j])-Math.log(a[m][j-1]))/pot2; double raiz=Math.exp(logaritmo);//determinación del signo raicesReales[numReales]=(Math.abs(valorPolinomio(raiz)) <Math.abs(valorPolinomio(-raiz)))? raiz : -raiz; numReales++; }
Para determinar el signo de la raíz real, hallaremos el valor del polinomio para dos valores raiz y -raiz, uno de los dos tiene que ser cero o muy próximo a cero. Una vez hallada la raíz real, su valor absoluto y su signo, se guarda en el arrayraicesReales.
Raíces reales dobles
En el apartado anterior, hemos supuesto que las raíces de un polinomio son reales y distintas, por lo que la aplicación del método de Graeffe es inmediata. Supongamos el polinomio x3-7x2+16x-12 que tiene una raíz doble 2, y una simple 3. Examinemos el comportamiento de sus coeficientes en el proceso de elevación al cuadrado en la tabla. Observaremos que el segundo coeficiente α1 se comporta como hemos descrito en el apartado anterior, cada coeficiente en una iteración es aproximadamente el cuadrado de la iteración 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 séptima iteración 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 iteración 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 señala la presencia de raíces reales dobles.
Para obtener la raíz doble, se ha de aplicar la siguiente fórmula que damos sin justificar. La raíz repetida se puede hallar calculando la raíz 2·2m de la razón de los coeficientes que inmediatamente preceden y siguen al coeficiente excepcional. Si i es el coeficiente excepcional, el módulo de la raíz doble se calcula mediante la fórmula
| r i |= 2 m+1 α i+1 α i−1 (5)
La codificación de la función raizRealDoble es similar a la función raizRealSimple. Primero, halla el módulo de la raíz aplicando la fórmula (5), y posteriormente, determina su signo. La raíz buscada se guarda dos veces en el arrayraicesReales, y se incrementa dos unidades el contador de raíces reales numReales.
private void raizRealDoble(int j){//valor absoluto de la raíz
Los polinomios pueden tener también raíces complejas y sus respectivas conjugadas. El caso más simple es el del un polinomio x2+1 que tiene una raíz compleja y su correspondiente conjugada. Sea el polinomio x3-7x2+16x-12 que tiene las siguientes raíces 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 iteración, además el valor del coeficiente en una iteración no es aproximadamente igual al cuadrado de su valor en la siguiente iteración, sino la mitad de dicho valor, un comportamiento similar al de las raíces dobles. Al coeficiente α1 le denominaremos coeficiente excepcional.
Para conocer si un polinomio tiene raíces 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 función miembro denominada cambiaSigno que explora cada columna buscando cambios de signo en el coeficiente de orden j, tras sucesivas iteraciones. Si no detecta ningún cambio de signo devuelve false y si encuentra un cambio de signo, quiere decir, que ha encontrado una raíz compleja y su correspondiente conjugada, añadiéndose una unidad al contador numComplejas. Posteriormente, calcula el módulo de dicha raíz, y por último, devuelve true.
El módulo de las raíces complejas se determina mediante la misma fórmula que las raíces dobles., calculando la raíz 2·2mde la razón de los coeficientes que inmediatamente preceden y siguen al coeficiente excepcional.
Para calcular una raíz compleja hemos de determinar su parte real, ya que la parte imaginaria se obtiene a partir de su módulo y de su parte real.
Denotemos las raíces complejas como u+vi y u-vi, si conocemos la parte real u, la parte imaginaria v se determina mediante la fórmula v= r 2 − u 2 , donde r es el módulo obtenido a partir de la fórmula (5). Queda, por tanto, determinar la parte real u, mediante la fórmula que daremos sin justificar.
u=− 1 2 ( coeficiente de x n-1 +suma de todas las raíces reales )
El código de la función denominada unaRaizCompleja, calcula primero la suma de todas las raíces reales. Luego, se determina u, y a partir de u se calcula v. La raíz compleja y su correspondiente conjugada se guardan el arrayraicesComplejas.
En esta porción de código, 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 número complejo y como funciones miembro, tiene los constructores, las funciones que suman, restan, multiplican o dividen dos numeros complejos.
Dos raíces complejas y sus conjugadas
Supongamos ahora el polinomio x5-4x4+11x3+4x2-10x-52 que tiene una raíz real 2 y dos raíces 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 además, se comportan de modo similar al de las raíces dobles, signo inequívoco de una raíz compleja. Por tanto, el polinomio tendrá dos raíces complejas y sus respectivas conjugadas, en total cuatro raíces que es el máximo número que permite este procedimiento.
Denotaremos las dos raíces complejas y sus respectivas conjugadas como u1±v1i y u2±v2i. Conocidos u1 y u2 se calculav1 y v2 mediante las fórmulas
v 1 = r 1 2 − u 1 2 v 2 = r 2 2 − u 2 2
Para determinar u1 y u2 se emplean las siguientes fórmulas
u 1 + u 2 =− 1 2 ( coeficiente de x n-1 +suma de todas las raíces reales ) u 1 r 2 2 + u 2 r 1 2 = (−1) n−1 a n−1 2(producto de las n-4 raíces reales ) − r 1 2 r 2 2 2 ( suma de las inversas de las n-4 raíces reales )
Se trata de un sistema de dos ecuaciones con dos incógnitas de las que se despeja u1 y u2
La función denominada dosRaicesComplejas, calcula la suma de todas las raíces reales, el producto y la suma de las inversas de dichas raíces. Después calcula los segundos miembros de cada una de las dos ecuaciones y los guarda en las variables locales y y z
La solución del sistema de dos ecuaciones con dos incógnitas es
u 1 = y r 1 2 −z r 1 2 − r 2 2 u 2 = −y r 2 2 +z r 1 2 − r 2 2
lo que nos permite calcular u1 y u2y posteriormente, v1 y v2
Finalmente, guardamos los valores obtenidos en el array raicesComplejas
La función pública hallaRaices se encarga de llamar a la función 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 iteración, e i el coeficiente: 0 es el índice del coeficiente de mayor grado y n es el índice del término independiente.
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 función cambiaSigno, y pasándole el índice i. Si la función devuelve true, quiere decir que se ha detectado una raíz 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 raíz if(cambiaSigno(i)){//raíz compleja y su correspondiente conjugada i++; continue; }
Posteriormente, analiza si hay una raíz doble, comprobando la relación entre los valores del coeficiente i en las dos últimas iteraciones. Recuérdese que a[m][i] es habitualmente el cuadrado de a[m-1][i], salvo para los coeficientes excepcionales. Cuando hay presentes raíces 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 raíces dobles. Cuando la variable cociente de la funcióncambiaSigno es cero (el logaritmo de la unidad es cero) o un valor muy próximo a cero (menor que la constante CEROfijada de antemano), se llama a la función raizRealSimple para hallar la raíz correspondiente al coeficiente i del polinomio. En caso contrario, tendremos una raíz doble y se llamará a la función raizRealDoble para calcularla.
La primera verificación se realiza sobre las raíces complejas ya que cumplen una condición más estricta que las raíces dobles, es decir, se comportan como las raíces dobles pero además presentan cambios de signo en las primeras iteraciones, mientras el término cuadrado no domina sobre los dobles productos.
Una vez concluido el análisis de cada uno de los coeficientes del polinomio en las sucesivas iteraciones que lo elevan al cuadrado y se han calculado las raíces reales simples y dobles se procede al cálculo de las raíces complejas. Si solamente hay una raíz compleja y su conjugada, se llama a la función unaRaizCompleja.
if(numComplejas==1){ unaRaizCompleja(); }
Si hay dos raíces complejas y sus conjugadas, se llama a la función dosRaicesComplejas.
if(numComplejas==2){ dosRaicesComplejas(); }
El código completo de la función miembro hallarRaices que calcula todas las raíces de un polinomio se muestra a continuación
public void hallarRaices(){ tabla();//el pimer coeficiente a[m][0] es siempre 1 for(int i=1; i<n+1; i++){ if(cambiaSigno(i)){//raíz compleja y su correspondiente conjugada i++; continue;
Primero, se muestran las raíces reales con dos cifras decimales
(double)Math.round(raicesReales[i]*100)/100
A continuación, se muestran las raíces complejas
System.out.println(raicesComplejas[2*i]);
La función println no solamente imprime objetos de la clase String, sino que también imprime datos numéricos de los tipos básicos y como vemos convierte automáticamente un número complejo en su representación textual, pero para ello hemos de redefinir en dicha clase la función toString miembro de la clase base Object de la cual derivan todas las clases en Java. La llamada implícita a dicha función muestra la parte real seguida de la imaginaria ambas con dos cifras decimales y seguida de la unidad imaginaria i. El código completo de la función mostrarRaices es el siguiente.
La función valorPolinomio halla el valor de un polinomio para un valor real de su variable x. Dicha función 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 términos for(int i=0; i<n+1; i++){ y+=a[0][i]*pot_x[n-i]; } return y; }
Es algo más 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 Complejoque describe dicha entidad matemática ha definir las siguientes operaciones: la suma de dos números complejos, el producto de un número real por un número complejo y la potencia de un número complejo.
La función valorPolinomio devolverá un número complejo, cuando se le pasa en su único argumento otro número complejo. Su definición en términos de las funciones mencionados es la siguiente.
Para hallar las raíces 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 continuación, 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 función miembro mostrarRaices, para hallar y mostrar las raíces del polinomio.
Graeffe g=new Graeffe(coef); g.mostrarRaices();
Para que practique el lector, se le propone hallar las raíces de los siguientes ecuaciones polinómicas:
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 método de Graeffe a un polinomio cuyo término independiente es distinto de cero. Ya que una ecuación como x3-4x2+x=0, tiene una raíz x=0 y por tanto, solamente precisamos calcular las raíces de la ecuaciónx2-4x+1=0.
Las raíces complejas de la ecuación 16x5-16x4-12x3+12x2+0x-1=0 no salen bien.
Referencias
Wylie. Matemáticas superiores para la ingeniería. Ediciones del Castillo. Apéndice.
B. P. Demidovich, I.A. Maron. Cálculo numérico funadamental. Método Lobachevski-Graeffe. Edt. Paraninfo (1977) págs 202-223
Ángel Franco.El método de Graeffe. Un procedimiento para hallar las raíces de un polinomio. Revista Profesional de Programadores nº 39, Abril de 1998, págs. 38-46.
Código 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 raíces no cambian for(int i=1; i<n+1; i++){ a[0][i]/=a[0][0]; } a[0][0]=1.0; m=1;
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 también 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 términos 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 raíz // System.out.println("Raiz simple"); double logaritmo=(Math.log(a[m][j])-Math.log(a[m][j-1]))/pot2; double raiz=Math.exp(logaritmo);//determinación del signo, y1 o y2 tienen que ser casi cero
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 números 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 números 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 número 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 número 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 números complejos//excepción 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 número complejo y un número 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 número e elevado a un número 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); }//raíz cuadrada de un número 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)); }//función auxiliar para la potencia de un número complejo private static double potencia(double base, int exponente){ double resultado=1.0; for(int i=0; i<exponente; i++){ resultado*=base; } return resultado; }//función auxiliar para la potencia de un número 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 número 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 número 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); }}