Algoritmo de Strassen
# Algoritmo de Strassen

En la disciplina matemática del álgebra lineal, el algoritmo de Strassen, llamado así por Volker Strassen, es un algoritmo usado para la multiplicación de matrices. Es asintóticamente más rápido que el algoritmo de multiplicación de matrices estándar, pero más lento que el algoritmo más rápido conocido, y es útil en la práctica para matrices grandes.

# Algoritmo de Strassen
## Historia

Volker Strassen publicó el algoritmo de Strassen en 1969. Pese a que su algoritmo es sólo ligeramente más rápido que el algoritmo estándar para la multiplicación de matrices, fue el primero en señalar que el enfoque estándar no es óptimo. Su artículo comenzó la búsqueda de algoritmos aún más rápidos, como el complejo algoritmo de Coppersmith–Winograd de Shmuel Winograd en 2010 (que utiliza 20 multiplicaciones binarias, pero utiliza 155 sumas binarias en lugar de las 18 del algoritmo de Strassen), publicado en 2000 .

# Algoritmo de Strassen
## Algoritmo

Sean A, B dos matrices cuadradas sobre un anillo R. Queremos calcular la matriz C como producto
    $\mathbf {C} =\mathbf {A} \mathbf {B} \qquad \mathbf {A} ,\mathbf {B} ,\mathbf {C} \in R^{2^{n}\times 2^{n}}$
Si las matrices A, B no son de tipo 2$^n$ x 2$^n$ habrá que rellenar lo que falta de filas y columnas con ceros.
Partimos A, B y C en matrices de igual tamaño de bloque
    $\mathbf {A} ={\begin{bmatrix}\mathbf {A} _{1,1}&\mathbf {A} _{1,2}\\\mathbf {A} _{2,1}&\mathbf {A} _{2,2}\end{bmatrix}}{\mbox{ , }}\mathbf {B} ={\begin{bmatrix}\mathbf {B} _{1,1}&\mathbf {B} _{1,2}\\\mathbf {B} _{2,1}&\mathbf {B} _{2,2}\end{bmatrix}}{\mbox{ , }}\mathbf {C} ={\begin{bmatrix}\mathbf {C} _{1,1}&\mathbf {C} _{1,2}\\\mathbf {C} _{2,1}&\mathbf {C} _{2,2}\end{bmatrix}}$
Con
    $\mathbf {A} _{i,j},\mathbf {B} _{i,j},\mathbf {C} _{i,j}\in R^{2^{n-1}\times 2^{n-1}}$
Entonces
    $\mathbf {C} _{1,1}=\mathbf {A} _{1,1}\mathbf {B} _{1,1}+\mathbf {A} _{1,2}\mathbf {B} _{2,1}$
    $\mathbf {C} _{1,2}=\mathbf {A} _{1,1}\mathbf {B} _{1,2}+\mathbf {A} _{1,2}\mathbf {B} _{2,2}$
    $\mathbf {C} _{2,1}=\mathbf {A} _{2,1}\mathbf {B} _{1,1}+\mathbf {A} _{2,2}\mathbf {B} _{2,1}$
    $\mathbf {C} _{2,2}=\mathbf {A} _{2,1}\mathbf {B} _{1,2}+\mathbf {A} _{2,2}\mathbf {B} _{2,2}$
Con esta construcción, no hemos reducido el número de multiplicaciones. Todavía tenemos 8 multiplicaciones para calcular la matriz C$_{i,j}$ , que es el mismo número de multiplicaciones que se necesitan cuando se usa el método estándar de multiplicación de matrices.
Ahora viene la parte importante. Definimos las matrices de nuevo
    $\mathbf {M} _{1}:=(\mathbf {A} _{1,1}+\mathbf {A} _{2,2})(\mathbf {B} _{1,1}+\mathbf {B} _{2,2})$
    $\mathbf {M} _{2}:=(\mathbf {A} _{2,1}+\mathbf {A} _{2,2})\mathbf {B} _{1,1}$
    $\mathbf {M} _{3}:=\mathbf {A} _{1,1}(\mathbf {B} _{1,2}-\mathbf {B} _{2,2})$
    $\mathbf {M} _{4}:=\mathbf {A} _{2,2}(\mathbf {B} _{2,1}-\mathbf {B} _{1,1})$
    $\mathbf {M} _{5}:=(\mathbf {A} _{1,1}+\mathbf {A} _{1,2})\mathbf {B} _{2,2}$
    $\mathbf {M} _{6}:=(\mathbf {A} _{2,1}-\mathbf {A} _{1,1})(\mathbf {B} _{1,1}+\mathbf {B} _{1,2})$
    $\mathbf {M} _{7}:=(\mathbf {A} _{1,2}-\mathbf {A} _{2,2})(\mathbf {B} _{2,1}+\mathbf {B} _{2,2})$
que luego se utilizan para expresar C$_{i,j}$ en términos de M$_k$. Debido a nuestra definición de la M$_k$ podemos eliminar una multiplicación de matrices y reducir el número de multiplicaciones a 7 (una multiplicación por cada M$_k$) y expresar C$_{i,j}$ como
    $\mathbf {C} _{1,1}=\mathbf {M} _{1}+\mathbf {M} _{4}-\mathbf {M} _{5}+\mathbf {M} _{7}$
    $\mathbf {C} _{1,2}=\mathbf {M} _{3}+\mathbf {M} _{5}$
    $\mathbf {C} _{2,1}=\mathbf {M} _{2}+\mathbf {M} _{4}$
    $\mathbf {C} _{2,2}=\mathbf {M} _{1}-\mathbf {M} _{2}+\mathbf {M} _{3}+\mathbf {M} _{6}$
Iteramos n-veces el proceso de división hasta que las submatrices degeneran en números (elementos del anillo R).
Las implementaciones prácticas del algoritmo de Strassen, permiten cambiar a métodos estándar de multiplicación de matrices para submatrices lo suficientemente pequeñas, para las cuales son más eficientes. El punto a partir del cual el algoritmo de Strassen es más eficiente depende de la implementación específica y del hardware. Se ha estimado que el algoritmo de Strassen es más rápido para matrices con anchura desde 32 a 128 para implementaciones optimizadas, y 60.000 o más para implementaciones básicas.

# Algoritmo de Strassen
## Análisis numérico

La multiplicación de matrices estándar requiere aproximadamente $2N^{3}$ (donde $N=2^{n}$)operaciones aritméticas (sumas y multiplicaciones); la complejidad asintótica es $O(N^{3})$.
El número de sumas y multiplicaciones requeridas en el algoritmo de Strassen puede ser calculado como sigue: sea $f(n)$ el número de operaciones para una matriz de $2^{n}\times 2^{n}$. Entonces por aplicación recursiva del algoritmo de Strassen, vemos que $f(n)=7f(n-1)+l4^{n}$, para alguna constante $l$ que depende del número de sumas realizadas en cada aplicación del algoritmo. Por lo tanto $f(n)=(7+o(1))^{n}$,esto es, la complejidad asintótica para multiplicar matrices de tamaño $N=2^{n}$ usando el algoritmo de Strassen es
    $O((7+o(1))^{n})=O(N^{\log _{2}7+o(1)})\approx O(N^{2.807})$.
La reducción en el número de operaciones aritméticas se obtiene a cambio de reducir un tanto la estabilidad numérica.