用C实现矩阵写的不像前面的链表等数据结构写的顺利,因为矩阵里面的内容多的多,特别是计算部分,着实花了几天时间。目前还有一小部分算法还没有实现,后续会补上。
github源码 文件夹为Matrix,主要有两个文件:Matrix.c 、Matrix.h
矩阵的数据结构的设计
矩阵是存储的一组或多组数据,所以毫无疑问,应使用顺序存储的方式存储数据元素。矩阵最直观的表示方式是用多维数组来表示,缺点在于不够活,使用前必须确定其长度,而且维度变换时十分麻烦。综合了诸多因素,我设计如下的主要使用堆的动态的矩阵数据结构:
typedef struct Dshape{
int shape[4]; //最多四维
}Dshape;
typedef struct Matrix{
double *array;
Dshape dshape; //数组结构
int length; //长度
int size; //空间大小
}Matrix;
我们看结构体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列的二维矩阵,
方法一:
Dshape dshape;
dshape.shape[0] = 0;
dshape.shape[1] = 0;
dshape.shape[2] = 4;
dshape.shape[3] = 5;
方法二:
int a[]={0,0,4,5};
Dshape dshape;
initDshape(&dshape,a);
方法三:
Matrix *a; //a已是一个4行5列的二维矩阵
Dshape dshape;
dshape = a->dshape;
矩阵的创建构造了以下函数:
1.从数据创建数组
Matrix *creatAsMatrixFromDatas(double *data,int data_len, Dshape dshape);
浅拷贝,数据不开辟新的内存空间,array的地址就指向data的地址。
2.从数据创建数组,深拷贝,数据开辟新的内存空间,从data复制一份数据。
Matrix *creatMatrixFromDatas(double *data,int data_len, Dshape dshape);
使用示例:
Matrix *m = NULL;
m = creatMatrixFromDatas(data,data_len,dshape);
3.创建一个单一值的矩阵
Matrix *creatMatrixFromValue(double value, Dshape dshape);
4.指定初始值和步长,创建等间隔值的矩阵,结束的值根据矩阵的大小变化
Matrix *creatMatrixFromArange(double startVal, double stepVal,Dshape dshape);
5.指定初始值和结束值,创建等间隔的矩阵,间隔根据矩阵的大小变化
Matrix *creatMatrixFromLinspace(double startVal, double endVal,Dshape dshape);
6.创建全0矩阵
Matrix *creatZerosMatrix(Dshape dshape);
7.创建全1矩阵
Matrix *creatOnesMatrix(Dshape dshape);
8.创建二维单位矩阵,要求dshape里行数与列数相等。
Matrix *creatIdentitySecondOrderMatrix(Dshape dshape);
矩阵的shape相关的函数
1.初始化dshape
void initDshape(Dshape *dshape,int *shapeval);
2.修改dshape
int reshape(Matrix *m,Dshape dshape);
3.获取矩阵的维数
int getMatrixNdim(Matrix *m);
矩阵打印的函数
1.打印矩阵的shape
void printShape(Matrix *m);
2.打印矩阵
void printarray(Matrix *m);
矩阵的清空与销毁函数
1.清空矩阵
void clearMatrix(Matrix *m);
2.销毁矩阵
void destroyMatrix(Matrix *m);
获取矩阵的元素
1.获取矩阵的元素
int getMatrixElem(Matrix *m,int dimen0,int dimen1,int dimen2,int dimen3,double *elem);
dimen表示维数,例如要获取3X3的二维矩阵m中第(1,1)个元素(下标从0开始计算):
double elem;
getMatrixElem(m,0,0,1,1,&elem);
函数返回的int为状态指示用的,一般都设定为返回0为操作成功,返回-1为操作失败。
2.修改矩阵的元素
int modifyMatrixElem(Matrix *m,int dimen0,int dimen1,int dimen2,int dimen3,double elem);
3.获取矩阵的连续几行
Matrix *getSecondOrderMatrixRows(Matrix *m,int startRow,int endRow);
下标从0开始计算,返回一个新的矩阵,用法示例:
Matrix *n = NULL;
n = getSecondOrderMatrixRows(m,0,1); //获取矩阵m的第一行
4.获取矩阵的连续几列
Matrix *getSecondOrderMatrixColumes(Matrix *m,int startColume,int endColume);
5.获取矩阵的子矩阵
Matrix *getSecondOrderSubMatrix(Matrix *m,int startRow,int startColume,int endRow,int endColume);
6.获取矩阵删除指定一行和一列之后的子矩阵
Matrix *getSecondOrderLeftSubMatrix(Matrix *m,int row,int colume);
矩阵的基本操作
1.复制矩阵
Matrix *copyMatrix(Matrix *m);
2.转置矩阵
int transposeSecondOrderMatrix(Matrix *m);
3.求二维矩阵的迹
double getSecondOrderMatrixTrace(Matrix *m);
4.交换二维矩阵的两行
int swapSecondOrderMatrixRow(Matrix *m, int row1,int row2);
5.交换二维矩阵的两列
int swapSecondOrderMatrixColume(Matrix *m, int colume1,int colume2);
6.二维矩阵的指定一行加、减、乘、除一个系数k
int kAddSecondOrderMatrixRow(Matrix *m, int row,double k);
int kSubSecondOrderMatrixRow(Matrix *m, int row,double k);
int kMulSecondOrderMatrixRow(Matrix *m, int row,double k);
int kDivSecondOrderMatrixRow(Matrix *m, int row,double k);
7.二维矩阵的指定一列加、减、乘、除一个系数k
int kAddSecondOrderMatrixColume(Matrix *m, int colume,double k);
int kSubSecondOrderMatrixColume(Matrix *m, int colume,double k);
int kMulSecondOrderMatrixColume(Matrix *m, int colume,double k);
int kDivSecondOrderMatrixColume(Matrix *m, int colume,double k);
8.二维矩阵的指定两行元素一一对应相加减乘除:row1 = row1 +-*/ row2
int addSecondOrderMatrixRows(Matrix *m, int row1,int row2);
int subSecondOrderMatrixRows(Matrix *m, int row1,int row2);
int mulSecondOrderMatrixRows(Matrix *m, int row1,int row2);
int divSecondOrderMatrixRows(Matrix *m, int row1,int row2);
9.二维矩阵的指定两列元素一一对应相加减乘除:colume1 = colume1 +-*/ colume2
int addSecondOrderMatrixColumes(Matrix *m, int colume1, int colume2);
int subSecondOrderMatrixColumes(Matrix *m, int colume1, int colume2);
int mulSecondOrderMatrixColumes(Matrix *m, int colume1, int colume2);
int divSecondOrderMatrixColumes(Matrix *m, int colume1, int colume2);
10.删除二维矩阵的指定行数
int deleteSecondOrderMatrixRows(Matrix *m,int startRow,int endRow);
11.删除二维矩阵的指定列数
int deleteSecondOrderMatrixColumes(Matrix *m,int startColume,int endColume);
12.删除二维矩阵的指定一行和一列
int deleteSecondOrderMatrixRowAndColume(Matrix *m,int row,int colume);
13.按行拼接两个矩阵,要求两个矩阵一行的元素数目要相等
int spliceSecondOrderMatrixRow(Matrix *m1,Matrix *m2);
14.按列拼接两个矩阵,要求两个矩阵一列的元素数目要相等
int spliceSecondOrderMatrixColume(Matrix *m1,Matrix *m2);
15.二维矩阵整体加、减、乘、除一个系数k
int kAddMatrix(Matrix *m,double k);
int kSubMatrix(Matrix *m,double k);
int kMulMatrix(Matrix *m,double k);
int kDivMatrix(Matrix *m,double k);
16.两个矩阵相加,得到一个新的矩阵,要求两个矩阵的shape要一致
Matrix *addSecondOrderMatrixs(Matrix *m1,Matrix *m2);
17.两个矩阵相减,得到一个新的矩阵,要求两个矩阵的shape要一致
Matrix *subSecondOrderMatrixs(Matrix *m1,Matrix *m2);
18.两个矩阵求点乘,得到一个新的矩阵,要求两个矩阵的shape要一致
Matrix *dotSecondOrderMatrixs(Matrix *m1,Matrix *m2);
19.两个矩阵求叉乘,m1为m X n矩阵,m2为n X p矩阵,乘积返回一个m X p矩阵
Matrix *mulSecondOrderMatrixs(Matrix *m1,Matrix *m2);
矩阵的高级操作
1.求一个二维方阵的行列式
int detSquareMatrixs(Matrix *m,double *result);
2.求二维方阵中指定元素的代数余子式
int getSquareMatrixElemAlgebraicComplement(Matrix *m,int row,int colume,double *result);
3.求二维方阵中指定一行元素的代数余子式
Matrix *getSquareMatrixRawAlgebraicComplement(Matrix *m,int row);
4.求二维方阵的伴随矩阵
Matrix *getSquareMatrixAdjointMatrix(Matrix *m);
5.求解二维方阵的逆矩阵
Matrix *invSquareMatrix(Matrix *m);
6.求二维矩阵的最简阶梯阵
Matrix *getEchelonMatrix(Matrix *m);
7.求二维矩阵的秩
int getSecondOrderMatrixRank(Matrix *m ,int *rank);
8.求齐次线性方程组AX=0的解,结果为全0矩阵或基础解系构成的矩阵
Matrix *solveHomoLinearEquations(Matrix *A);
9.求非齐次线性方程组AX=B的解,返回NULL表示无解;一列矩阵表示唯一解;多列矩阵中,除了最后一列为特解,前面几列为基础解系
Matrix *solveNonHomoLinearEquations(Matrix *A, Matrix *B);
10.求矩阵的无穷范数
double getMatrixInfNorm(Matrix *m);
11.求矩阵的L0范数
double getMatrixL0Norm(Matrix *m);
12.求矩阵的L1范数
double getMatrixL1Norm(Matrix *m);
13.求矩阵的L2范数
double getMatrixL2Norm(Matrix *m);
14.求矩阵的L21范数
double getMatrixL21Norm(Matrix *m);
To do list
1.求特征值与特征向量
2.奇异值分解(SVD)
3.QR分解
4.最小二乘解
5.矩阵的核范数