Strassen矩阵乘法的C语言算法实现

矩阵乘法是高等代数中的重要基本运算,本文将介绍Strassen矩阵乘法的基本原理和用C语言进行算法实现的过程。

1. 一般矩阵乘法

首先,我们来看一下一般矩阵乘法的计算过程。

矩阵 A = [ a 11 a 12 … a 1 n a 21 a 22 … a 2 n … … … … a n 1 a n 2 … a n n ] A=\begin{bmatrix} a_{11} & a_{12} & … & a_{1n} \\ a_{21} & a_{22} & … & a_{2n} \\ … & … & … & … \\ a_{n1} & a_{n2} & … & a_{nn} \end{bmatrix} A= a11a21an1a12a22an2a1na2nann ,矩阵 B = [ b 11 b 12 … b 1 n b 21 b 22 … b 2 n … … … … b n 1 b n 2 … b n n ] B=\begin{bmatrix} b_{11} & b_{12} & … & b_{1n} \\ b_{21} & b_{22} & … & b_{2n} \\ … & … & … & … \\ b_{n1} & b_{n2} & … & b_{nn} \end{bmatrix} B= b11b21bn1b12b22bn2b1nb2nbnn

C = A ⋅ B C=A \cdot B C=AB,那么 C C C中的第 i i i行第 j j j列元素可表示为:

C i j = ∑ k = 1 n a i k b k j = a i 1 b 1 j + a i 2 b 2 j + … + a i n b n j C_{ij}=\sum_{k=1}^{n}a_{ik}b_{kj}=a_{i1}b_{1j}+a_{i2}b_{2j}+…+a_{in}b_{nj} Cij=k=1naikbkj=ai1b1j+ai2b2j++ainbnj

可以发现两个 n × n n \times n n×n的矩阵相乘,结果还是一个 n × n n \times n n×n的矩阵,结果矩阵中每个元素经过了 n n n次乘法和 ( n − 1 ) (n-1) (n1)次加法计算出来,那么总共需要 n 3 n^3 n3次乘法和 ( n 3 − n 2 ) (n^3 - n^2) (n3n2)次加法,因此时间复杂度为 O ( n 3 ) O(n^3) O(n3)

2. 分块矩阵乘法

如果采用《分治法求解最大子数组》一文中的分治思想,将矩阵 A A A B B B C C C都划分为分块矩阵:

A = [ A 11 A 12 A 21 A 22 ] A=\begin{bmatrix} A_{11} & A_{12} \\ A_{21} & A_{22}\end{bmatrix} A=[A11A21A12A22] B = [ B 11 B 12 B 21 B 22 ] B=\begin{bmatrix} B_{11} & B_{12} \\ B_{21} & B_{22}\end{bmatrix} B=[B11B21B12B22] C = [ C 11 C 12 C 21 C 22 ] C=\begin{bmatrix} C_{11} & C_{12} \\ C_{21} & C_{22}\end{bmatrix} C=[C11C21C12C22]

其中每一块小矩阵的大小为 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n

此时有

C 11 = A 11 B 11 + A 12 B 21 C_{11}=A_{11}B_{11}+A_{12}B_{21} C11=A11B11+A12B21
C 12 = A 11 B 12 + A 12 B 22 C_{12}=A_{11}B_{12}+A_{12}B_{22} C12=A11B12+A12B22
C 21 = A 21 B 11 + A 22 B 21 C_{21}=A_{21}B_{11}+A_{22}B_{21} C21=A21B11+A22B21
C 22 = A 21 B 12 + A 22 B 22 C_{22}=A_{21}B_{12}+A_{22}B_{22} C22=A21B12+A22B22

因此 n × n n \times n n×n的矩阵 C C C的计算过程需要 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n小矩阵的8次乘法和4次加法,小矩阵的乘法可以递归调用,直至分解到每个小矩阵只剩一个元素。4次 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n小矩阵的加法时间复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2),因此矩阵 C C C计算的整体时间复杂度 T ( n ) T(n) T(n)可以表示为

