C语言矩阵乘法

article/2025/9/23 4:52:39

本篇内容

1)首先介绍了矩阵乘法的基本原理;

2)然后介绍了相对初阶的C语言乘法代码设计;

3)最后根据C语言动态内存规划,提出了更加便捷、优化的代码设计,希望能给大家带来帮助。

更新:

        没有想到一篇简单的矩阵乘法博客居然有这么多的阅读量,RTKLIB(一个开源导航算法程序)中matmul中乘法函数有着更加巧妙的思路,我在RTKLIB—matmul一文中进行了详细介绍。

目录

1.原理

左乘和右乘

乘法原理

2.C语言编写矩阵乘法函数

编写函数(传统形式)

测试

完整代码

3.优化方法

用malloc开辟矩阵

优化后的矩阵乘法代码(仅支持Windows)

优化后的矩阵乘法代码(所有平台通用)

1.原理

左乘和右乘

在线性代数中,矩阵左乘和矩阵右乘是不一样的。

例如:现有矩阵A: \begin{bmatrix} 1 1 \\ 0 1 \end{bmatrix},矩阵B:\begin{bmatrix} 0 1 \\ 1 0 \end{bmatrix}

AB为矩阵B左乘矩阵A:

BA为矩阵B右乘矩阵A:

通过计算发现:左乘和右乘的结果是完全不同的,所以在进行矩阵乘法时,需分清左乘和右乘。

乘法原理

矩阵乘法需要满足的条件:左侧矩阵的列数=右侧矩阵的行数。

结果矩阵的行和列:行数=左侧矩阵的行数,列数=右侧矩阵的列数。

矩阵的相乘过程

以AB为例:

  • A矩阵的第行中的元素和B矩阵第列中的元素对应相乘再相加,结果放在第一行第一列中;
  • A矩阵的第行中的元素和B矩阵第列中的元素对应相乘再相加,结果放在第一行第二列中;
  • A矩阵的第行中的元素和B矩阵第列中的元素对应相乘再相加,结果放在第一行第三列中;
  • ......
  • A矩阵的第行中的元素和B矩阵第列中的元素对应相乘再相加,结果放在第三行第三列中;

最后的结果为:

2.C语言编写矩阵乘法函数

编写函数(传统形式)

矩阵虽然有左乘和右乘之分,但是只要更改传入参数的位置,即可实现左乘和右乘。

核心思想:

  • 利用二维数组将矩阵中的数存放起来,用define定义行和列的数值,方便代码的更改。
  • 定义一个矩阵左乘函数Matrix_left_mul,将矩阵以及矩阵的行列长度传给函数。
  • 利用3个for循环遍历完成计算。

传参的时候可以用数组的形式传,也可以用指针的形式传,二者本质都一样。两种方法都会进行介绍。

1、我们创建两个数组arr1和arr2来存放两个矩阵

(在平时创建二维数组时,不能省略列,可以省略行;但是在矩阵乘法中,行和列的信息都要有)

//左矩阵的行和列
#define COL1 4  
#define ROW1 3
//右矩阵的行和列
#define ROW2 4
#define COL2 3
double arr1[ROW1][COL1] = { 1,2,3,4,5,6,7,8,9,10,11,12};
double arr2[ROW2][COL2] = { 12,11,10,9,8,7,6,5,4,3,2,1};

2、传参以及判断矩阵相称的条件是否成立

利用assert断言条件是否成立,如果不成立系统会提示报错,assert需要包含头文件<assert.h>。

因为数组传参传的是地址,所以函数的类型设置为void即可。

如果不进行判断,传入错误的参数后,二维数组的访问会溢出,返回一个随机数。

数组形式:

