/
Inicio :: Foros

 F.A.Q.F.A.Q.                  Conéctese para revisar sus mensajesConéctese para revisar sus mensajes   

Mulltiplicar matrices

 
      Índice del Foro elrincondelc.com -> C/C++ en General
Ver tema anterior :: Ver siguiente tema  
AutorMensaje
Juannabis



Registrado: 03 Ago 2004
Mensajes: 10

MensajePublicado: 26/09/2011 8:11 am
Título: Mulltiplicar matrices

Hola, alguien podría indicarme cómo optimizar la multiplicación de matrices en C, sin usar los típicos tres bucles??.

Estoy programando con Fortran y con C, donde en el primero existe una función llamada Matmul que va muy rápida y quería hacer algo parecido en C, no sé si alguien me podrá echar una mano.

Gracias.
Volver arriba
Pantalàimon_



Registrado: 17 Jul 2007
Mensajes: 1345

MensajePublicado: 26/09/2011 5:25 pm
Título:

Buenas!

Para multiplicar matrices con una complejidad menor que n^3( suponiendo matrices cuadradas de nxn ) puesdes probar de implementar el algoritmo de Strassen. Supongo que también habrá métodos numéricos para dicho cálculo pero ahora no recuerdo ninguno.

Por cierto, recalcar que una menor complejidad no supone una mayor rapidez. Por poner un ejemplo: puede ser que hasta matrices de 100x100 el algoritmo de Strassen sea más lento que el normal. Lo único que se puede garantizar es que a partir de cierta matriz de dimension N, el algoritmo de Strassen siempre será más rápido que el habitual.

De todas maneras podrías añadir el código que tienes hecho y si vemos algo que pueda mejorar su eficiencia te lo podríamos remarcar.

Un saludo!
Volver arriba
Juannabis



Registrado: 03 Ago 2004
Mensajes: 10

MensajePublicado: 28/09/2011 5:56 am
Título:

Hola!!, este es mi código
Código:

        for(i=0;i<tam_matriz;i++)
         for(j=0;j<tam_matriz;j++)
          for(k=0;k<tam_matriz;k++)
            Resultado[i][j]+=Identidad[i][k]*Matriz[k][j];



Estoy trabajando con matrices de 1000x1000, donde esta multiplicación en C tarda unos 14 segundos, mientras que en Fortran, utilizando en lugar del código anterior, la función Matmul, tarda 5 segundos.

Busco el código de esa función en Matmul y no lo encuentro, para intentar implementarlo en C y así ver la diferencias de tiempo.

También estaba buscando algo en C que hiciese que esta multiplicación de matrices se asimilase en tiempo a la realizada por Fortran con su función Matmul.

Muchas gracias.
Volver arriba
Pantalàimon_



Registrado: 17 Jul 2007
Mensajes: 1345

MensajePublicado: 28/09/2011 6:56 am
Título:

Pues a falta de que te responda otro busca información sobre el algoritmo de Strassen o algún método númerico para multiplicación de matrices.

El algoritmo de Strassen no lo he implementado nunca, aunque me gustaría hacerlo. Lo que estos días no dispondré de tiempo para tal.

Por cierto, intenta comprobar el tiempo para matMul y tu algoritmo para otras dimensiones de matrices. Y haz el cociente de tiempos.

Por ejemplo:
Para 1000 x 1000:
- tu algoritmo: 11 s
- matMul: 5 s
- Cociente: 11/5 = 2.2

Para 500 x 500:
- tu algoritmo: tantos s
- matMul: otros tantos s
- Cociente: tantos / otros tantos = ...

Y mira si ese cociente se incrementa, se mantiene constante o disminuye a medida que vas aumentando las dimensiones de las matrices. Infórmame cuando sepas algo.

Un saludo!
Volver arriba
Juannabis



Registrado: 03 Ago 2004
Mensajes: 10

