C实现矩阵数据结构与计算
用C实现矩阵写的不像前面的链表等数据结构写的顺利,因为矩阵里面的内容多的多,特别是计算部分,着实花了几天时间。目前还有一小部分算法还没有实现,后续会补上。
github源码 文件夹为Matrix,主要有两个文件:Matrix.c 、Matrix.h
矩阵的数据结构的设计
矩阵是存储的一组或多组数据,所以毫无疑问,应使用顺序存储的方式存储数据元素。矩阵最直观的表示方式是用多维数组来表示,缺点在于不够活,使用前必须确定其长度,而且维度变换时十分麻烦。综合了诸多因素,我设计如下的主要使用堆的动态的矩阵数据结构:
1 | typedef struct Dshape{ |
我们看结构体Matrix,里面首先是一个double型的指针*array,这用于指向的矩阵元素存储块的首地址。在刚开始的设计版本中,我把指针的类型设置为void型,这样*array可以指向任何类型的数据块,但是在存取时需要类型判断和转换,比较麻烦,而且在后面矩阵的一些算法如求行列式等就更为麻烦,所以最终设定Matrix的元素类型为double型。
Dshape dshape定义了矩阵的结构,可以看到struct Dshape里定义的是一个int型的大小为4的数组,这个数组用来存储矩阵的结构信息,4个int型代表维数,最多支持4维。例如,一个2X2(二行二列)的二维矩阵的dshape为{0,0,2,2}。要注意的是一个设定:一维矩阵,即只有一行,假设这一行的元素有3个,那么该一维矩阵的dshape为{0,0,0,3},而不是{0,0,1,3}。还有一种情况,一列元素,假设有n个,那么,该列的dshape为{0,0,n,1}。把int型数组用struct包装一下,这是一个小技巧,好处是struct可以直接整体赋值,在代码中就会简洁一些,不必再用循环依次对数组赋值。存放多维矩阵元素只用一块连续的存储区域,而矩阵的结构由dshape来决定,最大的好处在于,更改数组的维数、结构非常的容易,不需要改矩阵元素的存储区域,只要改一下dshape的值就可以了。
int length保存了矩阵*array里数据元素的长度, int size则保存了矩阵*array所占空间的大小。大部分情况下,这两个值是相等的,但在有些时候,不相等,比如说做删除矩阵里的元素操作时,我只改变了length的大小,而size不变。
矩阵的创建
矩阵创建时首先要确定dshape,dshape有三种初始化方式:
例如要创建的矩阵的结构为4行5列的二维矩阵,
方法一:
1 | Dshape dshape; |
方法二:
1 | int a[]={0,0,4,5}; |
方法三:
1 | Matrix *a; //a已是一个4行5列的二维矩阵 |
矩阵的创建构造了以下函数:
1.从数据创建数组
1 | Matrix *creatAsMatrixFromDatas(double *data,int data_len, Dshape dshape); |
浅拷贝,数据不开辟新的内存空间,array的地址就指向data的地址。
2.从数据创建数组,深拷贝,数据开辟新的内存空间,从data复制一份数据。
1 | Matrix *creatMatrixFromDatas(double *data,int data_len, Dshape dshape); |
使用示例:
1 | Matrix *m = NULL; |
3.创建一个单一值的矩阵
1 | Matrix *creatMatrixFromValue(double value, Dshape dshape); |
4.指定初始值和步长,创建等间隔值的矩阵,结束的值根据矩阵的大小变化
1 | Matrix *creatMatrixFromArange(double startVal, double stepVal,Dshape dshape); |
5.指定初始值和结束值,创建等间隔的矩阵,间隔根据矩阵的大小变化
1 | Matrix *creatMatrixFromLinspace(double startVal, double endVal,Dshape dshape); |
6.创建全0矩阵
1 | Matrix *creatZerosMatrix(Dshape dshape); |
7.创建全1矩阵
1 | Matrix *creatOnesMatrix(Dshape dshape); |
8.创建二维单位矩阵,要求dshape里行数与列数相等。
1 | Matrix *creatIdentitySecondOrderMatrix(Dshape dshape); |
矩阵的shape相关的函数
1.初始化dshape
1 | void initDshape(Dshape *dshape,int *shapeval); |
2.修改dshape
1 | int reshape(Matrix *m,Dshape dshape); |
3.获取矩阵的维数
1 | int getMatrixNdim(Matrix *m); |
矩阵打印的函数
1.打印矩阵的shape
1 | void printShape(Matrix *m); |
2.打印矩阵
1 | void printarray(Matrix *m); |
矩阵的清空与销毁函数
1.清空矩阵
1 | void clearMatrix(Matrix *m); |
2.销毁矩阵
1 | void destroyMatrix(Matrix *m); |
获取矩阵的元素
1.获取矩阵的元素
1 | int getMatrixElem(Matrix *m,int dimen0,int dimen1,int dimen2,int dimen3,double *elem); |
dimen表示维数,例如要获取3X3的二维矩阵m中第(1,1)个元素(下标从0开始计算):
1 | double elem; |
函数返回的int为状态指示用的,一般都设定为返回0为操作成功,返回-1为操作失败。
2.修改矩阵的元素
1 | int modifyMatrixElem(Matrix *m,int dimen0,int dimen1,int dimen2,int dimen3,double elem); |
3.获取矩阵的连续几行
1 | Matrix *getSecondOrderMatrixRows(Matrix *m,int startRow,int endRow); |
下标从0开始计算,返回一个新的矩阵,用法示例:
1 | Matrix *n = NULL; |
4.获取矩阵的连续几列
1 | Matrix *getSecondOrderMatrixColumes(Matrix *m,int startColume,int endColume); |
5.获取矩阵的子矩阵
1 | Matrix *getSecondOrderSubMatrix(Matrix *m,int startRow,int startColume,int endRow,int endColume); |
6.获取矩阵删除指定一行和一列之后的子矩阵
1 | Matrix *getSecondOrderLeftSubMatrix(Matrix *m,int row,int colume); |
矩阵的基本操作
1.复制矩阵
1 | Matrix *copyMatrix(Matrix *m); |
2.转置矩阵
1 | int transposeSecondOrderMatrix(Matrix *m); |
3.求二维矩阵的迹
1 | double getSecondOrderMatrixTrace(Matrix *m); |
4.交换二维矩阵的两行
1 | int swapSecondOrderMatrixRow(Matrix *m, int row1,int row2); |
5.交换二维矩阵的两列
1 | int swapSecondOrderMatrixColume(Matrix *m, int colume1,int colume2); |
6.二维矩阵的指定一行加、减、乘、除一个系数k
1 | int kAddSecondOrderMatrixRow(Matrix *m, int row,double k); |
7.二维矩阵的指定一列加、减、乘、除一个系数k
1 | int kAddSecondOrderMatrixColume(Matrix *m, int colume,double k); |
8.二维矩阵的指定两行元素一一对应相加减乘除:row1 = row1 +-*/ row2
1 | int addSecondOrderMatrixRows(Matrix *m, int row1,int row2); |
9.二维矩阵的指定两列元素一一对应相加减乘除:colume1 = colume1 +-*/ colume2
1 | int addSecondOrderMatrixColumes(Matrix *m, int colume1, int colume2); |
10.删除二维矩阵的指定行数
1 | int deleteSecondOrderMatrixRows(Matrix *m,int startRow,int endRow); |
11.删除二维矩阵的指定列数
1 | int deleteSecondOrderMatrixColumes(Matrix *m,int startColume,int endColume); |
12.删除二维矩阵的指定一行和一列
1 | int deleteSecondOrderMatrixRowAndColume(Matrix *m,int row,int colume); |
13.按行拼接两个矩阵,要求两个矩阵一行的元素数目要相等
1 | int spliceSecondOrderMatrixRow(Matrix *m1,Matrix *m2); |
14.按列拼接两个矩阵,要求两个矩阵一列的元素数目要相等
1 | int spliceSecondOrderMatrixColume(Matrix *m1,Matrix *m2); |
15.二维矩阵整体加、减、乘、除一个系数k
1 | int kAddMatrix(Matrix *m,double k); |
16.两个矩阵相加,得到一个新的矩阵,要求两个矩阵的shape要一致
1 | Matrix *addSecondOrderMatrixs(Matrix *m1,Matrix *m2); |
17.两个矩阵相减,得到一个新的矩阵,要求两个矩阵的shape要一致
1 | Matrix *subSecondOrderMatrixs(Matrix *m1,Matrix *m2); |
18.两个矩阵求点乘,得到一个新的矩阵,要求两个矩阵的shape要一致
1 | Matrix *dotSecondOrderMatrixs(Matrix *m1,Matrix *m2); |
19.两个矩阵求叉乘,m1为m X n矩阵,m2为n X p矩阵,乘积返回一个m X p矩阵
1 | Matrix *mulSecondOrderMatrixs(Matrix *m1,Matrix *m2); |
矩阵的高级操作
1.求一个二维方阵的行列式
1 | int detSquareMatrixs(Matrix *m,double *result); |
2.求二维方阵中指定元素的代数余子式
1 | int getSquareMatrixElemAlgebraicComplement(Matrix *m,int row,int colume,double *result); |
3.求二维方阵中指定一行元素的代数余子式
1 | Matrix *getSquareMatrixRawAlgebraicComplement(Matrix *m,int row); |
4.求二维方阵的伴随矩阵
1 | Matrix *getSquareMatrixAdjointMatrix(Matrix *m); |
5.求解二维方阵的逆矩阵
1 | Matrix *invSquareMatrix(Matrix *m); |
6.求二维矩阵的最简阶梯阵
1 | Matrix *getEchelonMatrix(Matrix *m); |
7.求二维矩阵的秩
1 | int getSecondOrderMatrixRank(Matrix *m ,int *rank); |
8.求齐次线性方程组AX=0的解,结果为全0矩阵或基础解系构成的矩阵
1 | Matrix *solveHomoLinearEquations(Matrix *A); |
9.求非齐次线性方程组AX=B的解,返回NULL表示无解;一列矩阵表示唯一解;多列矩阵中,除了最后一列为特解,前面几列为基础解系
1 | Matrix *solveNonHomoLinearEquations(Matrix *A, Matrix *B); |
10.求矩阵的无穷范数
1 | double getMatrixInfNorm(Matrix *m); |
11.求矩阵的L0范数
1 | double getMatrixL0Norm(Matrix *m); |
12.求矩阵的L1范数
1 | double getMatrixL1Norm(Matrix *m); |
13.求矩阵的L2范数
1 | double getMatrixL2Norm(Matrix *m); |
14.求矩阵的L21范数
1 | double getMatrixL21Norm(Matrix *m); |
To do list
1.求特征值与特征向量
2.奇异值分解(SVD)
3.QR分解
4.最小二乘解
5.矩阵的核范数
本文标题:C实现矩阵数据结构与计算
文章作者:Mr Bluyee
发布时间:2018-09-16
最后更新:2019-07-15
版权声明:The author owns the copyright, please indicate the source reproduced.