T ( n ) = 8 T ( n 2 ) + Θ ( n 2 ) T(n)=8T(\frac{n}{2})+\Theta(n^2) T(n)=8T(2n)+Θ(n2) n > 2 n > 2 n>2

可推导出 T ( n ) = O ( n log ⁡ 8 ) = O ( n 3 ) T(n) = O(n^{\log{8}})=O(n^3) T(n)=O(nlog8)=O(n3),也就是说明这样分块计算的时间复杂度与一般矩阵乘法的时间复杂度相同。

3. Strassen矩阵乘法

在上述分块矩阵的基础上,Strassen矩阵乘法尝试降低乘法的次数,设计出如下的7个中间矩阵:

P = ( A 11 + A 22 ) ( B 11 + B 22 ) P=(A_{11}+A_{22})(B_{11}+B_{22}) P=(A11+A22)(B11+B22)
Q = ( A 21 + A 22 ) B 11 Q=(A_{21}+A_{22})B_{11} Q=(A21+A22)B11
R = A 11 ( B 12 − B 22 ) R=A_{11}(B_{12}-B_{22}) R=A11(B12B22)
S = A 22 ( B 21 − B 11 ) S=A_{22}(B_{21}-B_{11}) S=A22(B21B11)
T = ( A 11 + A 12 ) B 22 T=(A_{11}+A_{12})B_{22} T=(A11+A12)B22
U = ( A 21 − A 11 ) ( B 11 + B 12 ) U=(A_{21}-A_{11})(B_{11}+B_{12}) U=(A21A11)(B11+B12)
V = ( A 12 − A 22 ) ( B 21 + B 22 ) V=(A_{12}-A_{22})(B_{21}+B_{22}) V=(A12A22)(B21+B22)

此时用了7次乘法和10次加法,然后再通过这7个中间矩阵计算得到矩阵 C C C

C 11 = P + S − T + V C_{11}=P+S-T+V C11=P+ST+V
C 12 = R + T C_{12}=R+T C12=R+T
C 21 = Q + S C_{21}=Q+S C21=Q+S
C 22 = P + R − Q + U C_{22}=P+R-Q+U C22=P+RQ+U

在此过程中,又用了8次加法。因此总共用了7次乘法和18次加法,整体时间复杂度 T ( n ) T(n) T(n)可以表示为

T ( n ) = 7 T ( n 2 ) + Θ ( n 2 ) T(n)=7T(\frac{n}{2})+\Theta(n^2) T(n)=7T(2n)+Θ(n2) n > 2 n > 2 n>2

可推导出 T ( n ) = O ( n log ⁡ 7 ) ≈ O ( n 2.81 ) T(n) = O(n^{\log{7}}) \approx O(n^{2.81}) T(n)=O(nlog7)O(n2.81)

4. 代码

