lkptr
|
00001 // Matrix.h Copyright (C) 2004 adolfo@di-mare.com 00002 00003 /** \file Matrix.h 00004 \brief Declaraciones y definiciones para la clase \c 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 Matrix_h 00013 #define Matrix_h 00014 00015 // Macro definida para evitar que \c assert() cancele la ejecución. 00016 // Tira \c std::out_of_range si \c assert(X) falla. 00017 #ifdef ASSERT_NO_ABORT 00018 #include <stdexcept> // std::out_of_range 00019 #include <string> 00020 #include <iostream> // cout 00021 00022 #ifndef NDEBUG 00023 #ifdef assert 00024 #undef assert 00025 #endif 00026 #define assert(X) { \ 00027 std::string line, msg = "Assertion failed: " #X ", file " __FILE__ ; \ 00028 unsigned long n = __LINE__; \ 00029 if ( n==0 ) { line = "0"; } \ 00030 else do { \ 00031 char ch = (n%10) + '0'; \ 00032 line = ch + line; \ 00033 n = n/10; \ 00034 } while ( n!=0 ); \ 00035 msg += ", line " + line + "\n"; \ 00036 if ( !(X) ) { throw std::out_of_range( msg ); } \ 00037 } 00038 #endif 00039 #else 00040 #include <cassert> // assert() 00041 #endif 00042 00043 /// Definido por la biblioteca C++ estándar 00044 namespace std {} // Está acá para que Doxygen lo documente 00045 00046 /// Matriz chirrisquitica de adolfo@di-mare.com 00047 namespace Mx { 00048 00049 /** Esta es una clase matriz muy chirrisquitica que puede cambiar dinámicamente de tamaño. 00050 - La matriz tiene tamaño \c rows() x \c cols() 00051 - Se le puede cambiar el tamaño dinámicamente con el método \c reSize(). 00052 - La clase \c E debe incluir un neutro para la adición, cuyo valor debe poderse 00053 obtener invocando el convertidor \c Sparse_Matrix<E>::value_type(). 00054 - Las operaciones aritméticas "+" "-" "*" deben estar definidas para 00055 \c Matrix<E>::value_type y debe existir el valor \c Matrix<E>::value_type() y también 00056 \c Matrix<E>::value_type(1) (para matrices unitarias) 00057 - Los valores de la matriz pueden estar almacenados por filas o por columnas, 00058 según quede implementado el método \c Matrix<E>::operator(unsigned, unsigned) 00059 00060 \see http://www.oonumerics.org/oon/ 00061 */ 00062 template <class E> 00063 class Matrix { 00064 public: 00065 /// Tipo del objeto almacenado, similar al nombre usado en STL 00066 typedef E value_type; 00067 /// Tipo del objeto almacenado, similar al nombre usado en STL 00068 typedef value_type& reference; 00069 /// Tipo del objeto almacenado, similar al nombre usado en STL 00070 typedef const value_type& const_reference; 00071 /// Tipo del tamaño de un objeto, similar al nombre usado en STL 00072 typedef unsigned size_type; 00073 public: 00074 Matrix(unsigned m = 1, unsigned n = 1); 00075 Matrix(const Matrix& o); ///< Constructor de copia 00076 /// Matriz escalar de valor \c V. 00077 Matrix(const value_type V) : m_rows(0), m_cols(0), m_val(0) { reSize(1,1); (*this)(0,0) = V; } 00078 ~Matrix(); 00079 public: 00080 unsigned rows() const { return m_rows; } ///< Cantidad de filas de la matriz 00081 unsigned cols() const { return m_cols; } ///< Cantidad de columnas de la Matriz 00082 unsigned size() const { return m_rows * m_cols; } ///< Cantidad de valores almacenados en la matriz 00083 unsigned count() const { return size(); } ///< Cantidad de valores almacenados en la matriz 00084 /// Cantidad máxima posible de valores diferentes que pueden ser almacenados en la matriz 00085 size_type capacity() const { return size(); } 00086 public: 00087 Matrix& operator= (const Matrix &o) { return copy(o); } ///< Sinónimo de \c this->copy(o) 00088 Matrix& copy( const Matrix &o ); 00089 Matrix& move( Matrix &o ); 00090 Matrix& swap( Matrix &o ); 00091 public: 00092 /// ¿¿¿ (p == q) ??? 00093 friend bool operator == (const Matrix &p, const Matrix &q) { return p.equals(q); } 00094 /// ¿¿¿ (p != q) ??? 00095 friend bool operator != (const Matrix &p, const Matrix &q) { return !(p==q); } 00096 bool equals( const Matrix & o ) const; ///< ¿¿¿ (*this==o) ??? 00097 /// Retorna \c true si \c "o" comparte sus valores con \c "*this" 00098 bool same( const Matrix & o ) const { return this == &o; } 00099 protected: // disponibles para clases derivadas 00100 void add( const Matrix & ); 00101 void substract( const Matrix & ); 00102 void multiply( const Matrix &, const Matrix & ); 00103 public: 00104 friend Matrix operator + (const Matrix& A, const Matrix& B) 00105 { Matrix Res = A; Res.add(B); return Res; } ///< Retorna \c A+B 00106 friend Matrix operator - (const Matrix& A, const Matrix& B) 00107 { Matrix Res = A; Res.substract(B); return Res; } ///< Retorna \c A-B 00108 friend Matrix operator * (const Matrix& A, const Matrix& B) 00109 { Matrix Res; Res.multiply(A, B); return Res; } ///< Retorna \c A*B 00110 public: 00111 reference operator () (unsigned, unsigned); 00112 const_reference operator () (unsigned, unsigned) const; 00113 reference at (unsigned m, unsigned n) 00114 { return operator() (m,n); } ///< Retorna \c operator()(m,n). 00115 const_reference at (unsigned m, unsigned n) const 00116 { return operator() (m,n); } ///< Retorna \c operator()(m,n) \c "const". 00117 public: 00118 void reSize( unsigned, unsigned); 00119 void reShape(unsigned, unsigned); 00120 void transpose() { 00121 unsigned tmp = m_rows; 00122 m_rows = m_cols; 00123 m_cols = tmp; 00124 } ///< Transpone la matriz. 00125 00126 template<class T> friend bool check_ok( const Matrix<T>& M ); 00127 template<class T> friend class test_Matrix; ///< Datos de prueba para la clase 00128 private: 00129 value_type * m_val; ///< Vector de valores de la matriz 00130 unsigned m_rows; ///< Cantidad de filas de la matriz 00131 unsigned m_cols; ///< Cantidad de columnas de la matris 00132 }; // Matrix 00133 00134 00135 /** Verifica la invariante de la clase. 00136 - Es posible que la matriz tenga dimensiones nulas, lo que implica que todos los 00137 punteros a los vectors paralelos deben ser nulos. Este hecho se marca dándolo 00138 el valor \c 0 (cero) al campo \c m_val. 00139 - Las matrices quedan almacenadas en un vector de tamaño [M x N]. 00140 - En todos los algoritmos, "m" o "m_rows" es la cantidad de filas == \c rows() 00141 - En todos los algoritmos, "n" o "m_cols" es la cantidad de columnas == \c cols() 00142 00143 \par <em>Rep</em> Modelo de la clase 00144 \code 00145 +---+ / \ 00146 | 2 | M(i,j) ==> m_val[ (i * m_cols) + j ] | 0 1 2 3 | m_rows == 2 00147 +---+ (almacenamiento por filas) | 4 5 6 7 | m_cols == 4 00148 | 4 | \ / 00149 +---+ +---+---+---+---+---+---+---+---+ 00150 | *-|-->| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 00151 +---+ +---+---+---+---+---+---+---+---+ 00152 \endcode 00153 00154 \par <em>Rep</em> Modelo de la clase 00155 \code 00156 +---+ 00157 | 4 | M(i,j) ==> m_val[ i + (j * m_rows) ] / a e \ 00158 +---+ (almacenamiento por columnas) | b f | m_rows == 4 00159 | 2 | | c g | m_cols == 2 00160 +---+ +---+---+---+---+---+---+---+---+ \ d h / 00161 | *-|-->| a | b | c | d | e | f | g | h | 00162 +---+ +---+---+---+---+---+---+---+---+ 00163 \endcode 00164 - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep 00165 \remark 00166 Libera al programador de implementar el método \c Ok() 00167 - http://www.di-mare.com/adolfo/binder/c04.htm#sc11 00168 */ 00169 template<class T> 00170 bool check_ok( const Matrix<T>& M ) { 00171 if ( M.m_rows == 0 || M.m_cols == 0 ) { // si alguno es cero... 00172 if ( M.m_rows != 0 || M.m_cols != 0 ) { // ... los 2 deben serlo 00173 return false; /// - Invariante: <code>(M.m_rows == 0) <==> (M.m_cols == 0)</code> 00174 } 00175 else { 00176 if (M.m_val != 0) { 00177 return false; /// - Invariante: <code>(M.m_rows == 0) <==> (M.m_val == 0)</code> 00178 } 00179 return true; // La implementación sí permite (m_val == 0) 00180 } 00181 } 00182 00183 unsigned MxN = M.m_rows * M.m_cols; 00184 for (unsigned k=0; k<MxN; ++k) { 00185 if ( ! check_ok( M.m_val[k] ) ) { 00186 return false; /// - Invariante: <code>check_ok( m_val[k] )</code> 00187 } 00188 } 00189 return true; 00190 } 00191 00192 00193 /** \fn template <class E>inline Matrix<E>::Matrix(const value_type V); 00194 \brief Constructor a partir de \c Matrix<E>::value_type(V). 00195 00196 - La matriz resultante es una matriz escalar, de dimensiones 1x1, 00197 y su valor es \c "V". 00198 */ 00199 00200 /** Constructor de vector. 00201 - Obtiene suficiente memoria dinámica para almacenas los 00202 <code> n * m </code> valores de la matriz 00203 - Si \c "value_type" tiene un constructor de vector, lo 00204 usar para inicializar cada uno de los valores de la matriz; 00205 de lo contrario, los deja tal cual están en la memoria 00206 - Si \c "value_type" es uno de los tipos escalares básicos, 00207 como lo son \c int o \c float, los valores almacenados 00208 en la matriz quedan tal cual están y no son inicializados. 00209 00210 \pre 00211 - <code> m * n > 0 </code> 00212 - <code> (m > 0) && (n > 0) </code> 00213 00214 */ 00215 template <class E> 00216 inline Matrix<E>::Matrix(unsigned m, unsigned n) { 00217 if (m == 0 || n == 0) { 00218 m_rows = m_cols = 0; 00219 m_val = 0; 00220 } else { 00221 m_rows = m; 00222 m_cols = n; 00223 m_val = new value_type [n*m]; 00224 } 00225 } 00226 00227 template <class E> 00228 Matrix<E>::Matrix(const Matrix<E>& o) { 00229 if (o.m_val == 0) { // OJO: una matriz "vacía" produce errores en 00230 m_rows = m_cols = 0; // Matrix<E>::operator(unsigned, unsigned) 00231 m_val = 0; 00232 return; 00233 } 00234 m_rows = o.m_rows; 00235 m_cols = o.m_cols; 00236 const unsigned MxN = o.m_rows * o.m_cols; 00237 m_val = new value_type [MxN]; 00238 00239 // como las matrices son del mismo tamaño, 00240 // basta copiarlas entrada por entrada. 00241 00242 value_type *pSelf = this->m_val; 00243 value_type *pO = o.m_val; 00244 value_type *pEND = &m_val[MxN]; 00245 for (; pSelf != pEND; ++pSelf, ++pO) { 00246 *pSelf = *pO; 00247 } 00248 } 00249 00250 /// Destructor 00251 template <class E> 00252 inline Matrix<E>::~Matrix() { 00253 if ( m_val != 0 ) { 00254 delete [] m_val; 00255 } 00256 } 00257 00258 template <class E> 00259 bool Matrix<E>::equals( const Matrix & o ) const { 00260 if (m_rows != o.m_rows || m_cols != o.m_cols) { 00261 return false; 00262 } else if ( m_val == 0 ) { 00263 return true; // las 2 matrices están vacías ==> son iguales 00264 } 00265 const unsigned MxN = o.m_rows * o.m_cols; 00266 value_type *pSelf = this->m_val; 00267 value_type *pO = o.m_val; 00268 value_type *pEND = &m_val[MxN]; 00269 for (; pSelf != pEND; ++pSelf, ++pO) { 00270 if (*pSelf != *pO) { 00271 return false; 00272 } 00273 } 00274 return true; 00275 } 00276 00277 /** Copia desde \c "o". 00278 - Copia todo el valor de \c "o" sobre \c "*this", de forma que el nuevo valor de 00279 \c "*this" sea un duplicado exacto del valor de \c "o" 00280 - El valor anterior de \c "*this" se pierde 00281 - El objeto \c "o" mantiene su valor anterior 00282 - Luego de la copia, cuando el valor de \c "o" cambia, el de \c "*this" no cambiará, 00283 y viceversa, pues la copia es una copia profunda; no es superficial 00284 - Si \c "*this" es \c "o" entonces su valor no cambia 00285 - Después de esta operación siempre ocurre que <code> *this == o </code> 00286 00287 \par Complejidad: 00288 O( <code> rows() * cols() </code> ) 00289 00290 \returns *this 00291 00292 \see http://www.di-mare.com/adolfo/binder/c04.htm#sc05 00293 */ 00294 template <class E> 00295 Matrix<E>& Matrix<E>::copy( const Matrix<E> &o ) { 00296 if (this == &o) { // evita auto-borrado 00297 return *this; 00298 } else if (o.m_val == 0) { 00299 if (m_val != 0) { 00300 delete [] m_val; 00301 m_rows = m_cols = 0; 00302 m_val = 0; 00303 } 00304 return *this; 00305 } 00306 // se asegura de que las dos matrices son del mismo tamaño 00307 const unsigned MxN = o.m_rows * o.m_cols; 00308 if (MxN != m_rows * m_cols) { // truco para evitar borrar la memoria dinámica 00309 delete [] m_val; 00310 m_val = new value_type [MxN]; 00311 } 00312 m_rows = o.m_rows; 00313 m_cols = o.m_cols; 00314 00315 00316 // como las matrices son del mismo tamaño, 00317 // basta copiarlas entrada por entrada. 00318 00319 value_type *pSelf = this->m_val; 00320 value_type *pO = o.m_val; 00321 value_type *pEND = &m_val[MxN]; 00322 for (; pSelf != pEND; ++pSelf, ++pO) { 00323 *pSelf = *pO; 00324 } 00325 return *this; 00326 } 00327 00328 /** Traslada el valor de \c "o" a \c "*this". 00329 - El valor anterior de \c "*this" se pierde 00330 - El nuevo valor de \c "*this" es el que \c "o" tuvo 00331 - \c "o" queda en el estado en que lo dejaría \c Erase() 00332 - Si \c "*this" es \c "o" entonces su valor no cambia 00333 - En general, después de esta operación casi 00334 nunca ocurre que <code> (*this == o) </code> 00335 00336 \par Complejidad: 00337 O( <code> rows() * cols() </code> ) 00338 00339 \returns \c *this 00340 00341 \see http://www.di-mare.com/adolfo/binder/c04.htm#sc07 00342 */ 00343 template <class E> 00344 Matrix<E>& Matrix<E>::move( Matrix<E> &o ) { 00345 if (this == &o) { // evita auto-borrado 00346 return *this; 00347 } else if (o.m_val == 0) { 00348 if (m_val != 0) { 00349 delete [] m_val; 00350 m_rows = m_cols = 0; 00351 m_val = 0; 00352 } 00353 return *this; 00354 } else if ( m_val != 0 ) { 00355 delete [] m_val; 00356 } 00357 m_val = o.m_val; // me robo los valores de "o" 00358 m_rows = o.m_rows; 00359 m_cols = o.m_cols; 00360 00361 o.m_rows = o.m_cols = 0; 00362 o.m_val = 0; 00363 return *this; 00364 } 00365 00366 /** Intercambia los valores de \c "*this" y \c "o". 00367 - Debido a que algunos métodos retornan copias del valor de \c "*this" en 00368 lugar de una referencia, como ocurre con \c Matrix::Child(), a veces 00369 \c swap() no tiene el resultado esperado por el programador. 00370 - Por ejemplo, si se invoca <code> T.Child(i). swap( T.Child(j) ) </code> 00371 el resultado no es intercambiar los hijos, sino más bien intercambiar 00372 los valores de los sub-árboles temporales \c T.Child(i) y \c T.Child(j). 00373 La forma correcta de intercambiar hijos es usar \c Graft(). 00374 00375 \par Complejidad: 00376 O( \c 1 ) 00377 00378 \returns *this 00379 00380 \see http://www.di-mare.com/adolfo/binder/c04.htm#sc08 00381 */ 00382 template <class E> 00383 inline Matrix<E>& Matrix<E>::swap( Matrix<E> &o ) { 00384 std::swap( this->m_val , o.m_val ); 00385 std::swap( this->m_rows , o.m_rows ); 00386 std::swap( this->m_cols , o.m_cols ); 00387 return *this; 00388 } 00389 00390 /** Le cambia las dimensiones a la matriz. 00391 - En algunos casos los valores almacenados en la matriz no quedan 00392 todos iguales a \c Matrix<E>::value_type(). 00393 - Si <code> (m * n == 0) </code> deja la matriz vacía. 00394 */ 00395 template <class E> 00396 void Matrix<E>::reSize(unsigned m, unsigned n) { 00397 const unsigned MxN = m * n; 00398 if (MxN == 0) { 00399 if (m_val != 0) { // sólo borra si hay algo que borrar 00400 delete [] m_val; 00401 m_rows = m_cols = 0; 00402 m_val = 0; 00403 } 00404 } else if ( MxN == m_rows * m_cols ) { // truco para evitar borrar la memoria dinámica 00405 m_rows = m; 00406 m_cols = n; 00407 } else { 00408 if (m_val != 0) { 00409 delete [] m_val; 00410 } 00411 m_val = new value_type [MxN] ; 00412 m_rows = m; 00413 m_cols = n; 00414 } 00415 return; 00416 00417 /* NOTA 00418 Esta es la antigua especificación de reSize(). Es incorrecta 00419 porque presume que el Rep de la matriz es un vector denso en 00420 donde están almacenados todos los valores de la matriz: 00421 00422 +----------------------------------------------------------+ 00423 | reSize(): Le cambia las dimensiones a la matriz. | 00424 | ======== | 00425 | - Si ocurre que (m*n) == rows() * cols() los valores de | 00426 | la matriz se mantienen, aunque cambian sus dimensiones.| 00427 | - Si (m*n) != rows() * cols() los valores de la matriz | 00428 | quedarán inicializados de la misma forma en que los | 00429 | inicializaría CERO == Sparse_Matrix<E>::value_type(). | 00430 | | 00431 | \pre (m > 0) && (n > 0) | 00432 +----------------------------------------------------------+ 00433 00434 En la actual especificación, que ya está corregida, no queda 00435 implícita la presunción sobre cómo está organizada internamente 00436 la matriz. Por eso, esta nueva especificación sí sirve para una 00437 matriz implementada con un vector denso de valores, o para la 00438 matriz implementada como una matriz rala. 00439 00440 Estos pequeños detalles en algunas ocasiones surgen cuando el 00441 programador de una clase introduce mejoras o modificaciones, pues 00442 muchas veces es muy difícil o prácticamente imposible predecir 00443 todos los pormenores y detalles de una especificación o de una 00444 implementación. 00445 */ 00446 00447 } 00448 00449 /** Le ajusta las dimensiones a la matriz. 00450 - Si ocurre que <code> (m*n) == rows()*cols() </code> hace 00451 lo mismo que haría \c reSize(m,n). 00452 - En caso contrario, no hace nada. 00453 */ 00454 template <class E> 00455 inline void Matrix<E>::reShape(unsigned m, unsigned n) { 00456 const unsigned MxN = m * n; 00457 if (MxN == m_rows * m_cols) { 00458 m_rows = m; 00459 m_cols = n; 00460 } 00461 } 00462 00463 /// Retorna una referencia al elemento [i,j] de la matriz ( \c const ). 00464 /// - \c M(i,j) significa lo que en arreglos se denota con \c M[i][j]. 00465 /// - <code>val = M(i,j); // M(i,j) es un "rvalue" (const)</code> 00466 template <class E> 00467 inline const E& Matrix<E>::operator() (unsigned i, unsigned j) const { 00468 assert( "Matrix<E>::operator()()" && (i < rows()) ); 00469 assert( "Matrix<E>::operator()()" && (j < cols()) ); 00470 00471 return m_val[ (i * m_cols) + j ] ; // almacena los valores por filas 00472 // return m_val[ i + (j * m_rows) ] ; // almacena los valores por columnas 00473 } 00474 00475 /// Retorna una referencia al elemento [i,j] de la matriz. 00476 /// - \c M(i,j) significa lo que en arreglos se denota con \c M[i][j]. 00477 /// - <code>M(i,j) = val; // M(i,j) es un "lvalue" (modificable)</code> 00478 template <class E> 00479 inline E& Matrix<E>::operator() (unsigned i, unsigned j) { 00480 assert( "Matrix<E>::operator()()" && (i < rows()) ); 00481 assert( "Matrix<E>::operator()()" && (j < cols()) ); 00482 00483 return m_val[ (i * m_cols) + j ] ; // almacena los valores por filas 00484 // return m_val[ i + (j * m_rows) ] ; // almacena los valores por columnas 00485 } 00486 00487 /** Le suma a \c "*this" la matriz \c "O". 00488 \pre 00489 - \c "*this" y \c "O" deben tener las mismas dimensiones 00490 - <code> rows() == O.rows() && cols() == O.cols() </code> 00491 00492 \par Complejidad: 00493 O( <code> rows() * cols() </code> ) 00494 00495 \remarks 00496 - Esta es la implementación de Matrix<E> operator+( Matrix<E>&, Matrix<E> ) 00497 - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>) 00498 definida con plantillas si esa función amiga no está definida (implementada) 00499 dentro de la declaración de la clase. Para solventar esta deficiencia existe 00500 este método que realiza el trabajo, aunque es poco cómodo de usar. 00501 */ 00502 template <class E> 00503 void Matrix<E>::add( const Matrix<E> & O ) { 00504 // verifica que las dos matrices sean del mismo tamaño 00505 assert( (rows() == O.rows()) && (cols() == O.cols()) ); 00506 00507 // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada. 00508 00509 value_type *pThis = m_val; 00510 value_type *pO = & O.m_val[0]; 00511 value_type *pEND = &m_val[m_cols * m_rows]; 00512 for ( ; pThis != pEND; ++pThis, ++pO) { 00513 *pThis += *pO; 00514 } 00515 return; 00516 } 00517 00518 /** Le resta a \c "*this" la matriz \c "O". 00519 \pre 00520 - \c "*this" y \c "O" deben tener las mismas dimensiones 00521 - <code> rows() == O.rows() && cols() == O.cols() </code> 00522 00523 \par Complejidad: 00524 O( <code> rows() * cols() </code> ) 00525 00526 \remarks 00527 - Esta es la implementación de Matrix<E> operator-( Matrix<E>&, Matrix<E> ) 00528 - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>) 00529 definida con plantillas si esa función amiga no está definida (implementada) 00530 dentro de la declaración de la clase. Para solventar esta deficiencia existe 00531 este método que realiza el trabajo, aunque es poco cómodo de usar. 00532 */ 00533 template <class E> 00534 void Matrix<E>::substract( const Matrix<E> & O ) { 00535 // verifica que las dos matrices sean del mismo tamaño 00536 assert( (rows() == O.rows()) && (cols() == O.cols()) ); 00537 00538 // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada. 00539 00540 value_type *pThis = m_val; 00541 value_type *pO = & O.m_val[0]; 00542 value_type *pEND = &m_val[m_cols * m_rows]; 00543 for ( ; pThis != pEND; ++pThis, ++pO) { 00544 *pThis -= *pO; 00545 } 00546 return; 00547 } 00548 00549 /** Calcula la multiplicación <code> A * B </code> y la almacena en \c "*this". 00550 - Las dimensiones de \c "*this" se ajustan de manera que: 00551 - <code> rows() == A.rows() && cols() == B.cols()</code> 00552 00553 \pre 00554 - \c "A" y \c "B" deben tener dimensiones compatibles 00555 - <code> A.cols() == B.rows() </code> 00556 - La multiplicación se hace [Fila x Columna], lo que implica que la cantidad 00557 de valores en la filas de \c "A" debe ser igual a la cantidad de columnas de \c "B" 00558 00559 \par Complejidad: 00560 O( <code> A.cols() * B.cols() * A.cols() </code> ) 00561 00562 \remarks 00563 - Esta es la implementación de Matrix<E> operator*( Matrix<E>&, Matrix<E> ) 00564 - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>) 00565 definida con plantillas si esa función amiga no está definida (implementada) 00566 dentro de la declaración de la clase. Para solventar esta deficiencia existe 00567 este método que realiza el trabajo, aunque es poco cómodo de usar. 00568 */ 00569 template <class E> 00570 void Matrix<E>::multiply( const Matrix<E> & A, const Matrix<E> & B ) { 00571 // Verifica que las matrices se puedan multiplicar 00572 assert( (A.cols() == B.rows()) && " => Matrix<E>::multiply()" ); 00573 00574 reSize( A.rows(), B.cols() ); 00575 value_type sum; 00576 for (unsigned i=0; i<rows(); i++) { 00577 for (unsigned j=0; j<cols(); j++) { 00578 sum = value_type(); // sum = E(0); 00579 for (unsigned k=0; k<A.cols(); k++) { 00580 sum = sum + A(i,k) * B(k,j); 00581 } 00582 // this->(i,j) = sum; // produce un error de compilación 00583 // this->operator()(i,j) = sum; // también funciona 00584 (*this)(i,j) = sum; // también funciona 00585 } 00586 } 00587 return; 00588 } 00589 00590 /// Graba en el flujo \c COUT el valor de \c M[][]. 00591 template <class E> 00592 std::ostream& operator<<(std::ostream& COUT, const Matrix<E>& M) { 00593 COUT << '[' << M.rows() << 'x' << M.cols() << ']' << std::endl; 00594 for (unsigned i=0; i < M.rows(); ++i) { 00595 for (unsigned j=0; j < M.cols(); ++j) { 00596 COUT << " " << M(i,j); 00597 } 00598 COUT << std::endl; 00599 } 00600 return COUT; 00601 } 00602 00603 /// Obtiene del flujo \c CIN el valor para \c M[][]. 00604 template <class E> 00605 std::istream& operator>>(std::istream& CIN, Matrix<E>& M) { 00606 assert( "This code has not been tested" ); 00607 unsigned rows,cols; 00608 CIN >> rows >> cols; 00609 M.reSize(rows,cols); 00610 for (unsigned i=0; i<rows; i++) { 00611 for (unsigned j=0; j<cols; j++) { 00612 CIN >> M(i,j); 00613 } 00614 } 00615 return CIN; 00616 } 00617 00618 } // namespace Mx 00619 00620 #include "Matrix_Lib.h" 00621 00622 #endif // Matrix_h 00623 // EOF: Matrix.h