void Matrix_left_mul(double arr1[][COL1], double arr2[][COL2], double arr3[][COL2],int row1,int row2,int col1,int col2)
{assert(col1 == row2);//判定左列是否等于右行,需包含头文件}

指针形式: 

void Matrix_left_mul(double(*arr1)[COL1], double (*arr2)[COL2], double(*arr3)[COL2], int row1, int row2, int col1, int col2)
{assert(col1 == row2);//判定左列是否等于右行,assert需包含头文件
}

3、for循环遍历计算

创建三个for循环:

  • 第一个for循环(i):循环左矩阵的i行
  • 第二个for循环(j):循环右矩阵的j列
  • 第三个for循环(k):逐个循环左行k元素×右列k元素,将其结果累加

数组形式:

int i = 0;//行
int j = 0;//列
int k = 0;//行列中,第k个元素相乘
for (i = 0; i < row1; i++)//从第i行开始
{for (j = 0; j < col2; j++)//从第j列开始{for (k = 0; k < col1; k++)//i行元素和j列元素相乘,结果累加{arr3[i][j] += arr1[i][k] * arr2[k][j];}}
}

指针形式:

void Matrix_left_mul(double(*arr1)[COL1], double (*arr2)[COL2], double(*arr3)[COL2], int row1, int row2, int col1, int col2)
{assert(col1 == row2);//判定左列是否等于右行,assert需包含头文件int i = 0;//行int j = 0;//列int k = 0;//行列中,第k个元素相乘for (i = 0; i < row1; i++)//从第i行开始{for (j = 0; j < col2; j++)//从第j列开始{for (k = 0; k < col1; k++)//i行元素和j列元素相乘,结果累加{*(* (arr3 + i)+j) += *(*(arr1 + i) + k) * *(*(arr2 +k) + j);}}}
}

用define定义常量的优点就是灵活,方便使用,后续只需要更改输入矩阵的相关参数即可进行运算。

测试

在VS中,F10进入调试模式,监视arr1,arr2,arr3数组。这里为了监视方便,我们暂时将数组改成整型,数组中的参数都是整数。

 结果完全正确,如果不放心还可以多试验几组数据。

完整代码

数组形式:

#include<stdio.h>
#include<assert.h>//左矩阵的行和列
#define COL1 4  
#define ROW1 3
//右矩阵的行和列
#define ROW2 4
#define COL2 3void Matrix_left_mul(double arr1[][COL1], double arr2[][COL2], double arr3[][COL2],int row1,int row2,int col1,int col2)
{assert(col1 == row2);//判定左列是否等于右行,assert需包含头文件int i = 0;//行int j = 0;//列int k = 0;//行列中,第k个元素相乘for (i = 0; i < row1; i++)//从第i行开始{for (j = 0; j < col2; j++)//从第j列开始{for (k = 0; k < col1; k++)//i行元素和j列元素相乘,结果累加{arr3[i][j] += arr1[i][k] * arr2[k][j];}}}
}int main()
{double arr1[ROW1][COL1] = { 1,2,3,4,5,6,7,8,9,10,11,12};double arr2[ROW2][COL2] = { 12,11,10,9,8,7,6,5,4,3,2,1 };double arr3[ROW1][COL2]={0};//结果矩阵的行等于左矩阵的行,列等于右矩阵的列Matrix_left_mul(arr1, arr2, arr3,ROW1,ROW2,COL1,COL2);return 0;
}

指针形式

#include<stdio.h>
#include<assert.h>//左矩阵的行和列
#define COL1 4  
#define ROW1 3
//右矩阵的行和列
#define ROW2 4
#define COL2 3void Matrix_left_mul(double(*arr1)[COL1], double (*arr2)[COL2], double(*arr3)[COL2], int row1, int row2, int col1, int col2)
{assert(col1 == row2);//判定左列是否等于右行,assert需包含头文件int i = 0;//行int j = 0;//列int k = 0;//行列中,第k个元素相乘for (i = 0; i < row1; i++)//从第i行开始{for (j = 0; j < col2; j++)//从第j列开始{for (k = 0; k < col1; k++)//i行元素和j列元素相乘,结果累加{*(* (arr3 + i)+j) += *(*(arr1 + i) + k) * *(*(arr2 +k) + j);}}}
}int main()
{double arr1[ROW1][COL1] = { 1,2,3,4,5,6,7,8,9,10,11,12 };double arr2[ROW2][COL2] = { 12,11,10,9,8,7,6,5,4,3,2,1 };double arr3[ROW1][COL2] = { 0 };//结果矩阵的行等于左矩阵的行,列等于右矩阵的列Matrix_left_mul(arr1, arr2, arr3, ROW1, ROW2, COL1, COL2);return 0;
}

3.优化方法

从上文中的代码不难看出,以二维数组进行矩阵运算时非常麻烦,需要输入矩阵的维数,不够智能,接下来将介绍更加方便的矩阵运算操作。

用malloc开辟矩阵

double** Make_Matrix(int row,int col)
{int i, j;double** arr = (double**)malloc(sizeof(double*) * row);if (arr != NULL){for (i = 0; i < row; i++){arr[i] = (double*)malloc(sizeof(double) * col);}}return arr;
}

这方面的知识在C语言——malloc开辟矩阵 中进行了详细讲解,这里不再赘述。

优化后的矩阵乘法代码(仅支持Windows)

需要注意:_msize函数返回指针指向的内存,仅支持winodws,其他系统不适用,可用其他方法代替,稍后会给出另一种版本的。

优化后的矩阵乘法需要具备以下功能:

1)自动判断矩阵的维数

