Multiplicacion de matrices y analisis de complejidad
Fuente: Material del curso
matricesmultiplicacioncomplejidadoperaciones
Enunciado
Dadas las siguientes matrices:
\qquad
B = \begin{pmatrix} 1 & 4 \\ 2 & 1 \\ 3 & 0 \end{pmatrix}_{3 \times 2}$$
**(a)** Calcule el producto $C = A \times B$. Muestre el calculo detallado de cada entrada $c_{ij}$:
$$c_{ij} = \sum_{k=1}^{p} a_{ik} \cdot b_{kj}$$
**(b)** Es posible calcular $B \times A$? Si es asi, cual seria la dimension de la matriz resultante? Calculela.
**(c)** El algoritmo clasico de multiplicacion de matrices de tamano $n \times n$ tiene complejidad $O(n^3)$. Explique por que, indicando cuantos bucles anidados se necesitan y que operacion se realiza en el bucle mas interno.
**(d)** Si se multiplican dos matrices de $1000 \times 1000$, aproximadamente cuantas operaciones de multiplicacion escalar se realizan?
**(e)** Investigue: que complejidad tiene el algoritmo de **Strassen** para multiplicacion de matrices? Por que es mejor que el algoritmo clasico para matrices grandes?
**(f)** Dada una matriz dispersa (sparse) donde el 90% de los elementos son cero, proponga una representacion eficiente en memoria y explique como se realizaria la multiplicacion aprovechando esta propiedad.
Solución rápida— la idea clave sin formalismo
La idea en simple
Como multiplicar. Cada casilla del resultado es “fila por columna”: multiplicas emparejando y sumas. Para c11 tomas la fila 1 de A y la columna 1 de B: 2⋅1+3⋅2+1⋅3=11. Repitiendo:
A×B=(11191116).
Se puede B×A? Si, porque las dimensiones internas encajan (2=2), y da una matriz 3×3 distinta. Ojo: multiplicar matrices no es como con numeros, el orden importa.
Por que es lento (O(n3)). Para llenar una matriz n×n recorres filas (n) por columnas (n), y cada casilla necesita una sumatoria de n productos. Son tres bucles encajados: n×n×n=n3. Con n=1000 eso son 10003= mil millones de multiplicaciones.
Se puede acelerar? Si:
Strassen hace la cuenta con 7 multiplicaciones de bloques en vez de 8 y baja el exponente a ≈2.807, util para matrices grandes.
Si la matriz es dispersa (casi todo ceros), guardas solo los numeros que no son cero y te saltas los ceros al multiplicar (cualquier cosa por cero es cero). Con 90% de ceros haces como 10× menos trabajo.
Solución formal— lista para entregar en un parcial
Solucion formal
(a)A2×3B3×2=C2×2, con cij=∑k=13aikbkj:
C=(11191116).
(b)B3×2A2×3=(BA)3×3, con (BA)ij=∑k=12bikakj:
BA=18863692173.
Como AB=BA, el producto no es conmutativo.
(c) El producto de Mn×n requiere cij=∑k=1naikbkj para cada uno de los n2 pares (i,j), y cada suma tiene n terminos:
#mult=∑i=1n∑j=1n∑k=1n1=n3⇒O(n3).
Tres bucles anidados; el interno hace una multiplicacion-acumulacion.
(d)n=1000⇒n3=109 multiplicaciones escalares.
(e) Strassen: O(nlog27)=O(n2.807), dividiendo en bloques 2n×2n y usando 7 (no 8) multiplicaciones recursivas. Como log27<3, es asintoticamente mejor para n grande.
(f) Matriz dispersa: almacenar solo elementos no nulos (COO: (i,j,v); o CSR). En la multiplicacion se omiten los productos con factor cero, reduciendo el costo de O(n3) a O(nnz⋅n), proporcional al numero de no nulos nnz. ■
Explicación completa— paso a paso, con visualizaciones
Solucion
(a) Producto C=A×B
A es 2×3 y B es 3×2; como las dimensiones internas coinciden (3=3), el producto existe y C es 2×2. Cada entrada es cij=∑k=13aikbkj:
Observacion: A×B=B×A (ni siquiera tienen la misma dimension). El producto de matrices no es conmutativo.
(c) Por que el algoritmo clasico es O(n3)
for (int i = 0; i < n; i++) // fila de C for (int j = 0; j < n; j++) { // columna de C C[i][j] = 0; for (int k = 0; k < n; k++) // producto punto C[i][j] += A[i][k] * B[k][j]; // operacion del bucle interno }
Son tres bucles anidados de n iteraciones cada uno. El bucle mas interno ejecuta una multiplicacion-acumulacion (C[i][j]←C[i][j]+A[i][k]⋅B[k][j]). El total de multiplicaciones escalares es
n⋅n⋅n=n3⇒O(n3).
(d) Dos matrices de 1000×1000
n3=10003=109 multiplicaciones escalares
Es decir, mil millones de multiplicaciones (y aproximadamente la misma cantidad de sumas). Esto ilustra por que el crecimiento cubico se vuelve costoso rapidamente.
(e) Algoritmo de Strassen
Strassen multiplica dos matrices en
O(nlog27)=O(n2.807…).
La idea es dividir cada matriz en cuatro bloques 2n×2n y calcular el producto usando 7 multiplicaciones de bloques en lugar de las 8 del metodo clasico (a cambio de mas sumas/restas). Al aplicarlo recursivamente, el exponente baja de 3 a log27≈2.807. Como 2.807<3, para ngrande Strassen realiza asintoticamente menos operaciones. En la practica solo compensa por encima de cierto umbral (matrices grandes), porque las constantes ocultas y las sumas extra lo hacen mas lento para n pequeno.
(f) Matriz dispersa (90% ceros)
Si el 90% de las entradas son cero, guardar n2 valores desperdicia memoria y tiempo. Representaciones eficientes:
Lista de coordenadas (COO): se guardan solo las triplas (fila,columna,valor) de los elementos no nulos.
CSR (Compressed Sparse Row): tres vectores — valores, indices_columna y punteros_fila — que permiten recorrer por filas sin almacenar ceros.
Multiplicacion aprovechando la dispersion: como 0⋅x=0, se omiten todos los productos donde algun factor es cero. El costo pasa a ser proporcional al numero de elementos no nulos (nnz) en lugar de n3. Con un 90% de ceros hay ~10× menos entradas activas, de modo que la multiplicacion dispersa realiza del orden de O(nnz⋅n) operaciones en vez de O(n3), un ahorro enorme para matrices grandes y muy dispersas.