MensajePublicado: 28/09/2011 8:31 am
Título:

Aquí están los resultados:

Matriz: 1000x1000
Matmul: 1.17sg
C: 13.89sg
Cociente: 13.89/1.17=11.87

Matriz: 500x500
Matmul: 0.18sg
C: 1.54sg
Cociente: 1.54/0.18=8.5

Gracias.
Volver arriba
Pantalàimon_



Registrado: 17 Jul 2007
Mensajes: 1345

MensajePublicado: 01/10/2011 12:19 pm
Título:

Hola de nuevo.

Parece ser que el algoritmo de matMul tiende a ser más rapido respecto al algoritmo clásico cuanto más grande son las dimensiones de las matrices. Ahora bien, para estar más seguro necesitaba otros ejemplos de dimensions de matrices y no sólo para 1000x1000 y 500x500( Como dije, se trataba de un ejemplo para que se entendiera lo que pedía).

De todos modos, ¿has probado de aplicar el algoritmo de Strassen o has encontrado algún otro método?

En base a esto

he creado un prototipo que sólo sirve para matrices de dimensión 2x2. Sin embargo, una vez funciona el algoritmo con matrices 2x2 sólo hay que preocuparse de implementar bien el algoritmo por divide y vencerás.

El código está muy ofuscado pero lo he hecho expresamente, así, reduciendo lo máximo que he podido el número de variables intermedias y el número de sumas/restas. Pues si se implementa tal cual en la imagen puede repercutir bastante negativamente en el rendimiento del algoritmo y el uso de memoria.
Código:

#include<stdio.h>

int main(void)
{
   int A[2][2] = {
      { 1,  3 },
      { 1,  2 }};

   int B[2][2] = {
      { 2,  4 },
      {-1, -3 }};

   int C[2][2] = {0};
   int D[2][2] = {0};

   int i, j, k;

   int m,n,o,p,q,r,s;

   /* Algoritmo clásico: 8 multiplicaciones */
   for( i = 0; i < 2; ++i )
      for( j = 0; j < 2; ++j )
         for( k = 0; k < 2; ++k )
            C[i][k] += A[i][j] * B[j][k];

   /* Algoritmo de Strassen: 7 multiplicaciones */
   m = A[0][0] * B[0][0];
   n = A[0][1] * B[1][0];

   D[0][0] = m + n;

   o =  A[0][0] - A[1][0];
   p = -B[0][1] + B[1][1];

   q =  A[1][1] - o;
   r =  B[0][0] + p;

   s =  q * r;
   s += m;

   n =  o * p;
   n += s;

   o =  A[1][0] + A[1][1];
   p = -B[0][0] + B[0][1];

   m = o * p;

   D[1][1] = m + n;

   m += s;

   o = A[0][1] - q;
   s = o * B[1][1];

   D[0][1] = m + s;

   p = r - B[1][0];
   s = A[1][1] * p;

   D[1][0] = n - s;

   /* imprimir  C */
   for( i = 0; i < 2; ++i )
   {
      for( j = 0; j < 2; ++j )
         printf(" %2d", C[i][j] );
      putchar('\n');
   }

   putchar('\n');

   /* imprimir D */
   for( i = 0; i < 2; ++i )
   {
      for( j = 0; j < 2; ++j )
         printf(" %2d", D[i][j] );
      putchar('\n');
   }

   return 0;
}


Un saludo!
Volver arriba
nisar



Registrado: 26 Sep 2011
Mensajes: 34

MensajePublicado: 04/10/2011 5:25 am
Título:

Buenas.

Estoy empezando con c e igual digo alguna tontería pero ahí va.

Y creando una función donde pasas las matrices como punteros para que sean procesadas como tales con la notación '*' haciéndolo por el método de los tres bucles for que comenta el compañero no habría diferencia en el tiempo de ejecución?

Ya se que las matrices en si son punteros pero bueno por probar?
Volver arriba
Pantalàimon_



