阅读经典——《算法导论》03

矩阵乘法是种极其耗时的运算。

以C = A • B为例,其中A和B都是 n x n 的矩阵。根据矩阵乘法的定义,计算过程如下:

SQUARE-MATRIX-MULTIPLY(A, B)

n = A.rows

let C be a new nxn matrix

for i = 1 to n

for j = 1 to n

c[i][j] = 0

for k = 1 to n

c[i][j] += a[i][k] * b[k][j]

return C

由于存在三层循环,它的时间复杂度将达到O(n3)。

这是一个很可怕的数字。但是,凭着科学家们的智慧,这个数正在一步步下降。本文介绍经典的Strassen算法,该算法将时间复杂度降低到O(nlg7) ≈ O(n2.81)。别小看这个细微的改进,当n非常大时,该算法将比平凡算法节约大量时间。

分治法

Strassen算法基于分治的思想,因此我们首先考虑一个简单的分治策略。

每个 n x n 的矩阵都可以分割为四个 n/2 x n/2 的矩阵:

(式3-1)

因此可以将公式C = A • B改写为

(式3-2)

于是上式就等价于如下四个公式:

(式3-3)

C11 = A11 • B11 + A12 • B21

C12 = A11 • B12 + A12 • B22

C21 = A21 • B11 + A22 • B21

C22 = A21 • B12 + A22 • B22

每个公式需要计算两次矩阵乘法和一次矩阵加法,使用T(n)表示 n x n 矩阵乘法的时间复杂度,那么我们可以根据上面的分解得到一个递推公式。

T(n) = 8T(n/2) + Θ(n2)

其中,8T(n/2)表示8次矩阵乘法,而且相乘的矩阵规模降到了n/2。Θ(n2)表示4次矩阵加法的时间复杂度以及合并C矩阵的时间复杂度。

要想计算出T(n)并不复杂,可以采用画递归树的方式计算,或采用下一篇文章中讲的“主方法”直接计算。结果是

T(n) = Θ(n3)

可见,简单的分治策略并没有起到加速运算的效果。

Strassen算法

1969年,Volker Strassen发表文章提出一种渐进快于平凡算法的矩阵相乘算法,引起巨大轰动。在此之前,很少人敢设想一个算法能渐近快于平凡算法。矩阵乘法的渐近上界自此被改进了。

让我们回头观察前面使用分治策略的时候为什么无法提高速度。

因为分解后的问题包含了8次矩阵相乘和4次矩阵相加,就是这8次矩阵相乘导致了速度不能提升。于是我们想到能不能减少矩阵相乘的次数,取而代之的是矩阵相加的次数增加。Strassen正是利用了这一点。

现在,我们来看一下Strassen算法的原理。

仍然把每个矩阵分割为4份,然后创建如下10个中间矩阵:

S1 = B12 - B22

S2 = A11 + A12

S3 = A21 + A22

S4 = B21 - B11

S5 = A11 + A22

S6 = B11 + B22

S7 = A12 - A22

S8 = B21 + B22

S9 = A11 - A21

S10 = B11 + B12

接着,计算7次矩阵乘法:

P1 = A11 • S1

P2 = S2 • B22

P3 = S3 • B11

P4 = A22 • S4

P5 = S5 • S6

P6 = S7 • S8

P7 = S9 • S10

最后,根据这7个结果就可以计算出C矩阵:

C11 = P5 + P4 - P2 + P6

C12 = P1 + P2

C21 = P3 + P4

C22 = P5 + P1 - P3 - P7

是不是很神奇呢?话说我第一次看到这个算法的时候真的是惊呆了,10个S矩阵和7个P矩阵究竟是怎么凑出来的,简直不可思议。

我们可以把P矩阵和S矩阵展开,并带入最后的式子计算,会发现恰好是公式3中的四个式子。也就是说,Strassen为了计算公式3,绕了一大圈,用了更多的步骤,成功的把计算量变成了7个矩阵乘法和18个矩阵加法。虽然矩阵加法增加了好几倍,而矩阵乘法只减小了1个,但在数量级面前,18个加法仍然渐进快于1个乘法。这就是该算法的精妙之处。

同样地,我们可以写出Strassen算法的递推公式:

T(n) = 7T(n/2) + Θ(n2)

使用递归树或主方法可以计算出结果:

T(n) = Θ(nlg7) ≈ Θ(n2.81)

下图展示了平凡算法和Strassen算法的性能差异,n越大,Strassen算法节约的时间越多。

性能比较

小技巧:如何计算n是否为2的幂

在矩阵分解的过程中,我遇到了这样一个问题:如何判断一个 n x n 的矩阵是否能恰好分解为4个大小相同的矩阵。它的本质是判断n是否为2的幂。

最先想到的方法是不断除以2,直到余数不为0时判断当前的被除数是否为1,是则为2的幂,否则不是2的幂。这相当于通过右移检查n的二进制形式是否为1000...0。

但这种方式有些繁琐,需要循环判断。为了提高效率,我发现有位高手用下面这行代码解决了这个问题:

n & (n - 1) == 0

没错,只需要一行代码,而且只做了一次加法运算和一次与运算,效率大大提高。其原理也很容易解释,把n和n-1的二进制形式写出来一看就明白了。假设n=0010 0000,那么n-1 = 0001 1111,相与得到

0010 0000

& 0001 1111

------------

0000 0000

恰好是0。只要把n中右边的任意一个0换成1,结果都不再是0。

还有一种类似的方法:

(n & -n) == n

本质和前面是一样的。据说后一种做法来自JDK,但我没有考证到。