#include <stdio.h>
#include <stdlib.h>#define N 512   //定义矩阵的大小(行数),默认参与运算的矩阵为方阵 void plus(int size, int **A, int **B, int **C) {  //计算 A+B->Cint i, j;for (i = 0; i < size; i++) {for (j = 0; j < size; j++) {C[i][j] = A[i][j] + B[i][j];}}
}void minus(int size, int **A, int **B, int **C) {  //计算 A-B->Cint i, j;for (i = 0; i < size; i++) {for (j = 0; j < size; j++) {C[i][j] = A[i][j] - B[i][j];}}
}void multiply(int size, int **A, int **B, int **C) {  //常规算法计算 A*B->C int i, j, k;for (i = 0; i < size; i++) {  for (j = 0; j < size; j++) {  C[i][j] = 0;        for (k = 0; k < size; k++) {  C[i][j] = C[i][j] + A[i][k]*B[k][j];          }    }          }  
}  void strassen(int size, int **A, int **B, int **C) {  //strassen算法计算 A*B->C int half = size / 2;if (size == 2) {  multiply(size, A, B, C);  //当矩阵阶数为 2 时,达到递归边界,直接按常规算法计算} else {int **A11, **A12, **A21, **A22;  //Divide matrix A into 4 sub-matrices int **B11, **B12, **B21, **B22;  //Divide matrix B into 4 sub-matricesint **C11, **C12, **C21, **C22;  //Divide matrix C into 4 sub-matricesint **AA, **BB;  //AA记录矩阵A的子矩阵运算的中间结果,BB记录矩阵B的子矩阵运算的中间结果 int **P1, **P2, **P3, **P4, **P5, **P6, **P7;//为上述矩阵开空间 A11 = (int**)malloc(sizeof(int*) * half);A12 = (int**)malloc(sizeof(int*) * half);A21 = (int**)malloc(sizeof(int*) * half);A22 = (int**)malloc(sizeof(int*) * half);B11 = (int**)malloc(sizeof(int*) * half);B12 = (int**)malloc(sizeof(int*) * half);B21 = (int**)malloc(sizeof(int*) * half);B22 = (int**)malloc(sizeof(int*) * half);C11 = (int**)malloc(sizeof(int*) * half);C12 = (int**)malloc(sizeof(int*) * half);C21 = (int**)malloc(sizeof(int*) * half);C22 = (int**)malloc(sizeof(int*) * half);AA = (int**)malloc(sizeof(int*) * half);BB = (int**)malloc(sizeof(int*) * half);P1 = (int**)malloc(sizeof(int*) * half);P2 = (int**)malloc(sizeof(int*) * half);P3 = (int**)malloc(sizeof(int*) * half);P4 = (int**)malloc(sizeof(int*) * half);P5 = (int**)malloc(sizeof(int*) * half);P6 = (int**)malloc(sizeof(int*) * half);P7 = (int**)malloc(sizeof(int*) * half);int i, j;	for (i = 0; i < half; i++) {A11[i] = (int*)malloc(sizeof(int) * half);A12[i] = (int*)malloc(sizeof(int) * half);A21[i] = (int*)malloc(sizeof(int) * half);A22[i] = (int*)malloc(sizeof(int) * half);B11[i] = (int*)malloc(sizeof(int) * half);B12[i] = (int*)malloc(sizeof(int) * half);B21[i] = (int*)malloc(sizeof(int) * half);B22[i] = (int*)malloc(sizeof(int) * half);C11[i] = (int*)malloc(sizeof(int) * half);C12[i] = (int*)malloc(sizeof(int) * half);C21[i] = (int*)malloc(sizeof(int) * half);C22[i] = (int*)malloc(sizeof(int) * half);AA[i] = (int*)malloc(sizeof(int) * half);BB[i] = (int*)malloc(sizeof(int) * half);P1[i] = (int*)malloc(sizeof(int) * half);P2[i] = (int*)malloc(sizeof(int) * half);P3[i] = (int*)malloc(sizeof(int) * half);P4[i] = (int*)malloc(sizeof(int) * half);P5[i] = (int*)malloc(sizeof(int) * half);P6[i] = (int*)malloc(sizeof(int) * half);P7[i] = (int*)malloc(sizeof(int) * half);}//将 A, B矩阵填入各自的分块矩阵 for (i = 0; i < half; i++) {for (j = 0; j < half; j++) {A11[i][j] = A[i][j];A12[i][j] = A[i][j + half];  A21[i][j] = A[i + half][j];  A22[i][j] = A[i + half][j + half];  B11[i][j] = B[i][j];  B12[i][j] = B[i][j + half];  B21[i][j] = B[i + half][j];  B22[i][j] = B[i + half][j + half]; }}//计算开始 minus(half, B12, B22, BB);  //Calculating P1 = A11 * (B12 - B22)strassen(half, A11, BB, P1);plus(half, A11, A12, AA);  //Calculating P2 = (A11 + A12) * B22strassen(half, AA, B22, P2);plus(half, A21, A22, AA);  //Calculating P3 = (A21 + A22) * B11strassen(half, AA, B11, P3);minus(half, B21, B11, BB);  //Calculating P4 = A22 * (B21 - B11)strassen(half, A22, BB, P4);plus(half, A11, A22, AA);  //Calculating P5 = (A11 + A22) * (B11 + B22)plus(half, B11, B22, BB);strassen(half, AA, BB, P5);minus(half, A12, A22, AA);  //Calculating P6 = (A12 - A22) * (B21 + B22)plus(half, B21, B22, BB);strassen(half, AA, BB, P6);minus(half, A11, A21, AA);  //Calculating P7 = (A11 - A21) * (B11 + B12)plus(half, B11, B12, BB);strassen(half, AA, BB, P7);plus(half, P5, P4, C11);  //Calculating C11 = P5 + P4 - P2 + P6minus(half, C11, P2, C11);plus(half, C11, P6, C11);plus(half, P1, P2, C12);  //Calculating C12 = P1 + P2plus(half, P3, P4, C21);  //Calculating C21 = P3 + P4plus(half, P5, P1, C22);  //Calculating C22 = P5 + P1 - P3 - P7minus(half, C22, P3, C22);minus(half, C22, P7, C22);//将矩阵C的四个分块矩阵合并填入C中 for (i = 0; i < half; i++) {  for (j = 0; j < half; j++) {  C[i][j] = C11[i][j];  C[i][j + half] = C12[i][j];  C[i + half][j] = C21[i][j];  C[i + half][j + half] = C22[i][j];          }          }  //释放空间 for (i = 0; i < half; i++) {free(A11[i]);free(A12[i]);free(A21[i]);free(A22[i]); 				free(B11[i]);free(B12[i]);free(B21[i]);				free(B22[i]);				free(C11[i]);free(C12[i]);free(C21[i]);				free(C22[i]);				free(P1[i]);free(P2[i]);free(P3[i]);free(P4[i]);				free(P5[i]);free(P6[i]);free(P7[i]);				free(AA[i]);free(BB[i]);			}free(A11);free(A12);free(A21);free(A22);				free(B11);free(B12);free(B21);free(B22);				free(C11);free(C12);free(C21);free(C22);				free(P1);free(P2);free(P3);free(P4);free(P5);				free(P6);free(P7);				free(AA);				free(BB);}
}int main() {int **A = (int**)malloc(sizeof(int*) * N); int **B = (int**)malloc(sizeof(int*) * N);int **C = (int**)malloc(sizeof(int*) * N); int i, j;for (i = 0; i < N; i++) {A[i] = (int*)malloc(sizeof(int) * N);B[i] = (int*)malloc(sizeof(int) * N);C[i] = (int*)malloc(sizeof(int) * N);}//从文件中读取矩阵A FILE *fa;fa = fopen("la.txt", "r");if (fa == NULL) {printf("Cannot get matrix A!\n");exit(0);}else {for (i = 0; i < N; i++) {for (j = 0; j < N; j++) {fscanf(fa, "%d", &A[i][j]);}}}fclose(fa);//从文件中读取矩阵B FILE *fb;fb = fopen("lb.txt", "r");if (fb == NULL) {printf("Cannot get matrix B!\n");exit(0);}else {for (i = 0; i < N; i++) {for (j = 0; j < N; j++) {fscanf(fb, "%d", &B[i][j]);}}}fclose(fb);strassen(N, A, B, C);  //strassen算法计算 A*B->C //打印结果到屏幕的同时,格式化输出到文件 FILE *fc = fopen("lc.txt", "w");for (i = 0; i < N; i++) {for (j = 0; j < N; j++) {printf("%d ", C[i][j]);if (j != N - 1) {fprintf(fc, "%d\t", C[i][j]);}else {fprintf(fc, "%d\n", C[i][j]);}}printf("\n");}return 0;
} 

