#算法学习录#Strassen矩阵乘法

今天我们谈谈一个“土豪”算法——Strasen矩阵算法
之说以说它“土豪”就是因为其带来了巨大的空间开销。
先来考察一个问题:请用三次实数乘法计算复数a+bi和c+di相乘。
由于:
a×(c+d)=ac+ad=s1 ;
b×(c-d)=bc-bd=s2 ;
d×(a+b)=ad+bd=s3 ;
故有实部:s1 -s3 =ac-bd,
虚部:s2+ s3 =ad+bc。
这样,四次的乘法就变成三次乘法。

Strassen矩阵乘法也是如此,把A,B,C矩阵分解为n/2×n/2子矩阵,进行7次递归计算n/2×n/2矩阵的乘法,其运行时间的递归式:

T(n)= Θ(1)             if n=1;

      7T(n/2)+Θ(n^2 )    if n>1;

令:
S1=B12-B22;
S2=A11+A12;
S3=A21+A22;
S4=B21-B22;
S5=A11+A22;
S6=B11+B22;
S7=A12-A22;
S8=B21+B22;
S9=A11-A21;
S10=B11+B12;
那么: P1= A11·S1 = A11·(B12-B22)
P2= B22·S2 = B22·(A11+A12)
P3= B11·S3 = B11·(A21+A22)
P4= A22·S4 = A22·(B21-B22)
P5= S5·S6 = (A11+A22)·(B11+B22)
P6= S7·S8 = (A12-A22)·(B21+B22)
P7= S9·S10 = (A11-A21)·(B11+B12)

C11= P5 + P4 - P2 + P6=A11×B11+A12×B21
C12= P1 + P2=A11×B12+A12×B22
C21= P3 + P4=A21×B11+A22×B21
C22= P5 + P1 – P3 – P7=A21×B21+A22×B22