2)自动判断矩阵是否满足乘法条件

3)自动为答案矩阵开辟空间

代码如下

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>typedef double Mattype;//为了之后更改方便Mattype** Matrix_Mul(Mattype** arr1, Mattype**arr2)
{if(arr1==NULL||arr2==NULL)exit(-1);int row1 = (int)_msize(arr1) / (int)sizeof(Mattype*);int col1 = (int)_msize(*arr1) / (int)sizeof(Mattype);int row2 = (int)_msize(arr2) / (int)sizeof(Mattype*);int col2 = (int)_msize(*arr2) / (int)sizeof(Mattype);if (col1 != row2)exit(-1);//判断左列是否等于右行double**res=(Mattype**)malloc(sizeof(Mattype*)*row1);if (res == NULL)exit(-1);int i,j,k;for (i = 0; i < row1; i++){res[i] = (Mattype*)malloc(sizeof(Mattype) * col2);//创建新矩阵}for (i = 0; i < row1; i++){for (j = 0; j < col2; j++){res[i][j] = 0.0;//开辟的新矩阵未初始化,计算前需要进行初始化for (k = 0; k < col1; k++){res[i][j] += arr1[i][k] * arr2[k][j];//该部分的计算与前文一致}}}return res;
}

 上述代码中:

  • 先判断传入参数是否为空,然后判断矩阵的维数,这部分在C语言判断矩阵维数中有详细讲解

  • 为了之后更改代码类型更方便,用typedef将double类型重定义为Mattype,之后若是想将代码更改成int,只需要修改此处即可。
  • 新开辟的矩阵未进行初始化,必须在计算前将其初始化

测试

为了更加直观的测试,再写一个Init_Matrix矩阵初始化函数和print打印矩阵的函数:

 初始化矩阵

void Init_Matrix(double** arr,double k)
{int i, j;int row = (int)_msize(arr) / (int)sizeof(double*);int col = (int)_msize(*arr) / (int)sizeof(double);for (i = 0; i < row; i++){for (j = 0; j < col; j++){arr[i][j] = k;}}
}

将矩阵全部元素初始化为k 

打印 

//打印矩阵
void print(double** arr)
{putchar('\n');int i, j, row, col;row = (int)_msize(arr) / (int)sizeof(double*);col = (int)_msize(*arr) / (int)sizeof(double);for (i = 0; i < row; i++){for (j = 0; j < col; j++){printf("%-10lf ", arr[i][j]);}putchar('\n');}
}

与前文一样,函数内部判断矩阵的维数,‘-’表示左对齐,10表示打印10个有效数字。

主函数测试: 

//测试加法double** a1 = Make_Matrix(3, 3);double** a2 = Make_Matrix(3, 3);Init_Matrix(a1, 1);Init_Matrix(a2, 2);double** a3 = Matrix_Plus(a1, a2);print(a3);

 计算结果如下:


优化后的矩阵乘法代码(所有平台通用)

即然无法使用_msize函数,又不想每次计算时重复输入矩阵的维数,不妨在创建矩阵时就将其记录下来,保存在一个结构体中。

typedef struct Matrix
{int row;int col;double** data;
}Matrix,*pMatrix;

如上,data里是我们要存放的数据,依旧使用malloc开辟矩阵,只不过在开辟矩阵前,需要将Matrix中的row和col给定,我们可以将创建矩阵部分封装成一个函数,修改后的内容如下:

Matrix MakeMatrix(int row,int col)
{int i=0;Matrix arr={0};arr.row=row;arr.col=col;arr.data=(double**)malloc(sizeof(double*)*arr.row);if(arr.data==NULL)exit(-1);for(i=0;i<arr.row;i++){arr.data[i]=(double*)malloc(sizeof(double)*arr.col);memset(arr.data[i],0,sizeof(double)*arr.col);}return arr;
}

这样,避免了繁琐的开辟矩阵的过程,也能避免在开辟矩阵时,忘记存入row和col的值,之后将上述乘法矩阵略作修改即可,这样写的好处是代码可以跨平台使用。

Matrix Matrix_Mul(const Matrix left, const Matrix right)
{if (left.col != right.row)exit(-1);//判断左列是否等于右行Matrix res={0};res.data=(double**)malloc(sizeof(double*)*left.row);if (res.data == NULL)exit(-1);int i,j,k;for (i = 0; i < left.row; i++){res.data[i] = (double*)malloc(sizeof(double) * right.col);//创建新矩阵memset(res.data[i],0,sizeof(double) * right.col);//初始化}for (i = 0; i < left.row; i++){for (j = 0; j < right.col; j++){for (k = 0; k < left.col; k++){res.data[i][j] += left.data[i][k] * right.data[k][j];//该部分的计算与前文一致}}}return res;
}


http://chatgpt.dhexx.cn/article/49CkVV8N.shtml

相关文章

c语言之矩阵

矩阵作为线性代数核心内容之一也是刷题人时常会遇到的一种类型。本篇博客简单介绍一下矩阵转置、上三角矩阵以及杨氏矩阵。 1.转置矩阵&#xff1a;输入m行n列的矩阵以n行m列的方式打印出来。只要将数组的行列进行交换即可&#xff0c;并不难想也不难写.&#xff08;相应练习&a…

vue-quill-editor 使用-图片上传

vue 项目开发中&#xff0c;文本编辑器的选择很多&#xff0c;一些熟悉的文本编辑器都可以使用&#xff0c;如UEditor、wangEditor&#xff0c;这里介绍基于 vue 的一个文本编辑器插件 vue-quill-editor 此插件基于 quill&#xff0c;所以使用 cdn 节点方式引用时&#xff0c;…

Error: Cannot find module ‘./XXX.jpg‘ 问题解决 Vue动态显示图片

刚开始学习Vue 在循环输出图片时&#xff0c;浏览器报错Error: Cannot find module ‘./tqwl.jpg’ 这张图片是放在本地文件夹内的 这是图片的展示代码 <el-table-column label"展示" width"180"><template slot-scope"scope"><…

解决js中获取不到图片路径的情况

在写一个todoList作品时&#xff0c;需要点击事件更改图片路径时&#xff0c;遇到了获取不到图片路径的情况 在html中用src“”来获取的图片&#xff0c;凭主观臆想觉得在js中判断它的路径时&#xff0c;没有作用&#xff0c;控制台中显示的不是我们熟悉的路径格式 查阅得知&a…

vue-quill-editor删除服务器多余图片

这几天在做富文本编辑业务&#xff0c;在删除服务器资源方面遇到了问题&#xff0c;网上搜索了很久都没找到办法&#xff0c;现在把自己解决的过程记录如下&#xff1a;思路&#xff1a; 点击工具栏的图标&#xff0c;选取图片&#xff08;不是base64格式&#xff09;上传到服…

将JS代码隐藏在图片中的方法

之前写过利用图片重写的方法清除图片中恶意代码的文章&#xff0c;java清除恶意代码 &#xff0c;但这些图片中的恶意代码是怎么植入进去的呢&#xff0c;有简便方法&#xff0c;也有复杂方法。先来看如下这张图片&#xff0c;是Google的LOGO&#xff0c;是一张完全正常的png图…

VUE-QUILL-EDITOR安装与调节图片大小记录