5. 运行结果

程序从文件la.txt和lb.txt中读入 A A A B B B两个矩阵,相乘后的结果矩阵 C C C会输出到屏幕上,同时会输出到文件lc.txt中。
运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法】观光(求次短路,Dijkstra)

题目 “您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。 比荷卢经济联盟有很多公交线路。 每天公共汽车都会从一座城市开往另一座城市。 沿途汽车可能会在一些城市&#xff08;零或更多&#xff09;停靠。 旅行社计划旅途从 S 城市出发&#xff0c;到 F 城市结…

美赛注意事项

2024年1月27日 &#xff1a; 赖维杰 同学分享 1、最后的展现必须要漂亮&#xff08;绘图、呈现&#xff09; 李维情 西北建模王 论文位&#xff08;核心&#xff09;必须清楚建模位、编程位知道做了些什么 常见模型&#xff1a; 1、看真题&#xff0c;读往年论文&#xff0c;选…

在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通

目录 博客前言 一.创建springboot项目 新建项目 选择创建类型​编辑 测试 二.集成达梦数据库 添加达梦数据库部分依赖 添加数据库驱动包 配置数据库连接信息 编写测试代码 验证连接是否成功 博客前言 随着数字化时代的到来&#xff0c;数据库在应用程序中的地位越来…

