[算法系列之十五]Strassen矩阵相乘算法

引言

Strassen矩阵乘法是一种典型的分治算法。目前为止,我们已经见过一些分治策略的算法了,例如归并排序和Karatsuba大数快速乘法。现在,让我们看看分治策略的背后原理是什么。

同动态规划不同,在动态规划中,为了得到最终的答案,我们需要把一个大的问题“展开”为几个子问题(“expand” the solutions of sub-problems),但是在这里,我们会更多的谈到如何把一些子解决方案组合到一起。对于一般问题,他们的子问题的解决方案是对等的,他们的归并方式也是通过某种方式定义好的。

一个典型的例子就是归并排序算法。在归并排序中,我们有两个有序数组,我们想要这两个数组在合并之后仍然保持有序。当然了,在归并排序中,最复杂的地方当属自我合并,而原因在于,我们不得不传递两个数组,A和B,然后去比较每一“对”分别来自数组A和数组B的元素。有一点离题,但是,这是归并排序的一个弱点,虽然,它的最坏情况的时间复杂度是 O(n.log(n)),但是,快速排序却往往是实践中更为有效的排序方法,因为,它没有“合并”的过程。快速排序仅仅把两个子数组连接到一起,请注意,在快速排序中,子数组一般并不具有相同的长度,虽然他的最坏时间复杂度是O(n^2),但它的性能却经常好于归并排序。

在上文中,那个简单的例子告诉我们:有时候如何合并两个子问题并不是一个简单的事情。因此,当我们使用分治策略的时候,我们必须非常谨慎。

历史

Volker Strassen是一位出生于1936年的德国数学家。他因为在概率论上的工作而广为人知,但是在计算机科学和算法领域,他却因为矩阵相乘算法而被大部分人认识,这个算法目前仍然是比通用矩阵相乘算法性能好的主要算法之一。

Strassen在1969年第一次发表关于这个算法的文章,并证明了复杂度为n^3的算法并不是最优算法。虽然,事实上Strassen给出的解决方案只会好一点点,但是,他的贡献却是相当巨大的,就是因为这导致了矩阵相乘领域更多的研究,产生了更快的算法,比如复杂度为O(n^2,3737)的Coppersmith-Winograd算法。

概述

两个矩阵 A[NxN] 和 B[NxN] 相乘的通用算法是非常简单的。虽然矩阵相乘比两个数字相乘要复杂得多,而且也不满足交换律,但它仍然非常简单——同时也很慢。

让我们先来定义一下什么是A[NxN]矩阵。当我们提到NxN矩阵,我们首先想到的是一个有N行N列的网格。在每一行和每一列的A[i][j],我们都有一个值。

当然,作为一个开发者,我们会把一个矩阵看成一个二维数组。

int array[][] =
    {
        {1,2,3,4},
        {5,6,7,8},
        {9,10,11,12}
    };

不要忘记,NxN的矩阵仅仅是任意矩阵中的一种情况,同样的,我们可以有其他任何大小的NxM矩阵(N <> M)。
然而,为了能够和另外一个矩阵相乘,矩阵的大小是非常重要的,为什么?
正如我上面提到的一样,矩阵相乘不同于数字相乘。首先,这个操作不满足交换律。

第二个问题是,让矩阵A和B相乘的方法。

仅仅因为这种方法只对NxN阶矩阵有效,因此我们能看到把矩形矩阵相乘产生的问题(Just because this works with NxN matrices we can see the problem with multiplying rectangular matrices)。确实,如果A矩阵的第二维和B矩阵的第一维不相等,这是不可能的完成的。

不过好在我们现在正在讨论的是具有相同维数的方形矩阵。
好了,现在我们知道如何让两个方形矩阵相乘(具有相同维数NxN),现在,让我们一起去评估一下一般矩阵相乘算法的时间复杂度。

我们知道A.B = C,当且仅当:

C[i][j] = sum(A[i][k] * B[k][j]) for k = 0 .. n

于是,我们有一个n^3复杂度的操作。现在,让我们尽力找一个分治策略的算法。

这个对于矩阵来说确实并不难,因为我们知道,一个矩阵可以被分成很多更小的子矩阵。

现在,我们有什么?

再一次,同样的时间复杂度——8个乘积和4个和,那么,计算量在哪?

当然, 为了得到更快的解决方案,我们不得不看一下Strassen在1969做过的工作。他如下图定义了P1, P2, P3, P4, P5, P6 和 P7。

时间复杂度

正如我以上提到的,Strassen算法仅仅比一般矩阵相乘算法好一点点。一般矩阵相乘算法时间复杂度是O(n^3),然而Strassen算法复杂度则是O(n^2.80)。

你能在下图观察到,随着n的变大,Strassen算法是如何比一般矩阵相乘算法变得更有效率的。

应用

虽然这个算法看起来更接近纯数学领域,而不是计算机领域。但在实际应用中,任何用到NxN数组的地方,我们都可以从矩阵相乘算法中获益。

另一方面,Strassen算法并不比n^3复杂度的通用矩阵相乘算法快很多。这很重要,因为对于一个很小的n(通常n<45)来说,通用矩阵相乘算法在实践中往往是更好的选择。然而,你可以从以上的图片中看到,对于n>100的情况来说,这两个算法的差别还是相当大的。

同时,当我们谈到|V| = n的邻接矩阵,以及一些依赖矩阵相乘的图论算法的时候,NxN数组经常会在这些领域中使用。

原文链接

Computer Algorithms: Strassen’s Matrix Multiplication