Strassen算法的具体实现(C语言):
int Strassen(int **A, int **B, int **Result, int Size){
 if (Size == 1){
  //直接计算C11
  Result[0][0] = A[0][0] * B[0][0];
  return 0;
 }
 int NewSize = Size / 2;
 /*分块矩阵*/
 int **A11, **A12, **A21, **A22;
 int **B11, **B12, **B21, **B22;
 int **C11, **C12, **C21, **C22;

 int **P1, **P2, **P3, **P4, **P5, **P6, **P7;
 /*存放数组A、B(i、j)的临时变量*/
 int **AResult, **BResult;

 A11 = new int*[NewSize];
 A12 = new int*[NewSize];
 A21 = new int*[NewSize];
 A22 = new int*[NewSize];

 B11 = new int*[NewSize];
 B12 = new int*[NewSize];
 B21 = new int*[NewSize];
 B22 = new int*[NewSize];

 C11 = new int*[NewSize];
 C12 = new int*[NewSize];
 C21 = new int*[NewSize];
 C22 = new int*[NewSize];

 P1 = new int*[NewSize];
 P2 = new int*[NewSize];
 P3 = new int*[NewSize];
 P4 = new int*[NewSize];
 P5 = new int*[NewSize];
 P6 = new int*[NewSize];
 P7 = new int*[NewSize];

 AResult = new int*[NewSize];
 BResult = new int*[NewSize];

 for (int i = 0; i < NewSize; i++)
 {
  A11[i] = new int[NewSize];
  A12[i] = new int[NewSize];
  A21[i] = new int[NewSize];
  A22[i] = new int[NewSize];

  B11[i] = new int[NewSize];
  B12[i] = new int[NewSize];
  B21[i] = new int[NewSize];
  B22[i] = new int[NewSize];

  C11[i] = new int[NewSize];
  C12[i] = new int[NewSize];
  C21[i] = new int[NewSize];
  C22[i] = new int[NewSize];

  P1[i] = new int[NewSize];
  P2[i] = new int[NewSize];
  P3[i] = new int[NewSize];
  P4[i] = new int[NewSize];
  P5[i] = new int[NewSize];
  P6[i] = new int[NewSize];
  P7[i] = new int[NewSize];

  AResult[i] = new int[NewSize];
  BResult[i] = new int[NewSize];


 }

 //对分块矩阵赋值
 for (int i = 0; i < NewSize; i++)
 {
  for (int j = 0; j < NewSize; j++)
  {
   A11[i][j] = A[i][j];
   A12[i][j] = A[i][j + NewSize];
   A21[i][j] = A[i + NewSize][j];
   A22[i][j] = A[i + NewSize][j + NewSize];

   B11[i][j] = B[i][j];
   B12[i][j] = B[i][j + NewSize];
   B21[i][j] = B[i + NewSize][j];
   B22[i][j] = B[i + NewSize][j + NewSize];

  }
 }

 //计算P1 = A11*(B12-B22)
 Sub(B12, B22, BResult, NewSize);
 Strassen(A11, BResult, P1, NewSize);

 //计算P2 = (A11+A12)*B22
 Add(A11, A12, AResult, NewSize);
 Strassen(AResult, B22, P2, NewSize);

 //计算P3 = (A21+A22)*B11
 Add(A21, A22, AResult, NewSize);
 Strassen(AResult, B11, P3, NewSize);

 //计算P4 = A22*(B21-B11)
 Sub(B21, B11, BResult, NewSize);
 Strassen(A22, BResult, P4, NewSize);

 //计算P5 = (A11+A22)*(B11+B22)
 Add(A11, A22, AResult, NewSize);
 Add(B11, B22, BResult, NewSize);
 Strassen(AResult, BResult, P5, NewSize);

 //计算P6 = (A12-A22)*(B21+B22)
 Sub(A12, A22, AResult, NewSize);
 Add(B21, B22, BResult, NewSize);
 Strassen(AResult, BResult, P6, NewSize);

 //计算P7 = (A11-A21)*(B11+B12)
 Sub(A11, A21, AResult, NewSize);
 Add(B11, B12, BResult, NewSize);
 Strassen(AResult, BResult, P7, NewSize);

 //计算C11,C12,C21,C22
 //C11 = P5 + P4 - P2 + P6;
 Add(P5, P4, AResult, NewSize);
 Sub(AResult, P2, BResult, NewSize);
 Add(BResult, P6, C11, NewSize);

 //C12=P1+P2
 Add(P1, P2, C12, NewSize);

 //C21=P3+P4
 Add(P3, P4, C21, NewSize);

 //C22=P5+P1-P3-P7
 Add(P5, P1, C22, NewSize);
 Sub(C22, P3, C22, NewSize);
 Sub(C22, P7, C22, NewSize);

 //合并C11,C12,C21,C22
 for (int i = 0; i < NewSize; i++)
 {
  for (int j = 0; j < NewSize; j++)
  {
   Result[i][j] = C11[i][j];
   Result[i][j + NewSize] = C12[i][j];
   Result[i + NewSize][j] = C21[i][j];
   Result[i + NewSize][j + NewSize] = C22[i][j];
  }
 }

 //删除数组,回收资源
 for (int i = 0; i < NewSize; i++){
  delete[] A11[i]; delete[] A12[i]; delete[] A21[i]; delete[] A22[i];
  delete[] B11[i]; delete[] B12[i]; delete[] B21[i]; delete[] B22[i];
  delete[] C11[i]; delete[] C12[i]; delete[] C21[i]; delete[] C22[i];
  delete[] P1[i]; delete[] P2[i]; delete[] P3[i]; delete[] P4[i]; delete[] P5[i]; delete[] P6[i]; delete[] P7[i];
  delete[] AResult[i]; delete[] BResult[i];
 }
 delete[] A11; delete[] A12; delete[] A21; delete[] A22;
 delete[] B11; delete[] B12; delete[] B21; delete[] B22;
 delete[] C11; delete[] C12; delete[] C21; delete[] C22;
 delete[] P1; delete[] P2; delete[] P3; delete[] P4; delete[] P5; delete[] P6; delete[] P7;
 delete[] AResult; delete[] BResult;
 return 0;
}


//矩阵相加
void Add(int **A, int **B, int **Q, int Size){
 for (int i = 0; i < Size; i++){
  for (int j = 0; j < Size; j++){
   Q[i][j] = A[i][j] + B[i][j];
  }
 }
}

//矩阵相减
void Sub(int**A, int**B, int **Q, int Size){
 for (int i = 0; i < Size; i++){
  for (int j = 0; j < Size; j++){
   Q[i][j] = A[i][j] - B[i][j];
  }
 }
}
演示结果:
 


与暴力求解相比:
   for(i=0;i<m;i++)
     for(j=0;j<m;j++){
         C[i][j]=0;
      for(k=0;k<n;k++)
            C[i][j]+=A[i][k]*B[k][j];            
}   
其运行时间(n^lg7,2.80<lg7<2.81)比暴力求解(n3)稍快,但其精度较低(在处理小数计算时),且消耗了大量存储空间。

最后附上源代码:https://github.com/LRC-cheng/Algorithms_Practise.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容

  • 贪心算法 贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上...
    fredal阅读 9,079评论 3 52
  • 阅读经典——《算法导论》03 矩阵乘法是种极其耗时的运算。 以C = A • B为例,其中A和B都是 n x n ...
    金戈大王阅读 26,900评论 10 24
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,663评论 0 33
  • C++运算符重载-下篇 本章内容:1. 运算符重载的概述2. 重载算术运算符3. 重载按位运算符和二元逻辑运算符4...
    Haley_2013阅读 1,402评论 0 49
  • 十月的最后一天。 万圣节的前夜。 一上午的专业课整个人都疲惫不堪,当然这怎么可能击倒当代大学生似火般的激情。顶着反...
    星然啊阅读 233评论 0 0