pytorch-metric-learning度量学习工具官方文档翻译

基于Pytorch实现的度量学习方法 开源代码&#xff1a;pytorch-metric-learning官网文档&#xff1a;PyTorch Metric Learning官方文档 度量学习相关的损失函数介绍&#xff1a; 度量学习DML之Contrastive Loss及其变种度量学习DML之Triplet Loss度量学习DML之Lifted Structu…

RDMA vs InfiniBand 网卡接口如何区分?

(该架构图来源于参考文献) 高性能计算网络&#xff0c;RoCE vs. InfiniBand该怎么选&#xff1f; 新 RoCEv2 标准可实现 RDMA 路由在第三层以太网网络中的传输。RoCEv2 规范将用以太网链路层上的 IP 报头和 UDP 报头替代 InfiniBand 网络层。这样&#xff0c;就可以在基于 IP…

Android (6) 弹窗 onJsAlert,onJsConfirm,onJsPrompt

目录 1 网页的3种弹窗 1.1 Alert警示弹窗 1.2 Confirm确认弹窗 1.3 Prompt输入弹窗 2 WebView支持弹窗 2.1 onJsAlert 2.2 onJsConfirm 2.3 onJsPrompt AndroidApp内嵌一个WebView用于承载网页,WebView会监听拦截网页的3种弹窗(Alert,Confirm,Prompt),如果不做任何处理…

Java算法---递归算法基础介绍

目录 一、递归算法 二、递归算法的典型例子 &#xff08;1&#xff09;阶乘 &#xff08;2&#xff09;二分查找 &#xff08;3&#xff09;冒泡排序 &#xff08;4&#xff09;插入排序 一、递归算法 计算机科学中&#xff0c;递归是一种解决计算问题的方法。其中解决方案…

GM/T 0018-2012 设备接口描述笔记

GM/T 0018-2012 设备接口描述笔记 文章目录 GM/T 0018-2012 设备接口描述笔记6. 设备接口描述6.1 密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置6.2 设备管理类函数6.3 密钥管理类函数6.4 非对称算法运算类函数6.5 对称算法运算类函数6.6 杂凑运算类函数6.7 用户…

ServletResponse接口

ServletResponse接口 ServletContext接口向servlet提供关于其运行环境的信息。上下文也称为Servlet上下文或Web上下文,由Web容器创建,用作ServletContext接口的对象。此对象表示Web应用程序在其执行的上下文。Web容器为所部署的每个Web应用程序创建一个ServletContext对象。…

NineData支持制定安全、可靠的SQL开发规范

在和数据库打交道中&#xff0c;不管是数据库管理员&#xff08;DBA&#xff09;还是开发人员&#xff0c;经常会做一些CURD操作。因为每个人对数据库的了解程度不一样&#xff0c;所以在项目上线时&#xff0c;往往还需要专职人员对数据库的CURD操作进行审核&#xff0c;确保C…

vue3+naiveUI二次封装的v-model 联动输入框

根据官网说明使用 源码 <template><div class"clw-input pt-3"><n-inputref"input":value"modelValue":type"type":title"title"clearable:disabled"disabled":size"size"placeholder&…