Registrado: 17 Jul 2007
Mensajes: 1345

MensajePublicado: 06/10/2011 2:51 am
Título:

Ya dije que el algoritmo de Strassen me gustaría implementarlo. Pues nada, que cuando me pica el gusanillo no puedo dejar las cosas a medias. Me ha costado más de lo que esperaba pero al final lo he conseguido Very Happy.

El algoritmo de Strassen para multiplicacion de matrices cuadradas nxn tiene una complejidad O(n^2.8 ) a diferencia del algoritmo clásico que tiene una complejidad O(n³). De esta manera, cuanto más grandes son las matrices, más se nota la diferencia de tiempos en ejecución entre los dos algoritmos. En caso de multiplicación de matrices mxn y nxp el coste es O( m*n*p / min(m,n,p)^0.2 ).

El algoritmo de Strassen es un algoritmo por divide y vencerás, con lo cual hay que buscar un umbral(threshold) para el cual dejar de aplicar recursividad y aplicar el algorítmo clásico. Hallar el mejor umbral es una tarea experimental y que depende de cada máquina. En mi caso he observado toscamente que el umbral debe estar entre matrices de 32x32 y 64x64.

En fin, os dejo el código( C++ ) por si queréis jugar un poco con éste:

Código:
#include<iostream>
#include<cstdlib>
#include<ctime>

class matrix;

class matrix
{
   private:
      static const int threshold;
      static bool random;
      bool erase;

      int* mat;
      int r_size, c_size;
      int r, c;

      void assign( const matrix& a, const int& rr, const int& cc, const int& k = 1 );
      void sum( const matrix& a, const int& rr, const int& cc, const int& k = 1 );
      void sum( const matrix& a, const matrix& b, const int& ak = 1, const int& bk = 1 );
      void mul_classic( const matrix& a, const matrix& b );
      void mul_strassen( const matrix& a, const matrix& b );

   public:
      matrix( const int& r_size, const int& c_size );
      matrix( const int& r, const int& c, const int& top_num );
      matrix( const int* m, const int& r, const int& c );
      matrix( const matrix& m, const int& r_init, const int& c_init, const int& r, const int& c );
      ~matrix();

      bool operator==( const matrix& a );
      matrix operator*( const matrix& a );
      matrix classic_mul( const matrix &a );
      void show() const;
};

#define M 1000
#define N 1000
#define P 1000

int main()
{

   matrix a( N, N, 2 );
//   a.show();
//   std::cout << std::endl;

   matrix b( N, N, 2 );
//   b.show();
//   std::cout << std::endl;

   matrix c = a * b;
   std::cout << "Hecho! multiplicacion por Strassen." << std::endl;
//   c.show();
//   std::cout << std::endl;

   matrix d = a.classic_mul( b );
   std::cout << "Hecho! multiplicacion por la forma clasica." << std::endl;
//   d.show();
//   std::cout << std::endl;

   return 0;
}



/* matrix */

bool matrix::random = false;
const int  matrix::threshold = 48;

// asignar memoria para una matriz. Mucho cuidado, no se inicializan sus dimensiones.
matrix::matrix( const int& r_size, const int& c_size )
   : r_size(r_size), c_size(c_size), erase(true)
{
   mat = new int[ r_size*c_size ];
}

// crear matriz r x c con valores aleatorios entre -top_num y top_num
matrix::matrix( const int& r, const int& c, const int& top_num )
   : r_size(r), c_size(c), r(r), c(c), erase(true)
{
   mat = new int[ r_size*c_size ];

   if( not random )
   {
      std::srand( time(NULL) );
      random = true;
   }

   for( int i = 0; i < r*c; ++i )
      mat[i] = -top_num + std::rand() % (2*top_num+1);
}

