Capítulo 5 Algebra lineal y diagonalización de matrices

Anuncio
Capítulo 5
Algebra lineal y diagonalización de
matrices
Dedicamos este capítulo a la resolución de problemas lineales tales como sistemas de ecuaciones, determinantes, inversión de matrices y diagonalización.
5.1. Eliminación de Gauss y descomposición LU
Como vimos en el tema anterior, en C++ se pueden hacer cálculos de matrices mediante el
empleo de arreglos. La forma más conveniente de trabajar es definir los arreglos como punteros
simples y dobles para vectores y matrices, respectivamente. Si bien esta no es la forma más
cómoda, es la que produce programas con mayor portabilidad. Como ejemplo práctico de la
definición de matrices como punteros dobles, damos el programa gauslu.cpp, que resuelve un
sistema de ecuaciones lineales realizando la eliminación de Gauss con pivotado (y obtiene la
descomposición LU de la matriz de los coeficientes como un subproducto) :
//Programa eliminacion Gauss
#include <iostream>
#include <cmath>
#include <iomanip>
#include <fstream>
using namespace std;
con pivotado: matriz cuadrada y descomposicion LU
void gaussp(int n, double** m, double* v, double** l){
//Definir vector permutacion
int* per;
per=new int[n];
for (int i=1;i<n;i++) per[i]=i;
79
80
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
for (int i=0;i<n; i++){
l[i][i]=1;
if (i<n-1) for(int j=i+1; j<n; j++) l[i][j]=0;
}
//ciclo eliminacion
for (int i=0;i<n;i++){
//search for the pivot
double piv=abs(m[i][i]);;
int ipiv=i;
for (int j=i;j<n;j++){
if( abs(m[j][i])>piv){
ipiv=j;
piv= abs(m[j][i]);
}
}
//Permutar filas i y ipiv
for (int j=i;j<n;j++) {
double temp=m[i][j];
m[i][j]=m[ipiv][j];
m[ipiv][j]=temp;
}
double temp = v[i];
v[i]=v[ipiv];
v[ipiv]=temp;
//Permutar vector permutacion
int itemp=per[i];
per[i]=per[ipiv];
per[ipiv]=itemp;
//Eliminacion
for (int j=i+1; j<n; j++){
//m[j][i] se anula despues de la primera elim.
double
piv=m[j][i]/m[i][i];
l[j][i]=piv;
for (int k=i; k<n; k++){
m[j][k]=m[j][k]- piv*m[i][k];
}
v[j]=v[j]- piv*v[i];
}
5.1. ELIMINACIÓN DE GAUSS Y DESCOMPOSICIÓN LU
}
}
void backsus(int n,double**m, double* v, double* x){
//solucion de un sistema triangular superior por sustitucion hacia atras
for (int i=n-1;i>=0;i--){
x[i]=v[i];
for (int j= n-1;j>i;j--)
x[i]=x[i]-m[i][j]*x[j];
x[i]=x[i]/m[i][i];
}
}
int main(){
//definir data file
ifstream fin("gausslu.dat");
int n;
//leer dimension del sistema
fin>>n;
//Definir matriz coefficientes
double** mat;
mat = new double* [n];
for (int i=0;i<n;i++) mat[i]=new double [n];
//definir vector terminos independientes
double* b;
b = new double [n];
//Leer matriz coeficient;
for (int i=0; i<n; i++)
for (int j=0;j<n; j++)
fin>>mat[i][j];
//Leer vector terminos
for (int i=0;i<n;i++)
fin>>b[i];
independientes
// Verificar lectura de datos
cout<<"Datos leidos"<<endl;
for (int i=0; i<n; i++){
cout<<endl;
for (int j=0;j<n; j++)
cout<<mat[i][j]<<"
";
}
81
82
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
cout <<endl<<endl;
for (int i=0; i<n;i++)
cout<<b[i]<<" ";
cout<< endl<<endl;
//Definir matriz L
double** l;
l = new double* [n];
for (int i=0;i<n;i++) l[i]=new double [n];
gaussp(n,mat,b,l);
// Imprimir sistema reducido
cout<<"Sistema reducido"<<endl;
for (int i=0; i<n; i++){
cout<<endl;
for (int j=0;j<n; j++)
cout<<mat[i][j]<<"
";
}
cout <<endl<<endl;;
for (int i=0; i<n;i++)
cout<<b[i]<<" ";
cout<< endl<<endl;;
// Print l
cout<<" Matriz L"<<endl;
for (int i=0; i<n; i++){
cout<<endl;
for (int j=0;j<n; j++)
cout<<l[i][j]<<"
";
}
cout <<endl<<endl;
//Solucion del sistema por
double* x;
x= new double [n];
backsus(n,mat,b,x);
//Imprimir
solucion
substitucion hacia atras
5.2. DIAGONALIZACIÓN
83
cout<<"Vector de soluciones"<<endl;
for (int i=0; i<n;i++)
cout<<"x["<<i<<"]="<<x[i]<<" ";
cout<< endl;
return 0;
}
Como podemos observar, el volumen de líneas de programación utilizado cuando se definen
las matrices mediante punteros es mucho mayor que el que se necesita cuando se utilizan clases
de matrices.
Ejercicio 1: Examinad, compilad y ejecutad el programa, resolviendo diferentes sistemas de
ecuacciones (modificando el fichero gausslu.dat).
5.2. Diagonalización
En el mismo espíritu que el apartado anterior, el programa jacobi.cpp realiza la diagonalización de una matriz por el método de Jacobi, utilizando punteros:
//Programa de diagonalizacion por Jacobi
//con busqueda del mayor elemento fuera de la diagonal
#include <iostream>
#include <cmath>
#include <iomanip>
#include <fstream>
using namespace std;
//Numero de digitos y precision
const int nwide=10;
const int nprec=6;
void
jacobi(int n, double **m, double **v)
{
//Numero maximo de iteraciones
int maxiter = 100;
//Tolerancia
double tol = 1e-12;
84
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
//definir v(matriz vectores propios) como matriz identidad
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
v[i][j] = 0;
v[i][i] = 1;
}
//Calcular Delta
double delta = 0.;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
delta = delta + m[i][j] * m[i][j];
cout << "Delta inicial = " << delta << endl;
//ciclo de iteraciones
for (int i = 0; i < maxiter; i++)
{
//Busqueda del mayor elemento fuera de la diagonal
//Esto no es eficiente para grandes matrices. Hacer un barrido en este caso.
double apq = 0;
int ip = 0;
int iq = 1;
for (int j = 0; j < n; j++)
{
for (int k = j + 1; k < n; k++)
{
if (abs(m[j][k]) > apq)
{
ip = j;
iq = k;
apq = abs(m[ip][iq]);
}
}
}
cout << "p= " << ip << " q= " << iq << " apq= " << apq << endl;
//Determinar c, s, c, tau
double t = 1;
5.2. DIAGONALIZACIÓN
if (m[ip][ip] != m[iq][iq])
{
double alpha = (m[iq][iq] - m[ip][ip]) / (2*m[ip][iq]);
t = -alpha + alpha / abs(alpha) * sqrt(alpha * alpha + 1);
}
double
double
double
double
c =
s =
tau
d =
1 / sqrt(t * t + 1);
t * c;
= s / (1 + c);
m[ip][iq];
cout << " s= " << s << " c= " << c << " t=" << t << " tau="
<< tau << endl << endl;
//Actualizar matriz m en y por encima de la diagonal
m[ip][ip] = m[ip][ip] - t * m[ip][iq];
m[iq][iq] = m[iq][iq] + t * m[ip][iq];
for (int j = 0; j < ip; j++)
{
double temp1 = m[j][ip];
double temp2 = m[j][iq];
m[j][ip] = temp1 - s * (temp2 + tau * temp1);
m[j][iq] = temp2 + s * (temp1 - tau * temp2);
}
for (int j = ip + 1; j < iq; j++)
{
double temp1 = m[ip][j];
double temp2 = m[j][iq];
m[j][iq] = temp2 + s * (temp1 - tau * temp2);
m[ip][j] = temp1 - s * (temp2 + tau * temp1);
}
for (int j = iq + 1; j < n; j++)
{
double temp1 = m[ip][j];
double temp2 = m[iq][j];
m[iq][j] = temp2 + s * (temp1 - tau * temp2);
m[ip][j] = temp1 - s * (temp2 + tau * temp1);
}
85
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
86
m[ip][iq] = 0;
//Imprimir m en cada iteracion
cout << "Matriz iteracion " << i + 1 << endl;
for (int j = 0; j < n; j++)
{
cout << endl;
for (int k = 0; k <= j; k++)
cout << setw(nwide) << m[k][j] << "
}
cout << endl << endl << endl;
";
//Actualizar v
for (int j = 0; j < n; j++)
{
double temp1 = v[j][ip];
double temp2 = v[j][iq];
v[j][ip] = c * temp1 - s * temp2;
v[j][iq] = s * temp1 + c * temp2;
}
//Imprimir v
cout << "V iteracion " << i + 1 << endl;
for (int j = 0; j < n; j++)
{
cout << endl;
for (int k = 0; k < n; k++)
cout << setw(nwide) << v[j][k] << "
}
cout << endl << endl;
";
//Actualizar delta
delta = delta - d * d;
cout << " Delta= " << delta << endl << endl;
//Si ha convergido actualizar diagonal inferior de m y volver a main
if (abs(delta) <= tol)
{
for (int j = 0; j < n; j++)
for (int k = j + 1; k < n; k++)
5.2. DIAGONALIZACIÓN
m[k][j] = m[j][k];
cout << " Jacobi ha convergido con " << i + 1 <<
" iteraciones" << endl << endl;
cout << "Delta= " << delta << endl << endl;
return;
}
}
//Ha hecho maxiter iteraciones
cout << "Numero de maximo de iteraciones en jacobi sin convergencia"
<< endl << endl;
cout << " Delta= " << delta << endl << endl;
}
int
main()
{
//Definir data file
ifstream fin("jacobi.dat");
int n;
//Leer dimension del sistema(jacobi.dat)
fin >> n;
//Definir matriz coeficientes
double **mat;
mat = new double *[n];
for (int i = 0; i < n; i++)
mat[i] = new double[n];
//Definir matriz vectores propios
double **vecp;
vecp = new double *[n];
for (int i = 0; i < n; i++)
vecp[i] = new double[n];
//Elegir matriz en jacobi.dat o matriz de Hilbert
//Leer matriz en jacobi.dat;
for (int i = 0; i < n; i++)
87
88
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
for (int j = 0; j < n; j++)
fin >> mat[i][j];
/*
//Matriz de Hilbert;
for (int i=0; i<n; i++)
for (int j=0;j<n; j++)
mat[i][j]=1/double(i+j+2);
*/
//Verificar lectura datos
for (int i = 0; i < n; i++)
{
cout << endl;
for (int j = 0; j < n; j++)
cout << setw(nwide) << mat[i][j] << "
}
cout << endl;
";
jacobi(n, mat, vecp);
//Imprimir matriz diagonalizada
cout << "Matriz diagonalizada" << endl;
for (int i = 0; i < n; i++)
{
cout << endl;
for (int j = 0; j < n; j++)
cout << setw(nwide) << mat[i][j] << "
}
cout << endl << endl;
";
//Imprimir matriz vectores propios
cout << "Vectores propios (columnas) " << endl;
for (int i = 0; i < n; i++)
{
cout << endl;
for (int j = 0; j < n; j++)
cout << setw(nwide) << vecp[i][j] << "
}
cout << endl << endl;
//Comprobacion ortogonalidad vectores propios
";
5.3. CLASES ALGORITMOS
89
cout << "Productos escalares de vectores propios" << endl << endl;
for (int i = 0; i < n; i++)
{
for (int j = i; j < n; j++)
{
double temp = 0;
for (int k = 0; k < n; k++)
temp = temp + vecp[k][i] * vecp[k][j];
cout << setw(nwide) <<temp << " ";
}
cout << endl;
}
//Liberar memoria
for (int j = 0; j < n; j++) delete[] mat[j];
delete[] mat;
delete[] vecp;
mat = 0;
vecp = 0;
return 0;
}
Ejercicio 2: Examinad compilad y ejecutad el programa jacobi.cpp para diversas matrices
simétricas.
5.3. Clases algoritmos
Junto con la librería TNT, se proporciona la librería JAMA. La librería JAMA es una clase
de patrones, y los ficheros que la componen se incluyen con la cabecera algebralineal.h utilizada en la práctica anterior. Las funciones de la librería JAMA son accesibles con el namespace
CALNUM, al igual que las funciones de la librería TNT. Esta librería proporciona algoritmos
para la descomposición por Cholesky, la descomposición LU, y el cálculo de valores propios
(aparte de otros algoritmos que no vamos a utilizar). Cada algoritmo es una clase de C++. Por
ejemplo, si queremos descomponer una matriz por Cholesky, creamos un algoritmo de la clase Cholesky, que es una clase de patrónes que depende de un parámetro, que es el tipo de los
elementos de la matriz. Por ejemplo:
Cholesky<double>mi_chol1(A);
donde A es la matriz, y mi_chol1 es el nombre que le doy al objeto particular de la clase del
algoritmo de Cholesky, que he creado para trabajar con la matriz A. Si tengo una matriz B del
tipo float que quiero descomponer LU, creo un elemento de la clase LU:
LU<float>mi_lu(B);
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
90
Si deseo descomponer una matriz C, de elementos del tipo long double en valores propios,
creo un elemento de la clase Eigenvalue:
Eigenvalue<long double>mis_valores_propios(C);
Lo algoritmos están programados de forma que se verifique si las dimensiones de las matrices asignadas son correctas. Por ejemplo, si intentamos descomponer por Cholesky una matriz
no simétrica, se produce un mensaje de error. El dato de cada uno de estos tres algoritmos es la
matriz asociada. Con los datos, las clases de algoritmos producen una serie de resultados, utilizando las funciones correspondientes de la clase. Una diferencia esencial entre las clases del
C++ y las estructuras del C, es que las clases contienen,además de datos, funciones. Para invocar
las funciones de la clase, se escribe el elemento de la clase unido al nombre de la función por un
punto. Por ejemplo, si milu es un elemento de la clase LU, el determinante se calcula invocando la función det() sobre este elemento, lo que se hace escribiendo milu.det(). Si queremos
guardar el determinante, debemos de hacerlo en una variable del tipo que devuelve la función.
Por ejemplo, det() devuelve un double, por lo que para guardar el determinante tendremos que
escribir, por ejemplo,
double deter=milu.det();
donde deter es una variable de tipo double que hemos creado para guardar el determinante.
Las funciones de cada una de las clases son:
Cholesky
Si chol es un algoritmo de la clase Cholesky y v es un vector y B una matriz, las siguientes
funciones están definidas:
1. chol.getL(): Devuelve la matriz triangular de Cholesky.
2. chol.is_spd(); Devuelve 1 si la matriz es definida positiva y 0 en caso contrario. Su uso
es, por ejemplo
if (chol.is_spd) L= chol.getL();
else cout< <”error: matriz no definida positiva”< <endl;
3. chol.solve(v), con v un vector: Devuelve el vector x, solucion de A ∗ x = v.
4. chol.solve(B); Da la matriz X, solución de A ∗ X = B.
El programa jama_cholesky.cpp ilustra el empleo de estas funciones:
//Ejemplos con TNT y JAMA
//compilar con g++ -I./templates
#include <iostream>
#include <iomanip>
#include "algebralineal.h"
using namespace std;
using namespace CALNUM;
jama_cholesky.cpp
5.3. CLASES ALGORITMOS
typedef double
91
Real; //tipo generico Real
int main(){
cout<< "Entrar dimension"<<endl;
int n;
cin>>n;
Matrix<Real> D(n,n);
Matrix<Real> E(n,n);
Vector<Real> b(n);
b=1.;
//Choleski. Atencion la matriz debe ser definida positiva
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
D[i][j]=Real(1.)/Real(i+j+1.);
cout<<D<<endl;
Cholesky<Real> chol(D);
Matrix<Real>M=chol.getL();
Matrix<Real> MT= transpose(M);
cout<<"matriz de Cholesky M"<<endl<<M<<endl;
cout<<"transpuesta M"<<endl<<MT<<endl;
cout<<"producto cholesky"<<endl<<M*transpose(M)<<endl<<
"matriz inicial"<<endl<<D<<endl;
//Resolucion de ecuaciones
Vector<Real> x= chol.solve(b); //Solucion de A*x=b
cout<<" x= "<< x<<endl;
cout<<"D*x= "<<D*x<<endl; //Comprobacion A*x=b
return 0;
}
LU
Si lu es un algoritmo de la clase LU, que tiene asignado la matriz A y si v es un vector y B
una matriz, las siguientes funciones están definidas:
1. lu.getL(): Devuelve la matriz inferior L.
2. lu.getU(): Devuelve la matriz superior U.
3. lu.det(): Devuelve el determinante.
92
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
4. lu.getPivot(): Devuelve el vector de pivotes.
5. lu.solve(v): Devuelve el vector x, solución de A ∗ x = v.
6. lu.solve(B): Devuelve la matriz X, solución de A ∗ X = B.
El programa jama_lu.cpp ilustra el empleo de las funciones de la clase LU:
//Ejemplos con TNT y JAMA
//compilar con g++ - I./templates jama_lu.cpp
#include <iostream>
#include <iomanip>
#include "algebralineal.h"
using namespace std;
using namespace CALNUM;
typedef double Real; //tipo generico Real
int main()
{
//Descomposicion LU de una matriz de Hilbert
cout << "Entrar dimension" << endl;
int
n;
cin >> n;
Matrix < Real > H(n, n);
Vector < Real > b(n);
b=1.;
//Creacion matriz de Hilbert
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
H[i][j] = Real(1.) / Real(i + j + 1.); //Conversion al tipo Real
cout << H << endl;
//descomposicion LU
//Creacion objeto llamado mi_lu de la clase de algoritmos LU
LU < Real > mi_lu(H);
Matrix < Real > L = mi_lu.getL(); //obtencion matriz L
cout << L << endl;
Matrix < Real > U = mi_lu.getU(); //Obtencion matriz U
cout << U << endl;
Real
det = mi_lu.det(); //Obtencion determinante
cout << "det =" << det << endl;
cout << "L*U = " << L * U << endl;//Producto L * U
5.3. CLASES ALGORITMOS
93
Matrix < Real > IH = mi_lu.inv(); //Calculo de la inversa
cout << "inversa = " << IH << endl;
Matrix < Real > I = H * IH;
cout << "H*IH = " << I << endl; //comprobacion H * inv(H) = I
//Solucion de ecuaciones
Vector < Real > x = mi_lu.solve(b); //Solucion H * x = b;
cout <<"x = "<<x<<endl;
return 0;
}
Eigenvalue
Si eig es un algoritmo de la clase Eigenvalue con matriz A, y si B,C y D son matrices de las
dimensiones adecuadas y lambda un vector, las siguientes funciones están definidas:
1. Eigenvalue <double >eig(A): Define un objeto de nombre eig perteneciente a la clase
Eigenvalue y con matriz asociada A.
2. eig.getRealEigenvalues(lambda: Rellena el vector lambda con las partes reales de los
valores propios.
3. eig.getImagEigenvalues(lambda): Rellena el vector lambda con las partes imaginarias
de los valores propios.
4. eig.getD(B): Rellena la matriz B con la matriz A diagonalizada.
5. eig.getV(C): Rellena la matriz C con los vectores propios de A, como columnas.
El programa jama_eigen.cpp ilustra el uso de las diferentes funciones de la librería JAMA:
//Ejemplos con TNT y JAMA
//compilar con g++ - I./templates jama_eigen.cpp
#include <iostream>
#include <iomanip>
#include "algebralineal.h"
using namespace std;
using namespace CALNUM;
typedef long double Real;//tipo generico Real
int main()
{
cout << "Entrar dimension" << endl;
int
n;
cin >> n;
94
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
Matrix < Real > H(n, n);
Matrix < Real > E(n, n);
//Creacion matriz Hilbert
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
H[i][j] = Real(1.) / Real(i + j + 1.);
cout << "H=" << H << endl;
//valores y vectores propios
//Se crea un elemennto val_prop de la clase Eingenvalue
Eigenvalue < Real > val_prop(H);
//creacion vector de valores propios reales en lambda
Vector < Real > lambda;
//relleno de lambda con los valores propios
val_prop.getRealEigenvalues(lambda);
cout << "valores propios=" << lambda << endl;
Matrix < Real > V;//Creacion de matriz de vectores propios
//vectores propios, se escriben en la matriz V
val_prop.getV(V);//relleno de V
//matriz diagonal, se escribe en D
Matrix < Real > D;
val_prop.getD(D);//relleno de D con la diagonal de H
cout << "Matriz Diagonal= " << D << endl;
cout << " Vectores propios = " << V << endl;
//Comprobacion H * V = V * D
Matrix < Real > AV = H * V - V * D;
//Comprobacion H * V = V * D
cout << " H*V-V*D= " << AV << endl;
Matrix < Real > I = V * transpose(V);
//Comprobacion ortogonalidad V
cout << "V*transpose(V)=" << I << endl;
I = transpose(V) * H * V;
5.3. CLASES ALGORITMOS
95
//Comprobacion VT * H * V = D
cout << "transpose(V)*H*V=" << I << endl;
return 0;
}
5.3.1.
Funciones adicionales de la librería TNT
Con objeto de facilitar la manipulación de matrices, se han definido una serie de funciones que permiten extraer la diagonal, la triangular superior o inferior o una determinada fila o
columna. Su empleo se ilustra en el programa tnt_ejemplos2.cpp:
//Ejemplos funciones adicionales con TNT
//compilar con g++ - I./templates tnt_ejemplos2.cpp
#include <cmath>
#include <iostream>
#include "algebralineal.h"
using namespace std;
using namespace CALNUM;
int main()
{
int n; //dimension, que puede ser leida en ejecucion
cout << "Entrar dimension" << endl;
cin >> n;
//Ejempos con matrices y escalares
//Construccion de tres matrices de double
Matrix < double >A(n, n), B(n, n), C(n, n);
for(int i = 0; i < n; i++)
A[i][i] = double (i); //relleno de la matriz A
//creacion identidad de dimension n
Matrix<double> I=identity<double>(n);
cout<<I<<endl;
B = 2.; //todos los elementos de B = 2.
cout<<"B= "<<B<<endl;
C=6./B;
cout<< "C=6./B "<< C<<endl;
//extraccion triangular inferior estricta (lower)
96
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
C=lower(B);
cout<< "lower(B) ="<<C<<endl;
//extraccion triangular superior estricta (upper)
C=upper(B);
cout<< "upper(B) ="<<C<<endl;
//extraccion matriz diagonal
C=diagonal(B);
cout<< "diagonal(B) ="<<C<<endl;
//division de matrices elemento a elemento
C=upper(B)/B;
cout<<"upper(B)/B= "<<C<<endl;
//normas de matrices
cout<<"normmax(upper(B)/B)= "<<normmax(C)<<endl;
cout<<"norm1(upper(B)/B)= "<<norm1(C)<<endl;
cout<<"normf(upper(B)/B)= "<<normf(C)<<endl; //norma Frobenius
//Extraccion vector diagonal
Vector<double> b= diag(A);
cout<<"diag(A)="<<b<<endl;
//Proyecta fila n de matriz C, empezando por 0 ( row(C,n) )
b=row(C,1);
cout<<b<<endl;
//Proyecta columna n de matriz C, empezando por 0 ( column (C,n) )
b=column(C,1);
cout<<b<<endl;
//Multiplicacion elemento a elemento
cout<<C%B<<endl;
return 0;
}
5.4. Raíces de polinomios
Uno de los métodos más eficientes de calcular todas las raíces de un polinomio es diagonalizar una matriz cuya ecuación característica es dicho polinomio. Las raíces son los valores
5.4. RAÍCES DE POLINOMIOS
97
propios de esta matriz. Si tenemos un polinomio
Pn (x) = an xn + an−1 xn−1 + · · · + a1 x + a0
se puede comprobar fácilmente que la siguiente matriz tiene dicho polinomio como ecuación
característica:
 a
an−2
a1
a0 
n−1
−
−
··· −
−
an
an
an
an 


1
0
···
0
0 




0
1
·
·
·
0
0




.
.
.
.
.
.
.
.
.
.

.
.
.
.
. 
0
0
···
1
0
El programa raices_pol.cpp utiliza la clase Eigenvalue para calcular las raíces de un
polinomio.
//Ejemplos con TNT y JAMA
//compilar con g++ - I./templates raices_pol.cpp
#include <iostream>
#include <iomanip>
#include <complex>
#include "algebralineal.h"
using namespace std;
using namespace CALNUM;
typedef long double Real;
//tipo generico Real
Real pol(Vector < Real > a, Real x, Real y)
{
//Calculo de un polinomio por el algoritmo de Horner
complex < Real > p = 0.;
int
n = a.size();
complex < Real > z = complex < Real > (x, y);
p = a(n);
for (int i = n - 1; i >= 1; i--)
p = p * z + a(i);
Real
pr = real(p);
return pr;
}
int main()
{
cout << "Entrar dimension" << endl;
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
98
int
cin >> n;
Matrix < Real >
Matrix < Real >
Vector < Real >
//Definicion de
a = 1.;
n;
A(n, n);
E(n, n);
a(n + 1), lambdar(n), lambdai(n);
a.En caso general leer n y a de fichero
//Creacion matriz
for (int i = 0; i < n - 1; i++) {
A[0][i] = -a[n - i - 1] / a[n];
A[i + 1][i] = 1.;
}
A[0][n - 1] = -a[0] / a[n];
cout << "A = " << A << endl;
//valores y vectores propios
//Se crea un elemennto val_prop de la clase Eingenvalue
Eigenvalue < Real > val_prop(A);
//relleno de lambda con los valores propios
val_prop.getRealEigenvalues(lambdar);
cout << "parte real raices=" << lambdar << endl;
val_prop.getImagEigenvalues(lambdai);
cout << "parte imaginaria raices=" << lambdai << endl;
//Verificacion de las raices
for (int i = 0; i < n; i++) {
Real
p = pol(a, lambdar[i], lambdai[i]);
cout << p << endl;
}
return 0;
}
5.5. Ejercicios
1. Utilizad el programa gausslu.cpp para resolver el siguiente sistema de ecuaciones lineales:
−x1 + x2 − 3x4 = 4
x1 + +3x3 + x4 = 0
x2 − x3 − x4 = 3
5.5. EJERCICIOS
99
3x1 + x3 + 2x4 = 1
2. Escribid un programa que resuelva, utilizando las librerías JAMA y TNT, el siguiente
sistema de ecuaciones lineales
6x1 − 2x2 + 2x3 + 4x4
12x1 − 8x2 + 4x3 + 10x4
3x1 − 13x2 + 3x3 + 3x4
−6x1 + 4x2 + 2x3 − 18x4
=
=
=
=
0
−10
−39
−16
El programa debe calcular e imprimir, además, la inversa y el determinante de la matriz de
coeficientes, la matriz L, la matriz U, y el producto L*U.
3. Diagonalizad, utilizando el programa jacobi.cpp, la matriz simétrica:


2 0
1
 0 3 −2 
1 −2 −1
El programa debe de imprimir en fichero los valores y vectores propios.
4. Diagonalizad, utilizando las librerías TNT y JAMA, la matriz:


4 −1 −1 0
 −1 4
0 −1 


 −1 0
4 −1 
0 −1 −1 4
Imprimid los valores y vectores propios. Comprobad que para cada valor propio λ, con
vector propio v, se satisface:
A ∗ v = λv
Comprobad que la matriz V , cuyas columnas son los vectores propios, es ortogonal, es
decir,
V T ∗V = I
Comprobad, además, que
donde D es la matriz diagonalizada.
V T ∗ A ∗V = D
5. Escribid un programa, al estilo de gauslu.cpp, es decir definiendo las matrices como punteros de punteros, que factorice una matriz mediante el algoritmo de Cholesky. Aplicadlo
a la matriz del ejercicio anterior. El programa debe de imprimir la matriz triangular M.
Comprobad que M ∗ M T = A. Comprobar el resultado con la clase Cholesky de la librería
JAMA. Incluid ambos programas, con sus correspondientes resultados, en la memoria de
la práctica.
100
CAPÍTULO 5. ALGEBRA LINEAL Y DIAGONALIZACIÓN DE MATRICES
6. Modificad el programa raices_pol.cpp para calcular las raíces del polinomio del primer
ejercicio de la práctica 3:
P(x) = x5 − x4 − 5x3 + 5x2 + 6x − 6
La dimensión del polinomio y los coeficientes deben leerse desde fichero.
Descargar