Resolver ecuaciones. Ceros de funciones
Resolver una ecuación del tipo Fórmula=0
es determinar el conjunto de valores de la variable (o variables) para los que la Fórmula
se anula. Es ésta una cuestión complicada y sólo para algunos tipos de Fórmula
es posible encontrar soluciones exactas, reales o complejas. En muchos casos sólo es posible encontrar soluciones aproximadas.
Soluciones exactas: ecuaciones lineales y algunas otras
Los sistemas lineales de ecuaciones y algunas otras ecuaciones sencillas pueden resolverse con ayuda del comando
- solve(Ecuaciones,Variables);
Trata de resolver una o varias ecuaciones algebraicas que contengan una o varias variables.
En el caso de tratarse de varias Ecuaciones con varias Variables cada una de estos
dos grupos está delimitado mediante corchetes [] y dentro Ecuaciones
y Variables los diferentes elementos aparecen separados entre sí
mediante comas. Cuando en las Ecuaciones no aparece explicitamente el signo =, Maxima sobreentiende que las expresiones están igualadas a 0.
Las Ecuaciones pueden ser de de muy diversos tipos (incluyendo funciones racionales, exponenciales, trigonométicas...), si bien, obviamente, sólo se obtienen resultados satisfactorios en ciertos casos, como las ecuaciones lineales o ecuaciones no lineales particularmente simples.
En el caso de que no haya ambiguedad posible, no es preciso indicar explícitamente cuales son las incógnitas, pero es preferible indicar cuales son las incógnitas.
- linsolve ([EC_1, ..., EC_m], [x_1, ..., x_n]);
Permite resolver sistemas de ecuaciones lineales en varias variables.
Las ecuaciones EC_1...EC_m
son polinomios de primer grado en las variables x_1,...x_n
con dos miembros separados entre sí por el signo =
. Pero también es posible escribir el polinomio sin incluir un sigo =
, en cuyo caso se añadirá internamente =0
para convertir el polinomio en una ecuación.
- multiplicities;
Informa sobre el grado de multiplicidad.
Internamente el comando solve
puede hacer uso del comando linsolve
y de otros comandos, dependiendo de la naturaleza de las ecuaciones a resolver. Para más información véase en la consola de Maxima el resultado de la ejecución del siguiente código ? solve;.
Se dedica más atención al comando linsolve en la sección correspondiente a las matrices, que es el ámbito natural para estudiar los sistemas de ecuaciones lineales. Veamos a continuación algunos ejemplos de utilización del comando solve
.
- /* para algunos polinomios, Maxima es capaz de encontrar sus raíces, reales y complejas */
solve(-4*x^3 - 12*x^2 + 12, x);
- /* pero para otros no es capaz de hacerlo */
solve(x^5-2*x^2-1);
- /* raíces de un sistema lineal de ecuaciones */
solve([2*x-7*y-2,x-y+3], [x,y]);
- /* en este ejemplo, y el siguiente (que no pone =0), aparecen funciones no polinómicas */
solve(2*log(x)=-1);
- /* además, advierte aquí que algunas soluciones pueden haberse perdido (incitando a reflexionar) */
solve (asin (cos (3*x))*(f(x) - 1), x);
-
solve(x^3-x^2-5*x-3,x); multiplicities;
- Un polinomio de interpolación de Lagrange es una función polinómica P de grado n-1 determinada por el hecho de pasar por n puntos [x_1,y_1],[x_2,y_2],...[x_n,y_n] previamente fijados. El problema puede ser abordado como la solución de un sistema lineal de ecuaciones (en la sección Interpolación se utilizan otros recursos).
/* polinomio con coeficientes a calcular */
p(x):= a*x^4+b*x^3+c*x^2+d*x+e$
/* puntos por los que pasa */
[[7,2],[8,2],[1,5],[3,2],[6,7]]$
/* en consecuencia, ha de cumplir las 5 ecuaciones */
e1: ev(p(x),[x=7])=2;
e2: ev(p(x),[x=8])=2;
e3: ev(p(x),[x=1])=5;
e4: ev(p(x),[x=3])=2;
e5: ev(p(x),[x=6])=7;
/* resolviendo, determinamos los coeficientes */
linsolve([e1,e2,e3,e4,e5],[a,b,c,d,e]);
Alternativamente, en lugar del comando ev podría haberse utilizado el comando at.
Soluciones aproximadas
Para la obtención de soluciones aproximadas el teorema de Bolzano proporciona un marco teórico muy útil al garantizar que un función real de variable real que toma valores con signos opuestos en los puntos Punto1 y Punto2 necesariamente se anula en algún punto intermedio.
Si se eligen bien las parejas de puntos de forma que sólo se produzca un cambio de signo en cada un de los intervalos que definen dichas parejas es posible entonces obtener numéricamente todas las soluciones reales. El grafismo (y eventualmente resultados del cálculo diferencial) puede servir de gran ayuda para conseguir tales soluciones aproximadas con ayuda de Maxima.
- find_root(Función, Variable, Punto1, Punto2);
Calcula un valor aproximado para un cero de la Función en el intervalo determinado por Punto 1 y Punto 2 que deben elegirse de forma que el signo de la Función sea diferente en cada uno de ellos, y en ese intervalo no debe haber otro cambio de signo para la función.
- realroots(Polinomio,Precisión);
Calcula las raíces reales aproximadas de un Polinomio con una cierta cota de error controlado por Precisión.
Los coeficientes deben ser números racionales, o decimales, que son convertidos a racionales, pero no pueden ser símbolos como %e, %pi,...
. Las soluciones son expresadas en forma racional. El argumento Precisión no es obligatorio; si no se especifica se le asignará el valor que tenga la variable rootsepsilon
. Si Precisión es menor que 1, las raíces enteras, caso de existir, se obtienem de forma exacta.
- allroots(Polinomio);
Calcula todas las raíces reales o complejas aproximadas del Polinomio
- algsys ([Polinomios],[Variables])
Parecido a solve en cuanto a sintaxis, con la salvedad de que los [] hay que ponerlos siempre aunque sólo contengan un elemento, proporciona soluciones exactas, si puede, o si no aproximadas, reales y complejas. Con la variable opcional realonly:true
muestra sólo soluciones reales.
Veamos algunos ejemplos y posibles estrategias a usar
- Para la función que aparece a continuación, el dibujo de la misma ayuda a identificar los intervalos en los que la función tiene un cambio de signo, y sólo uno. Lo cual permite calcular, de forma aproximada, las soluciones que existen.
f(x):= x- x^2 + 1+log (1+x)$
plot2d( f(x),[x,-0.9,5]);
find_root( f(x)=0, x,-0.9,0);
find_root( f(x)=0, x, 0,3);
- Más arriba habíamos constatado que
solve
había fracasado en el cálculo de las raíces del polinomio x^5-2*x^2-1
. Dibujar la gráfica de la función, no resulta tan "transparente" como en el ejemplo precedente y por ello analizaremos también la función derivada que nos da información sobre los intervalos de crecimiento de la función.
kill(f)$
f(x) := x^5-2*x^2-1;
plot2d(f(x) ,[x,-3,3]);
a la vista de la gráfica es claro que en el intervalo [-3,3] existe al menos un cambio de signo, pero cabe la duda de si existe más de un cambio de signo. Vamos a analizar la derivada para mayor seguridad.
kill(g);
define(g(x),diff(f(x),x));
solve(g(x),x);
/* a la vista de los ceros de la derivada es natural elegir el intervalo */
plot2d(g(x),[x,-1,2]);
plot2d(f(x),[x,-1,2]);
find_root(f(x)=0, x, -1, 2);
- Debido a que se trata de un polinomio, y no de una función general, podemos obviar lo anterior y dejar que sea Maxima el encargado de realizar las actuaciones que considere oportunas para encontrar las raíces utilizando fracciones (como ya hemos dicho).
realroots(f(x)); %,numer;
- Pero un polinomio de grado 5 tiene cinco raíces en el cuerpo de los complejos: las otras 4 las podemos obtener con ayuda del otro comando de Maxima
allroots(f(x));
- Otro comando diferente para el mismo polinómio
algsys([f(x)],[x]);
- Encontrar los puntos de corte entre una circunferencia y una elipse
algsys([x^2+y^2=1,(x-1)^2+y^2=1/4],[x,y]);
- Para acabar vamos, jugando con ventaja, a preparar un polinomio con raíces conocidas a fin de comparar los resultados de los diferentes comandos
kill(f)$ f(x):=(x-0.1)*(x+3)*(x^2+x+1); solve(f(x));
f(x):=(x-0.1)*(x+3)*(x^2+x+1)$ realroots(f(x)); ev(%,numer);
f(x):=(x-0.1)*(x+3)*(x^2+x+1)$ allroots(f(x));
Colaborando con Maxima en la solución de ecuaciones
A veces Maxima fracasa al resolver ecuaciones que, objetivamente, no son complicadas. En esas ocasiones si usted tiene idea de como se podría resolver la ecuación y le ayuda un poquito, Maxima puede ser capaz de tener éxito. Ayudarle, generalmente, consiste pedirle que realice una transformación en la ecuación que le lleva a otra que sea capaz de resolver. Comandos para contraer o expandir logaritmos, para tomar logaritmos o exponenciales en una igualdad, etc. son estrategias útiles. A continuación hay algunos ejemplos que sirven como botones de muestra.
Primer ejemplo.
- Comenzamos matando las asignaciones previas
kill(all);.
Como en esta sección no hemos cargado ningún paquete adicional, no hay riesgo de que se borre de la memoria el contenido del mismo; pero en otras condiciones puede también tener efectos secundarios.
Ponemos nombre a la ecuación que queremos resolver (no es imprescindible)
EC:log(y)-log(y+1)=log(x-1)-log(x);
- Tratamos, sin éxito de resolver la ecuación:
solve(EC,y);
- Como no sabe resolver la ecuación anterior, comenzamos nuestro proceso de ayuda. Le decimos que previamente "contraiga" los logaritmos usando el comando logcontract.
EC2:logcontract(EC);
tras lo cual, ya sabe resolver la ecuación
solve(EC2,y);.
Segundo ejemplo (variante del anterior)
- Ya hemos fracasado con
EC:log(y)-log(y+1)=log(x-1)-log(x);
solve(EC,y);
- Hagamos la exponencial de ambos miembros, utilizando el comando map de Maxima, a ver qué ocurre
EC3:map(exp,EC);
La ecuación se ha simplificado enormemente y resolverla ya no es problema.
solve(EC3,y);
Tercer ejemplo.
- Comenzamos matando las asignaciones anteriormente realizadas (una buena práctica)
kill(all); EC:0.5=exp(k*500)
- Le pedimos a Maxima que resuelva la ecuación:
solve(EC,k);
La solución, que tarda bastante tiempo, aparece en la pantalla correspondiente a la consola de Maxima. Si se cansa de esperar reinicie Maxima desde uno de los items que aparecen del menú "Archivo" llamado "Reiniciar".
- Maxima no se ha dado cuenta, pero tomando logaritmos el problema se simplifica.
Para ayudarle le decimos que tome logaritmos en la ecuación antes de resolverla. De nuevo hacemos uso del comando
map
kill(all); EC:0.5=exp(k*500);
Aplicamos logaritmos
EC2:map(log,EC);
E intentamos resolver de nuevo
solve(EC2,k);
- Las dos soluciones anteriores no son equivalentes.
Para convencerse de ello podemos resolver la misma ecuación (en la ventana de la consola) con un exponente de menor tamaño, como por ejemplo,
kill(all)$ EC: 0.5=exp(k*5);
Pedimos que la resuelva
solve(EC,k);.
Reutilizar las soluciones
La solución de un sistema lineal de ecuaciones proporciona unos valores para las incognitas que pueden ser requeridos para realizar otros cálculos con posterioridad. Pero las variables tienen un carácter local y las asignaciones se pierden después de ejecutar el comando Maxima que permite resolverlas. En algunos casos el resultado obtenido puede recuperarse de forma simple utilizando % (o % con el número de salida que corresponda), pero en otros casos las salidas son listas y las variables tienen un carácter local y las asignaciones no se conservan después de ejecutar comandos como solve. En el caso del comando solve es posible mantener la asignación con carácter global utilizando la variable booleana globalsolve. Otro comando útil para la reutilización de soluciones es rhs
- globalsolve que acepta las asignaciones true y false (valor por defecto).
Cuando se resuelven ecuaciones que no son sistemas de dos o más ecuaciones lineales, solve ignora el valor de dicha variable y lo mismo ocurre con otros comandos que resuelven ecuaciones.
- rhs(Expresión)
Devuelve el lado de la derecha (right-hand side) cuando Expresión consta de dos bloques separados entre sí mediante un signo = (u otros conceptualmente similares como: <, >, <=, =>, #, :=). Paralelamente existe para el lado de la izquierda
lhs(Expresión)
Respectivamente, los comandos second(Expresión) y los comandos first(Expresión) pueden ser utilizados con el mismo objetivo, si bien su utilidad es más amplia: ? first;
Veamos algunos ejemplos:
- Por defecto,
globalsolve:false
. En este ejemplo, aunque calcula los valores de x e y, no los retiene.
print("El valor de globalsolve es: ", globalsolve)$
solve([2*x-y=a,3*x+y=b],[x,y]);
print("El producto de las soluciones es:")$
x*y;
- Haciendo
globalsolve:true
conseguimos que retenga en memoria los valores.
kill(x,y)$ globalsolve:true$
print("El valor de globalsolve es: ", globalsolve)$
solve([2*x-y=a,3*x+y=b],[x,y]);
print("Compare este resultado con el anterior. El = se ha transformado en : ")$
print("El producto de las soluciones es:")$
x*y;
- El comando rhs permite usar una técnica diferente para abordar la cuestión precedente, e ilustra el funcionamiento del mismo:
kill(all)$ globalsolve:false$
print("El valor de globalsolve es: ", globalsolve)$
solve([2*x-y=a,3*x+y=b],[x,y]);
%[1];
[x,y]:[ rhs(%[1]),rhs(%[2]) ];
print("Como la solución es una lista, cuyo único elemento es
a su vez una lista, hemos procedido a recuper dicho elemento,
que es el que nos interesa, y que contenía dos ecuaciones.
Hemos recuperado la partes derechas de tales ecuaciones.
Haciendo uso de : se las asignamos a x e y.
Ahora ya podemos calcular xy")$
x*y;
- Una variante usando second :
kill(all)$
print("El valor de globalsolve es: ", globalsolve)$
solve([2*x-y=a,3*x+y=b],[x,y]);
%[1];
[x,y]:[ second(%[1]),second(%[2]) ];
x*y;
-
Ap_ResolverEcuaciones.wxmx