C#,数值计算,矩阵相乘的斯特拉森(Strassen’s Matrix Multiplication)分治算法与源代码
Volker Strassen
1 矩阵乘法
矩阵乘法是机器学习中最基本的运算之一,对其进行优化是多种优化的关键。通常,将两个大小为N X N的矩阵相乘需要N^3次运算。从那以后,我们在更好、更聪明的矩阵乘法算法方面取得了长足的进步。沃尔克·斯特拉森于1969年首次发表了他的算法。这是第一个证明基本O(n^3)运行时不是optiomal的算法。
Strassen算法的基本思想是将A和B分为8个子矩阵,然后递归计算C的子矩阵。这种策略称为分而治之。
2 伪代码
- 如上图所示,将矩阵A和B划分为大小为N/2 x N/2的4个子矩阵。
- 递归计算7个矩阵乘法。
- 计算C的子矩阵。
- 将这些子矩阵组合到我们的新矩阵C中
3 复杂性
- 最坏情况时间复杂度:Θ(n^2.8074)
- 最佳情况时间复杂度:Θ(1)
- 空间复杂度:Θ(logn)
年青时正在发愁的 Volker Strassen
4 算法的详细解释
矩阵相乘在进行3D变换的时候是经常用到的。在应用中常用矩阵相乘的定义算法对其进行计算。这个算法用到了大量的循环和相乘运算,这使得算法效率不高。而矩阵相乘的计算效率很大程度上的影响了整个程序的运行速度,所以对矩阵相乘算法进行一些改进是必要的。
我们先讨论二阶矩阵的计算方法。
对于二阶矩阵
a11 a12 b11 b12
A = a21 a22 B = b21 b22
先计算下面7个量(1)
x1 = (a11 + a22) * (b11 + b22);
x2 = (a21 + a22) * b11;
x3 = a11 * (b12 - b22);
x4 = a22 * (b21 - b11);
x5 = (a11 + a12) * b22;
x6 = (a21 - a11) * (b11 + b12);
x7 = (a12 - a22) * (b21 + b22);
再设C = AB。根据矩阵相乘的规则,C的各元素为(2)
c11 = a11 * b11 + a12 * b21
c12 = a11 * b12 + a12 * b22
c21 = a21 * b11 + a22 * b21
c22 = a21 * b12 + a22 * b22
比较(1)(2),C的各元素可以表示为(3)
c11 = x1 + x4 - x5 + x7
c12 = x3 + x5
c21 = x2 + x4
c22 = x1 + x3 - x2 + x6
根据以上的方法,我们就可以计算4阶矩阵了,先将4阶矩阵A和B划分成四块2阶矩阵,分别利用公式计算它们的乘积,再使用(1)(3)来计算出最后结果。
本文给出了多种算法,大家自己选择吧。
5 源程序
using System;
using System.Text;namespace Legal.Truffer.Algorithm
{/// <summary>/// 矩阵相乘的斯特拉森(V. Strassen)方法/// </summary>public static class Matrix_Calculator{#region [4x4]x[4x4]矩阵相乘的斯特拉森(V. Strassen)方法(快速算法)// 计算2X2矩阵private static void Multiply2X2(out double fOut_11, out double fOut_12, out double fOut_21, out double fOut_22,double f1_11, double f1_12, double f1_21, double f1_22,double f2_11, double f2_12, double f2_21, double f2_22){double x1 = ((f1_11 + f1_22) * (f2_11 + f2_22));double x2 = ((f1_21 + f1_22) * f2_11);double x3 = (f1_11 * (f2_12 - f2_22));double x4 = (f1_22 * (f2_21 - f2_11));double x5 = ((f1_11 + f1_12) * f2_22);double x6 = ((f1_21 - f1_11) * (f2_11 + f2_12));double x7 = ((f1_12 - f1_22) * (f2_21 + f2_22));fOut_11 = x1 + x4 - x5 + x7;fOut_12 = x3 + x5;fOut_21 = x2 + x4;fOut_22 = x1 - x2 + x3 + x6;}// 计算4X4矩阵public static Matrix Multiply4x4(Matrix a, Matrix b){//double c[7,4] = new double[7,4];double c_0_0, c_0_1, c_0_2, c_0_3;double c_1_0, c_1_1, c_1_2, c_1_3;double c_2_0, c_2_1, c_2_2, c_2_3;double c_3_0, c_3_1, c_3_2, c_3_3;double c_4_0, c_4_1, c_4_2, c_4_3;double c_5_0, c_5_1, c_5_2, c_5_3;double c_6_0, c_6_1, c_6_2, c_6_3;// (ma11 + ma22) * (mb11 + mb22)Multiply2X2(out c_0_0, out c_0_1, out c_0_2, out c_0_3,a[0] + a[10], a[1] + a[11], a[4] + a[14], a[5] + a[15],b[0] + b[10], b[1] + b[11], b[4] + b[14], b[5] + b[15]);// (ma21 + ma22) * mb11Multiply2X2(out c_1_0, out c_1_1, out c_1_2, out c_1_3,a[8] + a[10], a[9] + a[11], a[12] + a[14], a[13] + a[15],b[0], b[1], b[4], b[5]);// ma11 * (mb12 - mb22)Multiply2X2(out c_2_0, out c_2_1, out c_2_2, out c_2_3,a[0], a[1], a[4], a[5],b[2] - b[10], b[3] - b[11], b[6] - b[14], b[7] - b[15]);// ma22 * (mb21 - mb11)Multiply2X2(out c_3_0, out c_3_1, out c_3_2, out c_3_3,a[10], a[11], a[14], a[15],b[8] - b[0], b[9] - b[1], b[12] - b[4], b[13] - b[5]);// (ma11 + ma12) * mb22Multiply2X2(out c_4_0, out c_4_1, out c_4_2, out c_4_3,a[0] + a[2], a[1] + a[3], a[4] + a[6], a[5] + a[7],b[10], b[11], b[14], b[15]);// (ma21 - ma11) * (mb11 + mb12)Multiply2X2(out c_5_0, out c_5_1, out c_5_2, out c_5_3,a[8] - a[0], a[9] - a[1], a[12] - a[4], a[13] - a[5],b[0] + b[2], b[1] + b[3], b[4] + b[6], b[5] + b[7]);// (ma12 - ma22) * (mb21 + mb22)Multiply2X2(out c_6_0, out c_6_1, out c_6_2, out c_6_3,a[2] - a[10], a[3] - a[11], a[6] - a[14], a[7] - a[15],b[8] + b[10], b[9] + b[11], b[12] + b[14], b[13] + b[15]);return new Matrix(4, 4, new double[4 * 4] {c_0_0 + c_3_0 - c_4_0 + c_6_0,c_0_1 + c_3_1 - c_4_1 + c_6_1,c_2_0 + c_4_0,c_2_1 + c_4_1,c_0_2 + c_3_2 - c_4_2 + c_6_2,c_0_3 + c_3_3 - c_4_3 + c_6_3,c_2_2 + c_4_2,c_2_3 + c_4_3,c_1_0 + c_3_0,c_1_1 + c_3_1,c_0_0 - c_1_0 + c_2_0 + c_5_0,c_0_1 - c_1_1 + c_2_1 + c_5_1,c_1_2 + c_3_2,c_1_3 + c_3_3,c_0_2 - c_1_2 + c_2_2 + c_5_2,c_0_3 - c_1_3 + c_2_3 + c_5_3});}#endregion#region 基于Strassen算法的矩阵“分治”乘法(只支持维度为2的幂次的方阵相乘。)private static Matrix create(Matrix input, int r1, int r2, int c1, int c2){Matrix res = new Matrix(r2 - r1, c2 - c1);for (int i = r1, ii = 0; i <= r2 && ii < r2 - r1; i++, ii++){for (int j = c1, jj = 0; j < c2 && jj < c2 - c1; j++, jj++){res[ii, jj] = input[i, j];}}return res;}public static Matrix Multipy(Matrix A, Matrix B, int len, int r1 = 0, int c1 = 0){if (len == 1){return new Matrix(1, 1,new double[1] { A[0] * B[0] });}int lend2 = len / 2;Matrix a = create(A, r1, r1 + lend2, c1, c1 + lend2);Matrix e = create(B, r1, r1 + lend2, c1, c1 + lend2);Matrix b = create(A, r1, r1 + lend2, c1 + lend2, len);Matrix f = create(B, r1, r1 + lend2, c1 + lend2, len);Matrix c = create(A, r1 + lend2, len, c1, c1 + lend2);Matrix g = create(B, r1 + lend2, len, c1, c1 + lend2);Matrix d = create(A, r1 + lend2, len, c1 + lend2, len);Matrix h = create(B, r1 + lend2, len, c1 + lend2, len);Matrix p1 = a * (f - h); // multi(a, sub(f, h, lend2), 0, 0, lend2); Matrix p2 = (a + b) * h; // multi(add(a, b, lend2), h, 0, 0, lend2);Matrix p3 = (c + d) * e; // multi(add(c, d, lend2), e, 0, 0, lend2);Matrix p4 = d * (g - e); // multi(d, sub(g, e, lend2), 0, 0, lend2);Matrix p5 = (a + d) * (e + h); // multi(add(a, d, lend2), add(e, h, lend2), 0, 0, lend2);Matrix p6 = (b - d) * (g + h); // multi(sub(b, d, lend2), add(g, h, lend2), 0, 0, lend2);Matrix p7 = (a - c) * (e + f); // multi(sub(a, c, lend2), add(e, f, lend2), 0, 0, lend2);Matrix r = (((p5 + p4) + p6) - p2); // sub(add(add(p5, p4, lend2), p6, lend2), p2, lend2);Matrix s = p1 + p2; // add(p1, p2, lend2);Matrix t = p3 + p4; // add(p3, p4, lend2);Matrix u = (p5 + p1) - (p3 + p7);// sub(add(p5, p1, lend2), add(p3, p7, lend2), lend2);Matrix rr = new Matrix(len, len);for (int j = 0; j < lend2; j++){for (int jj = 0; jj < lend2; jj++){rr[j, jj] = r[j, jj];}}for (int j = 0; j < lend2; j++){for (int jj = 0; jj < lend2; jj++){rr[j, jj + lend2] = s[j, jj];}}for (int j = 0; j < lend2; j++){for (int jj = 0; jj < lend2; jj++){rr[j + lend2, jj] = t[j, jj];}}for (int j = 0; j < lend2; j++){for (int jj = 0; jj < lend2; jj++){rr[j + lend2, jj + lend2] = u[j, jj];}}return rr;}#endregion#region 基于Strassen矩阵乘法的递归分治算法/// <summary>/// 基于Strassen矩阵乘法的递归分治算法/// </summary>/// <param name="n"></param>/// <param name="A"></param>/// <param name="B"></param>/// <returns></returns>public static Matrix Strassen(int n, Matrix A, Matrix B){//2-order if (n == 2){return A * B;}int N = n / 2;Matrix A11 = new Matrix(N, N);Matrix A12 = new Matrix(N, N);Matrix A21 = new Matrix(N, N);Matrix A22 = new Matrix(N, N);Matrix B11 = new Matrix(N, N);Matrix B12 = new Matrix(N, N);Matrix B21 = new Matrix(N, N);Matrix B22 = new Matrix(N, N);//将矩阵A和B分成阶数相同的四个子矩阵,即分治思想。 for (int i = 0; i < n / 2; i++){for (int j = 0; j < n / 2; j++){A11[i, j] = A[i, j];A12[i, j] = A[i, j + n / 2];A21[i, j] = A[i + n / 2, j];A22[i, j] = A[i + n / 2, j + n / 2];B11[i, j] = B[i, j];B12[i, j] = B[i, j + n / 2];B21[i, j] = B[i + n / 2, j];B22[i, j] = B[i + n / 2, j + n / 2];}}//Calculate M1 = (A0 + A3) × (B0 + B3) Matrix M1 = Strassen(N, A11 + A22, B11 + B22);//Calculate M2 = (A2 + A3) × B0 Matrix M2 = Strassen(N, A21 + A22, B11);//Calculate M3 = A0 × (B1 - B3) Matrix M3 = Strassen(N, A11, B12 - B22);//Calculate M4 = A3 × (B2 - B0) Matrix M4 = Strassen(N, A22, B21 - B11);//Calculate M5 = (A0 + A1) × B3 Matrix M5 = Strassen(N, A11 + A12, B22);//Calculate M6 = (A2 - A0) × (B0 + B1) Matrix M6 = Strassen(N, A21 - A11, B11 + B12);//Calculate M7 = (A1 - A3) × (B2 + B3) Matrix M7 = Strassen(N, A12 - A22, B21 + B22);//Calculate C0 = M1 + M4 - M5 + M7 Matrix C11 = (M1 + M4) + (M7 - M5);//Calculate C1 = M3 + M5 Matrix C12 = M3 + M5;//Calculate C2 = M2 + M4 Matrix C21 = M2 + M4;//Calculate C3 = M1 - M2 + M3 + M6 Matrix C22 = (M1 - M2) + (M3 + M6);Matrix C = new Matrix(n, n);for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){C[i, j] = C11[i, j];C[i, j + N] = C12[i, j];C[i + N, j] = C21[i, j];C[i + N, j + N] = C22[i, j];}}return C;}#endregion}
}
相关文章:

C#,数值计算,矩阵相乘的斯特拉森(Strassen’s Matrix Multiplication)分治算法与源代码
Volker Strassen 1 矩阵乘法 矩阵乘法是机器学习中最基本的运算之一,对其进行优化是多种优化的关键。通常,将两个大小为N X N的矩阵相乘需要N^3次运算。从那以后,我们在更好、更聪明的矩阵乘法算法方面取得了长足的进步。沃尔克斯特拉森于1…...

linux:文件的创建/删除/复制/移动/查看/查找/权限/类型/压缩/打包
关于文件的关键词 创建 touch 删除 rm 复制 cp 权限 chmod 移动 mv 查看内容 cat(全部); head(前10行); tail(末尾10行); more,less 查找 find 压缩 gzip ; bzip 打包 tar 编辑 sed 创建文件 格式: touch 文件名 删除文件 复制文件 移动文件 查看文…...

SQL Server查询计划操作符——查询计划相关操作符(3)
7.3. 查询计划相关操作符 19)Collapse:该操作符对更改处理进行优化。当执行一个更改时,其能被劈成(用Split操作符)一个删除和一个插入。其参数列包含一个确定一系列键值字段的GROUP BY:()子句。如果查询处理器遇到删除和插入相同键值的毗邻行,其将用一个更高效的更改操作…...

