00001 // Sparse_Matrix.h Copyright (C) 2004 adolfo@di-mare.com 00002 00003 /** \file Sparse_Matrix.h 00004 \brief Declaraciones y definiciones para la clase \c Sparse_Matrix. 00005 00006 \author Adolfo Di Mare <adolfo@di-mare.com> 00007 \date 2004 00008 00009 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 00010 */ 00011 00012 #ifndef Sparse_Matrix_h 00013 #define Sparse_Matrix_h 00014 00015 #include <cassert> // assert() 00016 00017 /// Definido por la biblioteca C++ estándar 00018 namespace std {} // Está acá para que Doxygen lo documente 00019 00020 /// Matriz chirrisquitica de adolfo@di-mare.com 00021 namespace Mx { 00022 00023 /** Esta es una clase matriz muy chirrisquitica almacenada como una matriz rala. 00024 - La matriz tiene tamaño \c rows() x \c cols(). 00025 - Se le puede cambiar el tamaño dinámicamente con el método \c reSize(). 00026 - La clase \c E debe incluir un neutro para la adición, cuyo valor debe poderse 00027 obtener invocando el convertidor \c Sparse_Matrix<E>::value_type(). 00028 - Las operaciones aritméticas "+" "-" "*" deben estar definidas para 00029 \c Sparse_Matrix<E>::value_type y debe existir el valor \c Sparse_Matrix<E>::value_type(). 00030 \see http://www.oonumerics.org/oon/ 00031 */ 00032 template <class E> 00033 class Sparse_Matrix { 00034 public: 00035 /// Tipo del objeto almacenado, similar al nombre usado en STL 00036 typedef E value_type; 00037 /// Tipo del objeto almacenado, similar al nombre usado en STL 00038 typedef value_type& reference; 00039 /// Tipo del objeto almacenado, similar al nombre usado en STL 00040 typedef const value_type& const_reference; 00041 /// Tipo del tamaño de un objeto, similar al nombre usado en STL 00042 typedef unsigned size_type; 00043 public: 00044 Sparse_Matrix(unsigned m = 1, unsigned n = 1); 00045 Sparse_Matrix(const Sparse_Matrix& o); ///< Constructor de copia 00046 /// Matriz escalar de valor \c V. 00047 Sparse_Matrix(const value_type V) : m_capacity(0) { reSize(1,1); (*this)(0,0) = V; } 00048 ~Sparse_Matrix(); 00049 public: 00050 unsigned rows() const { return m_rows; } ///< Cantidad de filas de la matriz 00051 unsigned cols() const { return m_cols; } ///< Cantidad de columnas de la Matriz 00052 unsigned size() const { return m_rows * m_cols; } ///< Cantidad de valores almacenados en la matriz 00053 unsigned count() const { return size(); } ///< Cantidad de valores almacenados en la matriz 00054 /// Cantidad máxima posible de valores diferentes que pueden ser almacenados en la matriz 00055 size_type capacity() const { return m_capacity; } 00056 public: 00057 Sparse_Matrix& operator= (const Sparse_Matrix &o) { return copy(o); } ///< Sinónimo de \c this->copy(o) 00058 Sparse_Matrix& copy( const Sparse_Matrix &o ); 00059 Sparse_Matrix& move( Sparse_Matrix &o ); 00060 Sparse_Matrix& swap( Sparse_Matrix &o ); 00061 public: 00062 /// ¿¿¿ (p == q) ??? 00063 friend bool operator == (const Sparse_Matrix &p, const Sparse_Matrix &q) { return p.equals(q); } 00064 /// ¿¿¿ (p != q) ??? 00065 friend bool operator != (const Sparse_Matrix &p, const Sparse_Matrix &q) { return !(p==q); } 00066 bool equals( const Sparse_Matrix & o ) const; ///< ¿¿¿ (*this==o) ??? 00067 /// Retorna \c true si \c "o" comparte sus valores con \c "*this" 00068 bool same( const Sparse_Matrix & o ) const { return this == &o; } 00069 private: 00070 void add( const Sparse_Matrix & ); 00071 void substract( const Sparse_Matrix & ); 00072 void multiply( const Sparse_Matrix &, const Sparse_Matrix & ); 00073 public: 00074 friend Sparse_Matrix operator + (const Sparse_Matrix& A, const Sparse_Matrix& B) 00075 { Sparse_Matrix Res = A; Res.add(B); return Res; } ///< Retorna \c A+B 00076 friend Sparse_Matrix operator - (const Sparse_Matrix& A, const Sparse_Matrix& B) 00077 { Sparse_Matrix Res = A; Res.substract(B); return Res; } ///< Retorna \c A-B 00078 friend Sparse_Matrix operator * (const Sparse_Matrix& A, const Sparse_Matrix& B) 00079 { Sparse_Matrix Res; Res.multiply(A, B); return Res; } ///< Retorna \c A*B 00080 public: 00081 reference operator () (unsigned, unsigned); 00082 const_reference operator () (unsigned, unsigned) const; 00083 public: 00084 void reSize( unsigned, unsigned); 00085 void reShape(unsigned, unsigned); 00086 public: 00087 void setDefault(const E& same); 00088 /// Valor almacenado en la mayor parte de la \c Sparse_Matrix 00089 const E& getDefault() { return m_same; } 00090 /// Ajusta la matriz para que pueda almacenar \c n valores diferentes a \c getDefault(). 00091 void reserve(size_type _Count); 00092 void reSize(unsigned newsize); 00093 void clear(); ///< Borra todos los valores de la \c Sparse_Matrix 00094 00095 template<class T> friend bool check_ok( const Sparse_Matrix<T>& M ); 00096 template<class T> friend class test_Sparse_Matrix; ///< Datos de prueba para la clase 00097 private: 00098 unsigned* m_I; ///< Indice "i" de \c M(i,j) <code>0 <= i < m_capacity</code> 00099 unsigned* m_J; ///< Indice "j" de \c M(i,j) <code>0 <= i < m_capacity</code> 00100 E * m_val; ///< Valor para \c M(i,j) <code>0 <= i < m_capacity</code> 00101 unsigned m_size; ///< Cantidad de valores insertados en los 3 vectores paralelos 00102 unsigned m_capacity; ///< Tamaño de los 3 vectores paralelos 00103 unsigned m_rows; ///< Cantidad de filas de la matriz 00104 unsigned m_cols; ///< Cantidad de columnas de la matris 00105 E m_same; ///< Valor almacenado en la mayor parte de la \c Sparse_Matrix 00106 }; // Sparse_Matrix 00107 00108 /** Verifica la invariante de la clase. 00109 - El campo \c m_same indica cuál es el valor que se repite más en toda la matriz. 00110 - Usualmente \c same es el neutro aditivo \c value_type(). 00111 - No existe un constructor explícito para darle a \c m_same su valor inicial, que 00112 es siempre inicializado en \c value_type(). Para cambiarlo es necesario invocar 00113 el método \c setgetDefault(). 00114 - Los vectores \c m_I[], \c m_J[] y \c m_val[] son vectores paralelos, todos de 00115 longitud \c Sparse_Matrix::m_capacity. 00116 - La cantidad máxima de valores diferente que pueden ser almacenados en la matriz 00117 es \c Sparse_Matrix::m_capacity. 00118 - El largo de estos vectores aumenta cuando se hace referencia a un valor M(i,j) 00119 mediante la versión que no es \c const del \c operator()(i,j). O sea, que es 00120 ese método el encargado de agregar valores en \c m_val[], pues el operador 00121 homónimo \c const operator()(i,j) nunca agrega nada y, como es \c const, en 00122 general retorna una referencia constante a \c m_same. 00123 - Es posible que la matriz tenga dimensiones nulas, lo que implica que todos los 00124 punteros a los vectors paralelos deben ser nulos. Este hecho se marca dándolo 00125 el valor \c 0 (cero) al campo \c m_capacity. 00126 - En todos los algoritmos, "m" o "m_rows" es la cantidad de filas == \c rows() 00127 - En todos los algoritmos, "n" o "m_cols" es la cantidad de columnas == \c cols() 00128 00129 \par <em>Rep</em> Modelo de la clase 00130 \code 00131 ____________________________________ 00132 / m_capacity \ 00133 +---+---+---+---+---+---+-..-+---+---+ 0 1 2 3 4 5 6 00134 m_I--->| 1 | 3 | 3 |'-'| ... ... |'-'| 0 / - - - - - - - \ 00135 +---+---+---+---+ ... ... +---+ 1 | - a - - - - - | 00136 m_J--->| 1 | 2 | 1 |'-'| ... ... |'-'| 2 | - - - - - - - | 00137 +---+---+---+---+ ... ... +---+ 3 | - c b - - - - | 00138 m_val-->|'a'|'b'|'c'|'-'| ... ... |'-'| 4 \ - - - - - - - / 00139 +---+---+---+---+---+---+-..-+---+---+ 00140 0 1 2 | 00141 m_size--------+ == 3 m_same == '-' m_rows == 5 m_cols == 7 00142 \endcode 00143 - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep 00144 \remark 00145 Libera al programador de implementar el método \c Ok() 00146 - http://www.di-mare.com/adolfo/binder/c04.htm#sc11 00147 */ 00148 template<class T> 00149 bool check_ok( const Sparse_Matrix<T>& M ) { 00150 if ( M.m_capacity == 0 ) { // m_capacity es el "marcador" que determina el valor de los demás 00151 if ( M.m_I != 0 ) { 00152 return false; /// - Invariante: <code>(m_capacity == 0) <==> (m_I == 0)</code> 00153 } 00154 if ( M.m_J != 0 ) { 00155 return false; /// - Invariante: <code>(m_capacity == 0) <==> (m_J == 0)</code> 00156 } 00157 if ( M.m_val != 0 ) { 00158 return false; /// - Invariante: <code>(m_capacity == 0) <==> (m_val == 0)</code> 00159 } 00160 } 00161 else { 00162 if ( M.m_rows == 0 ) { 00163 return false; /// - Invariante: <code>(m_rows == 0) ==> (m_capacity == 0)</code> 00164 } 00165 if ( M.m_cols == 0 ) { 00166 return false; /// - Invariante: <code>(m_cols == 0) ==> (m_capacity == 0)</code> 00167 } 00168 if ( M.m_I == 0 ) { 00169 return false; /// - Invariante: <code>(m_capacity != 0) <==> (m_I != 0)</code> 00170 } 00171 if ( M.m_J == 0 ) { 00172 return false; /// - Invariante: <code>(m_capacity != 0) <==> (m_J != 0)</code> 00173 } 00174 if ( M.m_val == 0 ) { 00175 return false; /// - Invariante: <code>(m_capacity != 0) <==> (m_val != 0)</code> 00176 } 00177 } 00178 00179 if (M.m_rows == 0 || M.m_cols == 0) { 00180 if (M.m_rows == 0 && M.m_cols == 0) { 00181 // OK ==> los 2 son nulos 00182 } 00183 else { 00184 return false; /// - Invariante: <code>(m_rows == 0) <==> (m_cols == 0)</code> 00185 } 00186 } 00187 00188 if ( M.m_size > M.m_capacity ) { 00189 return false; /// - Invariante: <code>( m_size <= m_capacity )</code> 00190 } 00191 if ( ! check_ok (M.m_same) ) { 00192 return false; /// - Invariante: <code>check_ok (m_same)</code> 00193 } 00194 for (unsigned k=0; k<M.m_size; ++k) { 00195 if ( ! ( (0<=M.m_I[k]) && (M.m_I[k]<M.m_rows) ) ) { 00196 return false; /// - Invariante: <code>( (0<=m_I[k]) && (m_I[k] < m_rows) ) k = [0..m_size-1]</code> 00197 } 00198 if ( ! ( (0<=M.m_J[k]) && (M.m_J[k]<M.m_cols) ) ) { 00199 return false; /// - Invariante: <code>( (0<=m_J[k]) && (m_J[k] < m_cols) ) k = [0..m_size-1]</code> 00200 } 00201 if ( ! check_ok( M.m_val[k] ) ) { 00202 return false; /// - Invariante: <code>check_ok( m_val[k] )</code> 00203 } 00204 } 00205 return true; 00206 } 00207 00208 /// Define el escalar que por defecto está en todas las entradas de la \c Sparse_Matrix. 00209 /// - Si <code>same != getDefault()</code> la matriz queda vacía. 00210 /// - De lo contrario, nada ocurre. 00211 template<class E> 00212 inline void Sparse_Matrix<E>::setDefault(const E& same) { 00213 if ( m_same != same ) { 00214 m_same = same; 00215 m_size = 0; 00216 } 00217 } 00218 /** \fn template <class E>inline Sparse_Matrix<E>::Sparse_Matrix(const value_type V); 00219 00220 \brief Constructor a partir de \c Sparse_Matrix<E>::value_type(V). 00221 00222 - La matriz resultante es una matriz escalar, de dimensiones 1x1, 00223 y su valor es \c "V" 00224 */ 00225 00226 /** Constructor de vector. 00227 - Obtiene suficiente memoria dinámica para almacenas los 00228 <code> n * m </code> valores de la matriz. 00229 - Si \c "value_type" tiene un constructor de vector, lo 00230 usar para inicializar cada uno de los valores de la matriz; 00231 de lo contrario, los deja tal cual están en la memoria. 00232 - Si \c "value_type" es uno de los tipos escalares básicos, 00233 como lo son \c int o \c float, los valores almacenados 00234 en la matriz quedan tal cual están y no son inicializados. 00235 \pre 00236 - <code> m * n > 0 </code> 00237 - <code> (m > 0) && (n > 0) </code> 00238 */ 00239 template <class E> 00240 inline Sparse_Matrix<E>::Sparse_Matrix(unsigned m, unsigned n) : m_same() { 00241 if (m == 0 || n == 0) { 00242 m_rows = m_cols = 0; 00243 m_val = 0; 00244 m_capacity = 0; 00245 } else { 00246 m_rows = m; 00247 m_cols = n; 00248 00249 m_capacity = 16; // pues 16 == 2*2*2*2 ... 00250 m_I = new size_type [m_capacity]; 00251 m_J = new size_type [m_capacity]; 00252 m_val = new value_type[m_capacity]; 00253 } 00254 00255 m_size = 0; 00256 // m_same = E(); // Usa el número "cero" como neutro tipo "E" 00257 } 00258 00259 template <class E> 00260 inline Sparse_Matrix<E>::Sparse_Matrix(const Sparse_Matrix<E>& o) { 00261 m_capacity = 0; // m_capacity==0 indica que NO se está usando memoria dinámica 00262 copy( o ); // assert( this != &o ); 00263 return; // ... pues ya "o" existe y se está usando para incializar a "*this" 00264 /* NOTA 00265 Cuando un objeto usa memoria dinámica, copiarlo es un proceso diferente a 00266 inicializarlo a partir de otro valor de su misma clase. En otras palabras, 00267 el trabajo que hace el constructor CLS::CLS( const CLS& ) es muy diferente 00268 al que hace el copiador CLS& operator=( const CLS & ). 00269 00270 El truco de programación usado en en esta implementación consiste en "marcar" 00271 el valor "m_capacity" para saber si se ha obtenido o no memoria dinámica 00272 para los vectores paralelos. La implementación de copy() está hecha de manera 00273 que si "m_capacity" es un puntero nulo, en copy() se inicializan correctamente 00274 todos los del Rep de Sparse_Matrix. Eso explica por qué la implementación de 00275 copy() es un tanto más elaborada que lo que a primera vista pareciera que es 00276 necesario. 00277 */ 00278 } 00279 00280 /// Destructor 00281 template <class E> 00282 inline Sparse_Matrix<E>::~Sparse_Matrix() { 00283 if (m_capacity != 0) { // sólo devuelve la memoria dinámica que sí ha adquirido 00284 delete [] m_I; 00285 delete [] m_J; // Retorna la memoria dinámica en uso 00286 delete [] m_val; 00287 } 00288 } 00289 00290 template <class E> 00291 bool Sparse_Matrix<E>::equals( const Sparse_Matrix & o ) const { 00292 if ( this == & o ) { 00293 return true; 00294 } 00295 if (rows() != o.rows() || cols() != o.cols()) { 00296 return false; 00297 } 00298 00299 for (unsigned i=0; i<rows(); i++) { 00300 for (unsigned j=0; j<cols(); j++) { 00301 if ( (*this)(i,j) != o(i,j) ) { 00302 return false; 00303 } 00304 } 00305 } 00306 return true; 00307 } 00308 00309 /** Copia desde \c "o". 00310 - Copia todo el valor de \c "o" sobre \c "*this", de forma que el nuevo valor de 00311 \c "*this" sea un duplicado exacto del valor de \c "o". 00312 - El valor anterior de \c "*this" se pierde. 00313 - El objeto \c "o" mantiene su valor anterior. 00314 - Luego de la copia, cuando el valor de \c "o" cambia, el de \c "*this" no cambiará, 00315 y viceversa, pues la copia es una copia profunda; no es superficial. 00316 - Si \c "*this" es \c "o" entonces su valor no cambia. 00317 - Después de esta operación siempre ocurre que <code> *this == o </code>. 00318 00319 \par Complejidad: 00320 O( <code> rows() * cols() </code> ) 00321 00322 \returns *this 00323 \see http://www.di-mare.com/adolfo/binder/c04.htm#sc05 00324 */ 00325 template <class E> 00326 Sparse_Matrix<E>& Sparse_Matrix<E>::copy( const Sparse_Matrix<E> &o ) { 00327 if (this == &o) { // evita auto-borrado 00328 return *this; 00329 } 00330 if (o.m_capacity == 0) { 00331 if (m_capacity != 0) { // Como copy() puede ser invocado cuando el Rep 00332 delete [] m_val; // NO ha sido inicializado, en esta implementación 00333 delete [] m_J; // hay que tener cuidado de no usar los otros campos 00334 delete [] m_I; // del Rep antes de darles valor. 00335 } // - "m_capacity" SI debe tener su valor correcto. 00336 m_rows = m_cols = 0; 00337 m_val = 0; 00338 m_same = o.m_same; 00339 m_size = 0; // assert( o.m_size == 0 ); 00340 m_capacity = 0; // assert( o.m_capacity == 0 ); 00341 return *this; 00342 } 00343 00344 // se asegura de que las dos matrices sean del mismo tamaño 00345 if (m_capacity != o.m_capacity) { // truco para evitar borrar la memoria dinámica 00346 if (m_capacity != 0) { // sólo devuelve la memoria dinámica que sí ha adquirido 00347 delete [] m_val; 00348 delete [] m_J; 00349 delete [] m_I; 00350 } 00351 00352 m_capacity = o.m_capacity; 00353 m_I = new size_type [m_capacity]; 00354 m_J = new size_type [m_capacity]; 00355 m_val = new value_type[m_capacity]; 00356 } 00357 m_same = o.m_same; 00358 m_size = o.m_size; 00359 00360 // como las matrices son del mismo tamaño, 00361 // basta copiarlas entrada por entrada. 00362 m_rows = o.m_rows; 00363 m_cols = o.m_cols; 00364 for (unsigned k=0; k<m_size; ++k) { 00365 m_I[k] = o.m_I[k]; 00366 m_J[k] = o.m_J[k]; 00367 m_val[k] = o.m_val[k]; 00368 } 00369 return *this; 00370 } 00371 00372 /** Traslada el valor de \c "o" a \c "*this". 00373 - El valor anterior de \c "*this" se pierde. 00374 - El nuevo valor de \c "*this" es el que \c "o" tuvo. 00375 - \c "o" queda en el estado en que lo dejaría \c Erase(). 00376 - Si \c "*this" es \c "o" entonces su valor no cambia. 00377 - En general, después de esta operación casi 00378 nunca ocurre que <code> (*this == o) </code> 00379 00380 \par Complejidad: 00381 O( <code> rows() * cols() </code> ) 00382 00383 \returns \c *this 00384 00385 \see http://www.di-mare.com/adolfo/binder/c04.htm#sc07 00386 */ 00387 template <class E> 00388 Sparse_Matrix<E>& Sparse_Matrix<E>::move( Sparse_Matrix<E> &o ) { 00389 if (this == &o) { // evita auto-borrado 00390 return *this; 00391 } else if (o.m_val == 0) { 00392 if (m_val != 0) { 00393 delete [] m_val; 00394 } 00395 m_rows = m_cols = 0; 00396 m_val = 0; 00397 return *this; 00398 } else if ( m_val != 0 ) { 00399 delete [] m_val; 00400 } 00401 m_val = o.m_val; 00402 m_rows = o.m_rows; 00403 m_cols = o.m_cols; 00404 00405 o.m_val = 0; 00406 o.m_rows = o.m_cols = 0; 00407 return *this; 00408 } 00409 00410 /** Intercambia los valores de \c "*this" y \c "o". 00411 - Debido a que algunos métodos retornan copias del valor de \c "*this" en 00412 lugar de una referencia, como ocurre con \c Sparse_Matrix::Child(), a veces 00413 \c swap() no tiene el resultado esperado por el programador. 00414 - Por ejemplo, si se invoca <code> T.Child(i). swap( T.Child(j) ) </code> 00415 el resultado no es intercambiar los hijos, sino más bien intercambiar 00416 los valores de los sub-árboles temporales \c T.Child(i) y \c T.Child(j). 00417 La forma correcta de intercambiar hijos es usar \c Graft(). 00418 00419 \par Complejidad: 00420 O( \c 1 ) 00421 00422 \returns *this 00423 00424 \see http://www.di-mare.com/adolfo/binder/c04.htm#sc08 00425 */ 00426 template <class E> 00427 inline Sparse_Matrix<E>& Sparse_Matrix<E>::swap( Sparse_Matrix<E> &o ) { 00428 std::swap( this->m_I , o.m_I ); 00429 std::swap( this->m_J , o.m_J ); 00430 std::swap( this->m_val , o.m_val ); 00431 std::swap( this->m_size , o.m_size ); 00432 std::swap( this->m_capacity , o.m_capacity ); 00433 std::swap( this->m_rows , o.m_rows ); 00434 std::swap( this->m_cols , o.m_cols ); 00435 std::swap( this->m_same , o.m_same ); 00436 return *this; 00437 } 00438 00439 /** Le cambia las dimensiones a la matriz. 00440 - En algunos casos los valores almacenados en la matriz no quedan 00441 todos iguales a \c Sparse_Matrix<E>::value_type(). 00442 \pre <code> (m > 0) && (n > 0) </code> 00443 */ 00444 template <class E> 00445 void Sparse_Matrix<E>::reSize(unsigned m, unsigned n) { 00446 // NO hace falta cambiar m_capacity porque lo único que está cambiando 00447 // el la dimensión de la matriz 00448 if (m != m_rows || n != m_cols) { 00449 m_size = 0; // desecha todos los valores anteriores 00450 m_rows = m; // cambia las dimensiones de la matriz 00451 m_cols = n; 00452 } 00453 00454 if (m==0 || n==0) { 00455 m_rows = 0; 00456 m_cols = 0; 00457 m_size = 0; 00458 if (m_capacity != 0) { 00459 m_capacity = 0; // todo esto mantiene la invariante de la clase 00460 delete [] m_I; m_I = 0; 00461 delete [] m_J; m_J = 0; 00462 delete [] m_val; m_val = 0; 00463 } 00464 } 00465 return; 00466 00467 #if 0 00468 /* NOTA 00469 Esta es la antigua especificación de reSize(). Es incorrecta 00470 porque presume que el Rep de la matriz es un vector denso en 00471 donde están almacenados todos los valores de la matriz: 00472 00473 +----------------------------------------------------------+ 00474 | reSize(): Le cambia las dimensiones a la matriz. | 00475 | ======== | 00476 | - Si ocurre que (m*n) == rows() * cols() los valores de | 00477 | la matriz se mantienen, aunque cambian sus dimensiones.| 00478 | - Si (m*n) != rows() * cols() los valores de la matriz | 00479 | quedarán inicializados de la misma forma en que los | 00480 | inicializaría CERO == Sparse_Matrix<E>::value_type(). | 00481 | | 00482 | \pre (m > 0) && (n > 0) | 00483 +----------------------------------------------------------+ 00484 00485 En la actual especificación, que ya está corregida, no queda 00486 implícita la presunción sobre cómo está organizada internamente 00487 la matriz. Por eso, esta nueva especificación sí sirve para una 00488 matriz implementada con un vector denso de valores, o para la 00489 matriz implementada como una matriz rala. 00490 00491 Estos pequeños detalles en algunas ocasiones surgen cuando el 00492 programador de una clase introduce mejoras o modificaciones, pues 00493 muchas veces es muy difícil o prácticamente imposible predecir 00494 todos los pormenores y detalles de una especificación o de una 00495 implementación. 00496 */ 00497 00498 /* NOTA 00499 Esta es la imnplementación anterior, que debe reacomodar los ínices 00500 (i,j) de aquellos valores almacenados en los vectores paralalos para 00501 simular que la matriz rala en realidad está almacenada como una 00502 matriz densa, dentro de un vector. 00503 - Al modificar la especificación de para reSize() ya no hace falta 00504 hacer todo este trabajo. 00505 00506 NO hace falta cambiar m_capacity porque lo único que está cambiando 00507 el la dimensión de la matriz 00508 */ 00509 00510 /* Nota para esta implementación: 00511 Este método funciona de una forma similar a reSize() para la matriz implementada con 00512 un vector. Para lograr que las clases Matrix<E> y Sparse_Matrix<E> tengan la misma 00513 funcionalidad, esta implementación reacomoda los índices de la matriz de manera que 00514 las m_rows*m_cols entradas queden reacomodadas como si la implementación usara un 00515 vector de dimension m*n. Sin embargo, lo lógico sería eliminar todos los valores 00516 de la matriz trocando el ciclo "for(;k;){}" por un eficiente "m_size=0;" 00517 - Esto muestra que una especificación que aparenta ser "amplia", como lo es la de 00518 Matrix<E>::reSize(n,m), en realidad se queda "corta" si se toma en cuenta que la 00519 misma especificación debe servir para varias implementaciones diferentes. 00520 - Además, muestra que algunas operaciones sólo tienen sentido para una implementación 00521 específica, como ocurre con las operaciones Sparse_Matrix<E>>::getDefault() y su 00522 "seteador" Sparse_Matrix<E>>::setgetDefault(). 00523 */ 00524 if (m * n == m_rows * m_cols) { 00525 m_size = 0; // desecha todos los valores anteriores 00526 } 00527 else { 00528 unsigned row = 0, col = 0; 00529 for (unsigned k=0; k<m_size; ++k) { 00530 // M(i,j) <==> m_val[ (i * m_cols) + j ] ; // si almacena los valores por filas 00531 // M(i,j) <==> m_val[ i + (j * m_rows) ] ; // si almacena los valores por columnas 00532 00533 unsigned lineal = (m_I[k] * m_cols) + m_J[k]; // por filas 00534 // unsigned lineal = m_I[k] + (m_j[k] * m_rows); // por columnas 00535 m_I[k] = lineal / n; // lineal % m; 00536 m_J[k] = lineal % n; // lineal / m; 00537 } 00538 } 00539 m_rows = m; // cambia las dimensiones de la matriz 00540 m_cols = n; 00541 00542 #endif 00543 } 00544 00545 /** Le ajusta las dimensiones a la matriz. 00546 - Si ocurre que <code> (m*n) == rows()*cols() </code> hace 00547 lo mismo que haría \c reSize(m,n). 00548 - En caso contrario, no hace nada. 00549 */ 00550 template <class E> 00551 inline void Sparse_Matrix<E>::reShape(unsigned m, unsigned n) { 00552 if ( m * n == m_rows * m_cols ) { 00553 reSize(n,m); 00554 } 00555 } 00556 00557 /// Retorna una referencia al elemento [i,j] de la matriz ( \c const ). 00558 /// - \c M(i,j) significa lo que en arreglos se denota con \c M[i][j]. 00559 /// - <code>val = M(i,j); // M(i,j) es un "rvalue" (const)</code> 00560 template <class E> 00561 inline const E& Sparse_Matrix<E>::operator () (unsigned i, unsigned j) const { 00562 assert( "Sparse_Matrix<E>::operator()()" && (i < rows()) ); 00563 assert( "Sparse_Matrix<E>::operator()()" && (j < cols()) ); 00564 00565 for (unsigned k=0; k<m_size; k++) { 00566 if ( m_I[k] == i ) { 00567 if ( m_J[k] == j ) { 00568 return m_val[k]; 00569 } 00570 } 00571 } 00572 return m_same; 00573 /* NOTA 00574 Como este método es "const", de antemano se sabe que el programador no puede 00575 usarlo para modificar el valor de la matriz. Por eso, aunque el valor 00576 retornado sea una referencia a valor común por defecto m_same, de antemano 00577 el compilador asegura que ese valor no será modificado. 00578 00579 Sin embargo, cuando el programador usa el método homónimo operator()(i,j) 00580 que no es "const", es posible que el valor retornado sí sea modificado. 00581 En ese caso, ya no es correcto retornar una referencia al valor común "m_same". 00582 - Por eso, cuando se usa el hace referencia en el otro operator()(i,j) es 00583 necesario agregar una entrada en los vectores paralelos en aquellos casos 00584 en que no existe un valor diferente a "m_same" para (i,j). 00585 - Esto quiere decir que sí es posible que al utilizar la versión modificable 00586 de operator()(i,j) quede en el vector "m_val[]" un valor que es igual a 00587 "m_same". En el caso peor, podría ocurrir que todos los valores almacenados 00588 en el vector "m_val[]" sean todos iguales a "m_same". 00589 - Una forma de corregir esta anomalía es "revisar después" si existe un valor 00590 en el vector "m_val[]" que es igual a "m_same". Una forma eficiente de 00591 hacerlo es mantener el el Rep un puntero "m_last_change" que apunte al 00592 último valor "m_val[]" que la versión modificable de operator()(i,j) retornó. 00593 En la siguiente invocación a operator()(i,j), se puede verificar si hubo un 00594 ese valor anterior fue cambiado de manera que tiene almacenado "m_same". 00595 */ 00596 #if 0 00597 // Este código podría ser agregado al principio de operator()(i,j) 00598 if (m_last_change != 0) { 00599 if ( *m_last_change == m_same ) { // el último uso de op()(i,j) dejó "m_same" 00600 if ( m_size == 1 ) { // Elimina los vectores viejos 00601 delete [] m_I; m_I = 0; 00602 delete [] m_J; m_J = 0; 00603 delete [] m_val; m_val = 0; 00604 m_capacity = 0; 00605 m_size = 0; 00606 } 00607 else { // intercambia este valor con el último 00608 m_size--; 00609 unsigned k = (m_last_change - m_val); // índice de "* m_last_change" 00610 *m_last_change = m_val[m_size]; 00611 m_I[k] = m_I[m_size]; 00612 m_J[k] = m_J[m_size]; 00613 } 00614 } 00615 m_last_change = 0; 00616 } 00617 #endif 00618 } 00619 /// Retorna una referencia al elemento [i,j] de la matriz. 00620 /// - \c M(i,j) significa lo que en arreglos se denota con \c M[i][j]. 00621 /// - <code>M(i,j) = val; // M(i,j) es un "lvalue" (modificable)</code> 00622 template <class E> 00623 inline E& Sparse_Matrix<E>::operator() (unsigned i, unsigned j) { 00624 assert( "Sparse_Matrix<E>::operator()()" && (i < rows()) ); 00625 assert( "Sparse_Matrix<E>::operator()()" && (j < cols()) ); 00626 00627 // Busca al elemento (i,j) en los vectores paralelos 00628 for (unsigned k=0; k<m_size; k++) { 00629 if ( m_I[k] == i ) { 00630 if ( m_J[k] == j ) { 00631 // m_last_change = & m_val[k]; 00632 return m_val[k]; 00633 } 00634 } 00635 } 00636 00637 assert( (m_size <= m_capacity) && " => Agotado m_val[] Sparse_Matrix<E>::operator()()" ); 00638 if (m_size == m_capacity) { 00639 unsigned new_capacity = m_capacity; 00640 if (m_capacity % 16 == 0) { 00641 new_capacity += 16; 00642 } 00643 else { 00644 new_capacity /= 16; // Hace que el nuevo tamaño sea divisible por 16 00645 new_capacity *= 16; 00646 new_capacity += 16; 00647 } 00648 reSize( new_capacity ); // Agrega 16 porque 16 == 2*2*2*2 ... 00649 } 00650 // Agrega un nuevo valor modificable en los vectores paralelos 00651 m_I[m_size] = i; 00652 m_J[m_size] = j; 00653 m_val[m_size] = m_same; 00654 // m_last_change = & m_val[m_size]; 00655 return m_val[m_size++]; 00656 } 00657 00658 /// Le cambia el tamaño máximo posible a la matriz. 00659 /// - Le aumenta la cantidad de valores diferentes a \c getDefault(). 00660 /// - No hace nada cuando <code>size() < newsize</code>. 00661 template <class E> 00662 void Sparse_Matrix<E>::reSize(unsigned newsize) { 00663 if ( newsize < m_size ) { // hay más valores en uso que "newsize" 00664 // assert((m_size < newsize) && "=>Sparse_Matrix<E>::reSize()" ); 00665 return; 00666 } 00667 unsigned * new_I = new unsigned [ newsize ]; // Obtiene los vectores nuevos 00668 unsigned * new_J = new unsigned [ newsize ]; 00669 E * new_val = new E [ newsize ]; 00670 if (m_capacity > 0) { // Copia los valores en uso 00671 for (unsigned k=0; k<m_size; ++k) { 00672 new_I[k] = m_I[k]; 00673 new_J[k] = m_J[k]; 00674 new_val[k] = m_val[k]; 00675 } 00676 delete [] m_I; // Elimina los vectores viejos 00677 delete [] m_J; 00678 delete [] m_val; 00679 } 00680 m_I = new_I; // Instala los vectores nuevos 00681 m_J = new_J; 00682 m_val = new_val; 00683 m_capacity = newsize; 00684 } 00685 00686 /** Le suma a \c "*this" la matriz \c "O". 00687 \pre 00688 - \c "*this" y \c "O" deben tener las mismas dimensiones 00689 - <code> rows() == O.rows() && cols() == O.cols() </code> 00690 00691 \par Complejidad: 00692 O( <code> rows() * cols() </code> ) 00693 00694 \remarks 00695 - Esta es la implementación de Sparse_Matrix<E> operator+( Sparse_Matrix<E>&, Sparse_Matrix<E> ) 00696 - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>) 00697 definida con plantillas si esa función amiga no está definida (implementada) 00698 dentro de la declaración de la clase. Para solventar esta deficiencia existe 00699 este método que realiza el trabajo, aunque es poco cómodo de usar. 00700 */ 00701 template <class E> 00702 void Sparse_Matrix<E>::add( const Sparse_Matrix<E> & O ) { 00703 // verifica que las dos matrices sean del mismo tamaño 00704 assert( "Sparse_Matrix<E>::add()" && (rows() == O.rows()) && (cols() == O.cols()) ); 00705 00706 // crea una nueva matriz que va a ser la que se devuelve 00707 reSize(O.m_rows, O.m_cols); 00708 00709 // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada. 00710 00711 value_type *pThis = m_val; 00712 value_type *pO = & O.m_val[0]; 00713 value_type *pEND = &m_val[m_cols * m_rows]; 00714 for ( ; pThis != pEND; ++pThis, ++pO) { 00715 *pThis += *pO; 00716 } 00717 return; 00718 } 00719 00720 /** Le resta a \c "*this" la matriz \c "O". 00721 \pre 00722 - \c "*this" y \c "O" deben tener las mismas dimensiones 00723 - <code> rows() == O.rows() && cols() == O.cols() </code> 00724 00725 \par Complejidad: 00726 O( <code> rows() * cols() </code> ) 00727 00728 \remarks 00729 - Esta es la implementación de Sparse_Matrix<E> operator-( Sparse_Matrix<E>&, Sparse_Matrix<E> ) 00730 - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>) 00731 definida con plantillas si esa función amiga no está definida (implementada) 00732 dentro de la declaración de la clase. Para solventar esta deficiencia existe 00733 este método que realiza el trabajo, aunque es poco cómodo de usar. 00734 */ 00735 template <class E> 00736 void Sparse_Matrix<E>::substract( const Sparse_Matrix<E> & O ) { 00737 // verifica que las dos matrices sean del mismo tamaño 00738 assert( "Sparse_Matrix<E>::substract()" && (rows() == O.rows()) && (cols() == O.cols()) ); 00739 00740 // crea una nueva matriz que va a ser la que se devuelve 00741 reSize(O.m_rows, O.m_cols); 00742 00743 // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada. 00744 00745 value_type *pThis = m_val; 00746 value_type *pO = & O.m_val[0]; 00747 value_type *pEND = &m_val[m_cols * m_rows]; 00748 for ( ; pThis != pEND; ++pThis, ++pO) { 00749 *pThis -= *pO; 00750 } 00751 return; 00752 } 00753 00754 /** Calcula la multiplicación <code> A * B </code> y la almacena en \c "*this". 00755 - Las dimensiones de \c "*this" se ajustan de manera que: 00756 - <code> rows() == A.rows() && cols() == B.cols()</code> 00757 00758 \pre 00759 - \c "A" y \c "B" deben tener dimensiones compatibles 00760 - <code> A.cols() == B.rows() </code> 00761 - La multiplicación se hace [Fila x Columna], lo que implica que la cantidad 00762 de valores en la filas de \c "A" debe ser igual a la cantidad de columnas de \c "B" 00763 00764 \par Complejidad: 00765 O( <code> A.cols() * B.cols() * A.cols() * A.capacity() * B.capacity() </code> ) 00766 00767 \remarks 00768 - Esta es la implementación de Sparse_Matrix<E> operator*( Sparse_Matrix<E>&, Sparse_Matrix<E> ) 00769 - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>) 00770 definida con plantillas si esa función amiga no está definida (implementada) 00771 dentro de la declaración de la clase. Para solventar esta deficiencia existe 00772 este método que realiza el trabajo, aunque es poco cómodo de usar. 00773 */ 00774 template <class E> 00775 void Sparse_Matrix<E>::multiply( const Sparse_Matrix<E> & A, const Sparse_Matrix<E> & B ) { 00776 // Verifica que las matrices se puedan multiplicar 00777 assert( (A.cols() == B.rows()) && " => Sparse_Matrix<E>::multiply()" ); 00778 00779 reSize( A.rows(), B.cols() ); 00780 value_type sum; 00781 for (unsigned i=0; i<rows(); i++) { 00782 for (unsigned j=0; j<cols(); j++) { 00783 sum = value_type(); // sum = E(0); 00784 for (unsigned k=0; k<A.cols(); k++) { 00785 sum = sum + A(i,k) * B(k,j); 00786 } 00787 // this->(i,j) = sum; // produce un error de compilación 00788 // this->operator()(i,j) = sum; // también funciona 00789 (*this)(i,j) = sum; // también funciona 00790 } 00791 } 00792 return; 00793 } 00794 00795 /// Graba en el flujo \c COUT el valor de \c M[][]. 00796 template <class E> 00797 std::ostream& operator<<(std::ostream& COUT, const Sparse_Matrix<E>& M) { 00798 COUT << '[' << M.rows() << 'x' << M.cols() << ']' << std::endl; 00799 for (unsigned i=0; i < M.rows(); ++i) { 00800 for (unsigned j=0; j < M.cols(); ++j) { 00801 COUT << " " << M(i,j); 00802 } 00803 COUT << std::endl; 00804 } 00805 return COUT; 00806 } 00807 00808 /// Obtiene del flujo \c CIN el valor para \c M[][]. 00809 template <class E> 00810 std::istream& operator>>(std::istream& CIN, Sparse_Matrix<E>& M) { 00811 assert( "This code has not been tested" ); 00812 unsigned rows,cols; 00813 CIN >> rows >> cols; 00814 M.reSize(rows,cols); 00815 for (unsigned i=0; i<rows; i++) { 00816 for (unsigned j=0; j<cols; j++) { 00817 CIN >> M(i,j); 00818 } 00819 } 00820 return CIN; 00821 } 00822 00823 } // namespace Mx 00824 00825 // Este [horrible] truco permite "compartir" el mismo archivo Matrix_Lib.h 00826 // entre Matrix.h y Sparse_Matrix.h 00827 // #define Matrix Sparse_Matrix 00828 // #include "Matrix_Lib.h" 00829 // #undef Matrix 00830 00831 #include "Matrix_Lib.h" 00832 00833 #endif // Sparse_Matrix_h 00834 // EOF: Sparse_Matrix.h
1.3.9.1