用C实现矩阵写的不像前面的链表等数据结构写的顺利,因为矩阵里面的内容多的多,特别是计算部分,着实花了几天时间。目前还有一小部分算法还没有实现,后续会补上。
github源码 文件夹为Matrix,主要有两个文件:Matrix.c 、Matrix.h

矩阵的数据结构的设计

矩阵是存储的一组或多组数据,所以毫无疑问,应使用顺序存储的方式存储数据元素。矩阵最直观的表示方式是用多维数组来表示,缺点在于不够活,使用前必须确定其长度,而且维度变换时十分麻烦。综合了诸多因素,我设计如下的主要使用堆的动态的矩阵数据结构:

1
2
3
4
5
6
7
8
9
10
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列的二维矩阵,
方法一:

1
2
3
4
5
Dshape dshape;
dshape.shape[0] = 0;
dshape.shape[1] = 0;
dshape.shape[2] = 4;
dshape.shape[3] = 5;

方法二:

1
2
3
int a[]={0,0,4,5};
Dshape dshape;
initDshape(&dshape,a);

方法三:

1
2
3
Matrix *a; //a已是一个4行5列的二维矩阵
Dshape dshape;
dshape = a->dshape;

矩阵的创建构造了以下函数:
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
2
Matrix *m = NULL;
m = creatMatrixFromDatas(data,data_len,dshape);

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
2
double elem;
getMatrixElem(m,0,0,1,1,&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
2
Matrix *n = NULL;
n = getSecondOrderMatrixRows(m,0,1); //获取矩阵m的第一行

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
2
3
4
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

1
2
3
4
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

1
2
3
4
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

1
2
3
4
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.删除二维矩阵的指定行数

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
2
3
4
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要一致

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.矩阵的核范数