// crear matriz r x c a partir de un array
matrix::matrix( const int* m, const int& r, const int& c )
   : r_size(r), c_size(c), r(r), c(c), erase(true)
{
   mat = new int[ r * c ];

   for( int i = 0; i < r*c; ++i )
      mat[i] = m[i];
}

// crear submatriz en funcion de otra matriz. Coste O(1)
matrix::matrix( const matrix& m, const int& r_init, const int& c_init, const int& r, const int& c )
   : mat(m.mat + r_init * m.c_size + c_init), r_size(m.r_size), c_size(m.c_size), r(r), c(c), erase(false)
{ }

matrix::~matrix()
{
   if( erase ) delete [] mat;
}

matrix matrix::operator*( const matrix& a )
{
   matrix b( r, a.c );

   b.r = r;
   b.c = a.c;

   if( c != a.r )
      std::cout << "error: las matrices no se pueden multiplicar" << std::endl;
   else
      b.mul_strassen( *this, a );

   return b;

}

matrix matrix::classic_mul( const matrix& a )
{

   matrix b( r, a.c );

   b.r = r;
   b.c = a.c;

   if( c != a.r )
      std::cout << "error: las matrices no se pueden multiplicar" << std::endl;
   else
      b.mul_classic( *this, a );

   return b;
}

bool matrix::operator==( const matrix& a )
{
   if( c != a.c || r != a.r )
      return false;

   int i;
   for( i = 0; i < r*c && mat[i] == a.mat[i] ; ++i );

   return i >= r*c;
}

// this = k * a( rr x cc )
void matrix::assign( const matrix& a, const int& rr, const int& cc, const int& k )
{
   int i, j;
   for( i = 0; i < rr; ++i )
      for( j = 0; j < cc; ++j )
         mat[ i * c_size + j ] = k * a.mat[ i * a.c_size + j ];
}

// this += k * a( rr x cc)
void matrix::sum( const matrix& a, const int& rr, const int& cc, const int& k )
{
   int i, j;
   for( i = 0; i < rr; ++i )
      for( j = 0; j < cc; ++j )
         mat[ i * c_size + j ] += k * a.mat[ i * a.c_size + j ];
}

// this = ak*a + bk*b
void matrix::sum( const matrix& a, const matrix& b, const int& ak, const int& bk )
{
   if( erase )
   {
      r = a.r;
      c = a.c;
      assign( a, a.r, a.c, ak );
      sum   ( b, b.r, b.c, bk );
   }
   else
   {
      assign( a, r, c, ak );
      sum   ( b, r, c, bk );
   }
}


// this = a x b
void matrix::mul_classic( const matrix& a, const matrix& b )
{
   int i, j, k;

   r = a.r;
   c = b.c;

   for( i = 0; i < r; ++i )
      for( j = 0; j < c; ++j )
         mat[ i * c_size + j ] = 0;

   for( i = 0; i < r; ++i )
      for( j = 0; j < c; ++j )
         for( k = 0; k < b.r; ++k ) // cambio hace poco
            mat[ i * c_size + j ] += a.mat[ i * a.c_size + k ] * b.mat[ k * b.c_size + j ];
}

void matrix::show() const
{
   for( int i = 0; i < r; ++i )
   {
      for( int j = 0; j < c; ++j )
      {
         std::cout.width(3);
         std::cout << mat[ i*c_size + j ];
      }
      std::cout << std::endl;
   }
}