【Notepad++】Notepad++如何删除包含某个字符串所在的行
Notepad如何删除包含某个字符串所在的行 一,简介二,操作方法三,总结 一,简介 在使用beyoundcompare软件进行对比的时候,常常会出现一些无关紧要的地方,且所在行的内容是变化的,不方便进行比较&…...

Android 来电白名单 只允许联系人呼入电话
客户需求只允许通讯录中联系人可以呼入电话。参考自带的黑名单实现 CallsManager.java类中的onSuccessfulIncomingCall方法有一些过滤器,可以仿照黑名单的方式添加自己的过滤器。 packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java …...

【计算机网络】lab3 802.11 (无线网络帧)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀计算机网络_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2.…...

单片机(MCU)-简单认识
简介: 内部集成了CPU,RAM,ROM,定时器,中断系统,通讯接口等一系列电脑的常用硬件功能。 单片机的任务是信息采集(依靠传感器),处理(依靠CPU)&…...

全面教程:Nacos 2.3.2 启用鉴权与 MySQL 数据存储配置
全面教程:Nacos 2.3.2 启用鉴权与 MySQL 数据存储配置 1. 配置 Nacos 开启鉴权功能 1.1 修改 application.properties 配置文件 在 Nacos 2.3.2 中,开启鉴权功能需要修改 conf/application.properties 文件。按照以下方式配置: # 开启鉴权…...