RISC-V常用汇编指令

RISC-V寄存器表&#xff1a; RISC-V和常用的x86汇编语言存在许多的不同之处&#xff0c;下面将列出其中部分指令作用&#xff1a; 指令语法描述addiaddi rd,rs1,imm将寄存器rs1的值与立即数imm相加并存入寄存器rdldld t0, 0(t1)将t1的值加上0,将这个值作为地址&#xff0c;取…

【JaveWeb教程】(32)SpringBootWeb案例之《智能学习辅助系统》的详细实现步骤与代码示例(5)文件上传的实现

目录 SpringBootWeb案例052. 文件上传2.1 简介2.2 本地存储 SpringBootWeb案例05 前面我们已经实现了员工信息的条件分页查询以及删除操作。 关于员工管理的功能&#xff0c;还有两个需要实现新增和修改员工。 本节的主要内容&#xff1a; 文件上传 2. 文件上传 在我们完成…

从 React 到 Qwik:开启高效前端开发的新篇章

1. Qwik Qwik 是一个为构建高性能的 Web 应用程序而设计的前端 JavaScript 框架,它专注于提供即时启动性能,即使是在移动设备上。Qwik 的关键特性是它采用了称为“恢复性”的技术,该技术消除了传统前端框架中常见的 hydration 过程。 恢复性是一种序列化和恢复应用程序状态…

日常学习之:如何使用 dockerfile 将 vue 的单独前端项目通过 docker 的方式部署到 heroku上

文章目录 需求描述开始操作准备阶段&#xff1a;准备 server.js 文件并安装依赖&#xff0c;将 vue 项目包装成单独的服务器制作 server.js安装 server.js 需要的依赖 构建 Dockerfileheroku container 链接和部署其他细节 需求描述 你想用 vue 构建前端&#xff0c;用 django…

【设计模式】阿里终面:你觉得这个例子是策略模式吗?

什么是策略模式&#xff1f; 策略模式&#xff0c;举几个贴近生活的例子&#xff1a;当我们出行的时候&#xff0c;不同的出行方式就是不同的策略&#xff0c;例如走路、开车、骑自行车、坐飞机、坐邮轮等等&#xff0c;每一种出行方式都代表着不同的费用和时间&#xff1b;当…

make: *** No rule to make target ‘clean‘. Stop.

项目场景&#xff1a; 在Ubuntu下编写makefile文件编译的时候,出现make: *** No rule to make target ‘clean’. Stop. 问题描述 make: *** No rule to make target ‘clean’. Stop. 解决方案&#xff1a; 原本我makefile文件的名字是MakeFile , 把它改为makefile以后完美运…

腾讯云OpenCloudOS安装ES(elasticsearch7.17.16)

腾讯云OpenCloudOS安装ES&#xff08;elasticsearch7.17.16&#xff09; 下载ES 先从官网下载es的Linux解压包官网地址 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-17-16 下载完成后&#xff0c;将其放置在自己想要放到的路径下 配置ES 解压文件 …

第五季特别篇:一夜杯、游戏之宴 2017.04.26

第五季特别篇&#xff1a;一夜杯、游戏之宴 2017.04.26 OVA 第1话&#xff1a;一夜酒杯 / 一夜杯OVA 第2话&#xff1a;游戏之宴 / 遊戯の宴 OVA 第1话&#xff1a;一夜酒杯 / 一夜杯 遭到独角妖袭击的妖怪夫妇日土和初菜被夏目所救&#xff0c;这对妖怪夫妇制作的酒杯&#xf…

R数据分析:非劣效性研究设计的统计处理方法,原理和实例

在我们经常接触的统计模式中&#xff0c;我们是在寻求推翻原假设&#xff0c;证明差异&#xff0c;这种统计模型在传统的临床试验中&#xff0c;在各种统计推断中已经成为默认了。在传统的临床试验中通常会将一种新的治疗方法与标准治疗或安慰剂进行比较&#xff0c;从而证明这…