时间: 2024-11-01 17:59:02

[算法系列之十五]Strassen矩阵相乘算法的相关文章

算法系列(十二) 多边形区域填充算法:几种边标志填充算法

四.边界标志填充算法 在光栅显示平面上,多边形是封闭的,它是用某一边界色围成的一 个闭合区域,填充是逐行进行的,即用扫描线逐行对多边形求交,在交点对之间填充.边界标志填充 算法就是在逐行处理时,利用边界或边界颜色作为标志来进行填充的.准确地说,边界标志填充算法 不是指某种具体的填充算法,而是一类利用扫描线连贯性思想的填充算法的总称.这类算法有很多种 ,本篇就介绍几种. 首先介绍一种以边为中心的边缘填充算法,这种边界标志算法的基本思想 是:对于每一条扫描线和每一条多边形边的交点(xi,yi),将该

算法系列(十二) 多边形区域填充算法:改进的扫描线填充算法

三.改进的扫描线填充算法 扫描线填充算法的原理和实现都很简单,但是因为要同时维护 "活动边表(AET)"和"新边表(NET)",对存储空间的要求比较高.这两张表的部分内容是重复 的,而且"新边表"在很多情况下都是一张稀疏表,如果能对其进行改进,避免出现两张表,就可以 节省存储空间,同时省去从"边表"生成"新边表"的开销,同时也省去了用"新边表"维护"活动 边表"的开销

算法系列(十二) 多边形区域填充算法--扫描线种子填充算法

1.3扫描线种子填充算法 1.1和1.2节介绍的两种种子填充算法的优点是非常简单,缺点是使 用了递归算法,这不但需要大量栈空间来存储相邻的点,而且效率不高.为了减少算法中的递归调用 ,节省栈空间的使用,人们提出了很多改进算法,其中一种就是扫描线种子填充算法.扫描线种子填 充算法不再采用递归的方式处理"4-联通"和"8-联通"的相邻点,而是通过沿水平扫描线填充像素 段,一段一段地来处理"4-联通"和"8-联通"的相邻点.这样算法

算法系列(十二) 多边形区域填充算法--递归种子填充算法

平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界(也 可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜色(也 可能是图案填充).区域填充中最常用的是多边形填色,本文中我们就讨论几种多边形区域填充算法 . 一.种子填充算法(Seed Filling) 如果要填充的区域是以图像元数据方式给出的 ,通常使用种子填充算法(Seed Filling)进行区域填充.种子填充算法需要给出图像数据的区域, 以及区域内的一个点,这种算法比较适合人

[算法系列之三十]Dijkstra单源最短路径算法

单源最短路径问题 给定一个带权有向图 G=(V,E) ,其中每条边的权是一个非负实数.另外,还给定 V 中的一个顶点,称为源.现在我们要计算从源到所有其他各顶点的最短路径长度.这里的长度是指路上各边权之和.这个问题通常称为单源最短路径问题. 前面Bellman-Ford最短路径算法讲了单源最短路径的Bellman-Ford算法(动态规划算法).这里介绍另外一个更常见的算法Dijkstra算法. Dijkstra算法和 最小生成树Prim算法最小生成树算法非常类似,大家可以先熟悉下个算法.两个算法

算法系列(十五) 循环和递归在算法中的应用

一.递归和循环的关系 1. 递归的定义 顺序执行.循环和跳转是冯·诺依曼计算机体 系中程序设计语言的三大基本控制结构,这三种控制结构构成了千姿百态的算法,程序,乃至整个软件世 界.递归也算是一种程序控制结构,但是普遍被认为不是基本控制结构,因为递归结构在一般情况下都可 以用精心设计的循环结构替换,因此可以说,递归就是一种特殊的循环结构.因为递归方法会直接或间接 调用自身算法,因此是一种比迭代循环更强大的循环结构. 2. 递归和循环实现的差异 循 环(迭代循环)结构通常用在线性问题的求解,比如多项

算法系列(十二)多边形区域填充算法:扫描线填充算法(有序边表法)

二.扫描线算法(Scan-Line Filling) 扫描线算法适合对矢量图形进行区域填充,只需要 直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合使用,比如电 脑游戏和三维CAD软件的渲染等等. 对矢量多边形区域填充,算法核心还是求交.<计算几何 与图形学有关的几种常用算法>一文给出了判断点与多边形关系的算法――扫描交点的奇偶数判断算 法,利用此算法可以判断一个点是否在多边形内,也就是是否需要填充,但是实际工程中使用的填充 算法都是只使用求交的思想,并不直接使用这

x264代码剖析(十五):核心算法之宏块编码中的变换编码

x264代码剖析(十五):核心算法之宏块编码中的变换编码           为了进一步节省图像的传输码率,需要对图像进行压缩,通常采用变换编码及量化来消除图像中的相关性以减少图像编码的动态范围.本文主要介绍变换编码的相关内容,并给出x264中变换编码的代码分析.   1.变换编码           变换编码将图像时域信号变换成频域信号,在频域中图像信号能量大部分集中在低频区域,相对时域信号,码率有较大的下降. H.264对图像或预测残差采用4×4整数离散余弦变换技术,避免了以往标准中使用的通

jQuery 1.9.1源码分析系列(十五)之动画处理_jquery

首先需要有队列(queue)的基本知识.见上一章. 相关教程:jQuery下的动画处理总结: http://www.jb51.net/article/42000.htm jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween  :http://www.jb51.net/article/75821.htm a.动画入口jQuery.fn.animate函数执行流程详解 ------------------------------------------------------