软件23种设计模式完整版[附Java版示例代码]
一、什么是设计模式 设计模式是在软件设计中反复出现的问题的通用解决方案。它们是经过多次验证和应用的指导原则,旨在帮助软件开发人员解决特定类型的问题,提高代码的可维护性、可扩展性和重用性。 设计模式是一种抽象化的思维方式,可以帮助开发人员更好地组织和设计他们…...

国标GB28181-2022视频平台EasyGBS小知识:局域网ip地址不够用怎么解决?
在局域网中,IP地址不足的问题通常不会在小型网络中出现,但在拥有超过255台设备的大型局域网中,就需要考虑如何解决IP地址不够用的问题了。 在企业局域网中,经常会出现私有IP地址如192.168.1.x到192.168.1.255不够用的情况。由于0…...

PHP 循环控制结构深度剖析:从基础到实战应用
PHP 循环控制结构深度剖析:从基础到实战应用 PHP提供了多种控制结构,其中循环控制结构是最常见的结构之一。它们使得我们能够高效地重复执行一段代码,直到满足某个条件为止。本文将从PHP循环的基础知识出发,逐步分析其在实际项目…...

vue的属性绑定
重建一个新的项目 App.vue main.js HelloWorld.vue 属性绑定 双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令 <template><div v-bind:id"dynamicId" v-bind:class"dynamicClass">…...