一、quill-editor安装 安装quill-editor npm install vue-quill-editor --save安装调节图片大小插件(不是必须的&#xff0c;看需求) npm install quill-image-resize-module -S二、 引用&#xff08;两种方式&#xff09; 全局引用 在main.js中填入以下代码 import Vue f…

将js/css脚本放到png图片中的实践。

起因 高级浏览器支持data协议,如: 参考:http://en.wikipedia.org/wiki/Data:_URL <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6…

[JS插件] PhotoSwipe 图片浏览插件使用方法

一、介绍 PhotoSwipe 是专为移动触摸设备设计的相册/画廊.兼容所有iPhone、iPad、黑莓6,以及桌面浏览器.底层实现基于HTML/CSS/JavaScript,是一款免费开源的相册产品。 官方网站&#xff1a;http://photoswipe.com/ 源码下载&#xff1a;https://github.com/dimsemenov/photo…

原生js 图片查看器

将以前用angular 写的 自定义指令 封装成 插件&#xff0c;无需引用jquery、angular。 下载下来即可查看效果。 github网址: GitHub - wzhGitH/imgView: js 图片查看器,H5图片预览(imgView) 百度云下载链接: 百度网盘 请输入提取码 密码: gidq 可实现放大、缩小、拖拽、旋转…

vue-quill-editor拖拽或粘贴的图片上传到服务器回显插入图片后删除生成的Base64图片

问题描述&#xff1a;拖拽或粘贴图片上传到服务器后返回url插入富文本编辑器后&#xff0c;quill默认生成的base64图片链接也同时出现在富文本编辑器中&#xff0c;等于是有两张相同的图片&#xff0c;一张是我们服务器图片&#xff0c;一张是base64格式图片&#xff01;&#…

Unity3D在UI中加入Image图片

在将图片拖入到Assets后发现根本不能将图片拖入到UI中&#xff1a; 新建Image后在Source Image中也不能找到图片&#xff1a; 那是因为你没把图片设置为Sprite&#xff0c;图片只是Texture而已&#xff0c;只能作为贴图使用。 可以将图片设置为Sprite: 这样一切就正常了&#…

纯JS图片查看器

目前支持&#xff1a;放大、缩小&#xff08;含滚轮&#xff09;、旋转、还原 /*** 说明信息* date 2022/10/13 19:46:44* 初始化图片查看器* date 2022/09/21 14:40:06* id 唯一标识&#xff0c;指定id区域内的图片* params 扩展参数&#xff0c;设置图片大小&#xff0c;目前…

vue-quill-editor上传图片

问题&#xff1a; vue-quill-editor富文本编辑器上传图片默认为base64&#xff0c;存入数据库过于过于庞大&#xff0c;使用quill-image-extend-modulevue-quill-editor实现图片地址上传。 解决完之后效果图&#xff1a; 解决思路&#xff1a; 哈哈&#xff0c;第一步当然去…

unity UI 加入image图片

1.新建画布&#xff0c;新建image. 2.我们发现这个时候把Assets里的图片直接拖到Image上是行不通的。原因就是此时的图片没有设为sprite. 处理方法&#xff1a;点击图片&#xff0c;修改Texture Type为Sprite. 下图左边图片为调整后&#xff0c;右边图片为没有调整. 3.调整后就…

js预览本地图片

本地图片在上传服务器前&#xff0c;如何预览效果&#xff1f; 一个方法就可以了&#xff0c;主要是如何从事件中读取文件数据&#xff0c;放到<img>展示 幸运的是&#xff0c;事件中的图片数据已经被转换成<img>可直接识别的内容&#xff0c;直接赋值给<img&g…

python QT 图片缩放,移动

python QT graphicsView控件实现图片的缩放与移动 1、效果图2、界面搭建3、实现方法3.1、构建处理图元的类3.1、绘制图像3.2、拖拽方法实现3.3、缩放方法实现 4、调用方法 1、效果图 选择图片后可在graphicsView窗口中显示选择的图片&#xff0c;可以用鼠标拖拽图片。当鼠标停…

Unity UI修改Image中的图片资源

Unity UI修改Image中的图片资源 一、取资源文件 把资质文件放到Assets文件夹下Resources文件中。 二、在属性面板下修改图片类型 三、在脚本中修改需要修改的资源 Sprite sprite Resources.Load(“Images/2”); //Images文件夹下名为2的图片 Image2.sprite sprite; //修改…

Linux命令scp用法

本文主要讲的是scp用法如果哪里不对欢迎指出&#xff0c;主页https://blog.csdn.net/qq_57785602?typeblog scp 可以在win系统使用&#xff0c;本文百分之八十写的是win系统怎么使用&#xff0c;在本地上到服务器文件,从服务器下载文件到本地 用工具连接到公司服务器时&#x…