void matrix::mul_strassen( const matrix& a, const matrix& b )
{
   if( a.r <= threshold ||
       a.c <= threshold ||
       b.r <= threshold ||
       b.c <= threshold )
   {
      mul_classic( a, b );
      return;
   }

   r = a.r;
   c = b.c;

   int arm =  r  / 2;
   int acm = a.c / 2;
   int brm = b.r / 2;
   int bcm =  c  / 2;

   matrix xa(r - arm, a.c - acm );
   matrix ya(r - arm, a.c - acm );

   matrix A00( a,  0,  0,  arm ,   acm   );
   matrix A01( a,  0,acm,  arm , a.c-acm );
   matrix A10( a,arm,  0, r-arm,   acm   );
   matrix A11( a,arm,acm, r-arm, a.c-acm );

   matrix xb(b.r - brm, c - bcm );
   matrix yb(b.r - brm, c - bcm );

   matrix B00( b,  0,  0,   brm  ,  bcm   );
   matrix B01( b,  0,bcm,   brm  , c-bcm );
   matrix B10( b,brm,  0, b.r-brm,   bcm   );
   matrix B11( b,brm,bcm, b.r-brm, c-bcm );

   matrix m( xa.r_size, xb.c_size );
   matrix n( xa.r_size, xb.c_size );
   matrix o( xa.r_size, xb.c_size );

   matrix C00( *this,  0,  0,  arm ,  bcm  );
   matrix C01( *this,  0,bcm,  arm , c-bcm );
   matrix C10( *this,arm,  0, r-arm,  bcm  );
   matrix C11( *this,arm,bcm, r-arm, c-bcm );

   // m = A00 * B00
   m.mul_strassen( A00, B00 );
   // n = A01 * B10
   n.mul_strassen( A01, B10 );
   // C00 = m + n
   C00.sum( m, n );
   // xa = -A10 + A00
   xa.sum( A10, A00, -1,  1 );
   // xb =  B11 - B01
   xb.sum( B11, B01,  1, -1 );
   // ya =  A11 - xa
   ya.sum( A11,  xa,  1, -1 );
   // yb =  xb + B00
   yb.sum( xb, B00 );
   // o = ya * yb
   o.mul_strassen( ya, yb );
   // o += m
   o.sum( m, m.r, m.c );

   // redimensionar xa en caso necesario
   if( xa.c < xb.r ){
      for( int i = 0; i < xa.r; ++i )
         xa.mat[ i*xa.c_size + xa.c ] = 0;
      ++xa.c;
   }
   // n = xa * xb
   n.mul_strassen( xa, xb );
   // n += o
   n.sum( o, o.r, o.c );
   // xa = A11 + A10
   xa.sum( A11, A10 );
   // xb = B10 - B00
   xb.sum( B01, B00, 1, -1 );

   // redimensionar xb en caso necesario
   if( xb.r < xa.c ){
      for( int j = 0; j < xb.c; ++j )
         xb.mat[ xb.r*xb.c_size + j ] = 0;
      ++xb.r;
   }
   // m = xa * xb
   m.mul_strassen( xa, xb ); // m' x m
   // C11 = n + m
   C11.sum( n, m );
   // m += o
   m.sum( o, o.r, o.c ); // m x m'''
   // xa = -ya + A01;
   xa.sum( ya, A01, -1,  1 );
   // o = xa * B11
   o.mul_strassen( xa, B11 );
   // C01 = m + o
   C01.sum( m, o );
   // xb = yb - B10;
   xb.sum( yb, B10,  1, -1 );
   // o = A11 * xb
   o.mul_strassen( A11, xb );
   // C10 = n - o
   C10.sum(  n,  o,  1, -1 );

}



Un saludo!
Volver arriba
Juannabis



Registrado: 03 Ago 2004
Mensajes: 10

MensajePublicado: 20/10/2011 11:06 am
Título:

Pues si que te lo has currado, mis más sinceras felicitaciones.

He estado tomando tiempos y este algoritmo es mucho más rápido que el que tengo yo, pero sigue siendo lento con respecto a la función matmul de Fortran...

Gracias!!.
Volver arriba
      Índice del Foro elrincondelc.com -> C/C++ en General
Página 1 de 1Todas las horas están en GMT - 8 Horas

 
No puede crear mensajes
No puede responder temas
No puede editar sus mensajes
No puede borrar sus mensajes
No puede votar en encuestas

(c) ElRincondelC.com

Un proyecto de UrlanHeat.com