参考资料

矩阵相乘的strassen算法_矩阵乘法Strassen算法相关推荐

  1. Java黑皮书课后题第8章:**8.6(代数:两个矩阵相乘)编写两个矩阵相乘的方法。编写一个测试程序,提示用户输入两个3*3的矩阵,然后显示它们的乘积

    **8.6(代数:两个矩阵相乘)编写两个矩阵相乘的方法.编写一个测试程序,提示用户输入两个3*3的矩阵,然后显示它们的乘积 题目 题目描述与运行示例 破题 代码 题目 题目描述与运行示例 **8.6( ...

  2. 矩阵相乘的strassen算法_矩阵乘法的Strassen算法+动态规划算法(矩阵链相乘和硬币问题)...

    矩阵乘法的Strassen 这个算法就是在矩阵乘法中采用分治法,能够有效的提高算法的效率. 先来看看咱们在高等代数中学的普通矩阵的乘法 两个矩阵相乘 上边这种普通求解方法的复杂度为: O(n3) 也称 ...

  3. python矩阵乘法分治算法_矩阵乘法的Strassen算法详解 --(算法导论分治法求矩阵)...

    1 题目描述 2 思路分析 3 解法 4 小结 1 题目描述 请编程实现矩阵乘法,并考虑当矩阵规模较大时的优化方法. 2 思路分析 根据wikipedia上的介绍:两个矩阵的乘法仅当第一个矩阵B的列数 ...

  4. 矩阵相乘入门,两个矩阵相乘

    •矩阵:矩阵可以看成一个n×m的数表,用二维数组表示 •矩阵乘法:定义矩阵A,B.A和B可以乘法操作当且仅当A的大小是a×b,B的大小是b×c,设矩阵C=AB,则C的大小是a×c,且有 最普通的矩阵乘 ...

  5. C++两个矩阵相乘代码(内附有矩阵相乘的条件与规则,以及对代码的详细解答)

    再复制粘贴代码之前可以先了解学习一下什么是矩阵相乘,矩阵相乘的条件与规则又是什么. 点击一下链接即可进入学习:                       #矩阵相乘的学习链接 以下是两个矩阵相乘的代 ...

  6. 用python实现朴素贝叶斯算法_朴素贝叶斯算法 python 实现

    应用贝叶斯准则: 使用上面这些定义,可以定义贝叶斯分类准则为: 如果 P(c1|x, y) > P(c2|x, y), 那么属于类别 c1; 如果 P(c2|x, y) > P(c1|x, ...

  7. mysql区间算法_「五大常用算法」一文图解分治算法和思想

    前言 分治算法(divide and conquer)是五大常用算法(分治算法.动态规划算法.贪心算法.回溯法.分治界限法)之一,很多人在平时学习中可能只是知道分治算法,但是可能并没有系统的学习分治算 ...

  8. kmeans算法_实战 | KMeans 聚类算法

    1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支持向量机,集成算法Ad ...

  9. dijkstra算法_最小树——Dijkstra算法及Python实现

    #文章首发于公众号"如风起". 原文链接: 最小树--Dijkstra算法及Python实现​mp.weixin.qq.com ​最小树问题是一类非常简单的网络最优化问题,它在许多 ...

  10. 人工智能算法_人工智能的灵魂——算法

    人工智能有三驾马车:数据.算法.算力.本文重点介绍算法相关的知识. 本文将介绍算法在人工智能里的概念,算法的4个特征.6个通用方法.以及在选择算法时需要注意的3个点. 什么是算法? 简单的说,算法就是 ...

最新文章

  1. 解决使用Dockerfile来build镜像时pip install遇到的BUG
  2. ubuntu安装thrift
  3. Flocking for Multi-Agent Dynamic Systems:Algorithms and Theory
  4. 从C语言的角度重构数据结构系列(六)-C语言的数据类型及常变量
  5. 01_9_ServletContext
  6. linux下文件删除的原理精华讲解(考试题答案系列)
  7. android chrome 44,[图]非隔代升级:新代码暗示Chrome OS的安卓支持将基于Android Q
  8. 吴恩达|机器学习作业7.0.k-means聚类
  9. javascript 技巧总结积累1-108条(正在积累中)
  10. 实时环境映射贴图(Real-time Evironmnet Mapping)
  11. 华为数通部门软开9.16凉经
  12. java 将cad文件转化成pdf或图片,实现在线预览
  13. c++语言编程软件视频教程下载,C++编程开发全套视频教程下载
  14. 安装mantis 2.14
  15. 元数据管理实践数据血缘
  16. Fluent材料属性之比热容计算方法
  17. 信号与系统笔记 拉普拉斯变换的性质
  18. TeamTalk 详细介绍
  19. 创客必备!树莓派知识大扫盲
  20. 织梦dede文章列表调用标签的用法和规则

热门文章

  1. 操作系统原理课程 期末考试复习重点
  2. 【sketchup 2021】草图大师的辅助建模工具1【量角器与文字、尺寸标注与三维字、实体工具】
  3. 移远百科 | LTE-A关键技术分析
  4. 从“断臂求生”到一骑绝尘,航运巨头马士基如何利用区块链技术力挽狂澜?
  5. SyncToy -- 微软同步工具
  6. 专精特新企业数据库-专精特新企业名单及汇总
  7. kali 破解无线密码
  8. matlab或_Matlab下载安装教程
  9. [橘汁仙剑网出品]仙剑奇侠传六全剧情视频动画配音版[1080P][720P][H264]
  10. 内网代理流量:Socks5协议原理分析和编程