FFmpeg音视频流媒体,视频编解码性能优化
你是不是也有过这样一个疑问:视频如何从一个简单的文件变成你手机上快速播放的短片,或者是那种占满大屏幕的超高清大片?它背后的法宝,离不开一个神奇的工具——FFmpeg!说它强大,完全不为过,它在…...

16_Redis Lua脚本
Redis Lua脚本是Redis提供的一种强大的扩展机制。 1.Redis Lua脚本介绍 1.1 基本概念 Redis Lua脚本允许开发者将一段Lua语言编写的代码发送给Redis服务器执行。这项功能自Redis 2.6版本引入以来,为用户提供了强大的灵活性和扩展能力,使得可以在Redis内部直接处理复杂的业…...

Redis为 List/Set/Hash 的元素设置单独的过期时间
一.业务简介 我们知道,Redis 里面暂时没有接口给 List、Set 或者 Hash 的 field 单独设置过期时间,只能给整个列表、集合或者 Hash 设置过期时间。 这样,当 List/Set/Hash 过期时,里面的所有 field 元素就全部过期了。但这样并不…...

鸿蒙中调整应用内文字大小
1、ui Stack() {Row() {ForEach([1, 2, 3, 4], (item: number) > {Text().width(3).height(20).backgroundColor(Color.Black).margin(item 2 ? { left: 8 } : item 3 ? { left: 7 } : { left: 0 })})}.width(97%).justifyContent(FlexAlign.SpaceBetween).padding({ ri…...

计算机网络之---防火墙与入侵检测系统(IDS)
防火墙与入侵检测系统(IDS) 防火墙(Firewall) 和 入侵检测系统(IDS, Intrusion Detection System) 都是网络安全的关键组件,但它们的作用、功能和工作方式有所不同。 防火墙 防火墙是网络安全的一种设备或软件&#…...

KG-CoT:基于知识图谱的大语言模型问答的思维链提示
一些符号定义 知识图谱实体数量: n n n 知识图谱中关系类型数量: m m m 三元组矩阵: M ∈ { 0 , 1 } n n m \textbf{M} \in \{0, 1\}^{n \times n \times m} M∈{0,1}nnm, M i j k 1 M_{ij}^k 1 Mijk1则说明实体 i i i和实…...

【JMeter】多接口关联
1. 同一线程组内,如何实现多接口关联 非加密的值 前置接口的返回单条数据使用Json提取器提取前置接口的返回多条数据使用Json提取器+逻辑控制器Loop Controller前置接口的返回多条数据使用Json提取器+逻辑控制器forEach加密的值 前置接口的返回值使用Beanshell后置提取器存储为…...

2020 年 12 月青少年软编等考 C 语言五级真题解析
目录 T1. 漫漫回国路思路分析T2. 装箱问题思路分析T3. 鸣人和佐助思路分析T4. 分成互质组思路分析T1. 漫漫回国路 2020 年 5 月,国际航班一票难求。一位在美国华盛顿的中国留学生,因为一些原因必须在本周内回到北京。现在已知各个机场之间的航班情况,求问他回不回得来(不考…...

前端实时显示当前在线人数的实现
实时显示当前在线人数的实现 本文档提供了在网页上实时显示当前在线人数的多种实现方法,包括使用 WebSocket 实现实时更新和轮询方式实现非实时更新。 方法一:使用 WebSocket 实现实时更新 服务器端设置 通过 Node.js 和 WebSocket 库(如 …...

Linux第一个系统程序---进度条
进度条---命令行版本 回车换行 其实本质上回车和换行是不同概念,我们用一张图来简单的理解一下: 在计算机语言当中: 换行符:\n 回车符:\r \r\n:回车换行 这时候有人可能会有疑问:我在学习C…...

vscode 无法使用npm, cmd命令行窗口可以正常执行
解决方法: 执行命令获得命令的位置 get-command npm 得到如下 然后删除或者修改 npm.ps1文件 让其不能使用就行。然后重启vscode即可。 pnpm 同理即可 另外加速源 国内镜像源(淘宝): npm config set registry https://regist…...

Leetcode 967 Numbers With Same Consecutive Differences
题意 给定n,代表整数的长度,给定k代表两个相邻数字之间的间隔。求所有的值构成的组合 题目链接 https://leetcode.com/problems/numbers-with-same-consecutive-differences/description/ 题解 dfs,有k位置要选,第一个位置我…...

node.js中实现token的生成与验证
Token(令牌)是一种用于在客户端和服务器之间安全传输信息的加密字符串。在Web开发中,Token常用于身份验证和授权,确保用户能够安全地访问受保护的资源。 作用与意义 身份验证:Token可以用来验证用户的身份࿰…...

[C++11]_[初级]_[工作线程如何监听主线程条件变量wait_for方法的使用]
场景 在开发多线程程序时,有时候需要启动一个线程来监听外部进程的执行情况,并且在指定时间如果还没运行结束就强制结束外部线程。那么C标准库有这种监听线程并能在超时时提示的方法吗? 说明 在C11的<condition_variable>里就可以用…...

Openstack持久存储-Swift,Cinder,Manila三者之间的区别
总结不易,给个三连吧!!! 补充: 文件共享存储服务Manila 在OpenStack生态系统中,Cinder和Manila分别提供了两种不同类型的存储服务,类似于传统的SAN(存储区域网络)和NAS&…...
深度学习第三弹:python入门与线性表示代码
一、python入门 1.熟悉基础数据结构——整型数据,浮点型数据,列表,字典,字符串;了解列表及字典的切片,插入,删除操作。 list1 [1, 2, 3, 4, 5] for each in list1:print(each) print(list1[1…...

解决报错记录:TypeError: vars() argument must have __dict__ attribute
解决报错记录:manager_pyplot_show vars(manager_class).get(“pyplot_show“) TypeError: vars() argument must 1.问题引申 在pycharm中调用matplotlib函数批量绘制维度图时,抛出异常: manager_pyplot_show vars(manager_class).get(&…...

SpringBoot 原理篇(day14)
配置优先级 SpringBoot 中支持三种格式的配置文件: 配置文件优先级排名(从高到低): properties 配置文件yml 配置文件yaml 配置文件 注意事项 虽然 springboot 支持多种格式配置文件,但是在项目开发时,推荐…...