C语言实现简单的矩阵运算

        最近实验室的一个项目,要求使用c语言,并且需要使用大量的向量、矩阵计算。于是自己突然来了兴趣,想自己用c语言复现一下matlab中各种简单的矩阵运算(包括+、-、.*、*、./、inv、cross、各种按位操作的初等函数计算、行列交换、切片、粘贴、赋值等)。代码分享在如下网盘连接。(2024/12/2修改)

通过网盘分享的文件:catlab
链接: https://pan.baidu.com/s/1GeP76rTOiaJSEkDfWss0Tg?pwd=1111 提取码: 1111
--来自百度网盘超级会员v5的分享

1.定义矩阵

        无论是矩阵,还是向量,都能通过mat来定义。注意,只接受double的矩阵。

        定义矩阵可以通过手动赋值、对角阵、单位阵、全0矩阵、随机数矩阵赋值。

(1)手动赋值:

        mat* make_mat(int row, int column, double data[]);

double arr[25] = { 0.814723686393179,	0.905791937075619,	0.126986816293506,	0.913375856139019,	0.632359246225410,
0.0975404049994095,	0.278498218867048,	0.546881519204984,	0.957506835434298,	0.964888535199277,
0.157613081677548,	0.970592781760616,	0.957166948242946,	0.485375648722841,	0.800280468888800,	
0.141886338627215,	0.421761282626275,	0.915735525189067,	0.792207329559554,	0.959492426392903,
0.655740699156587,	0.0357116785741896,	0.849129305868777,	0.933993247757551,	0.678735154857774 };
mat * mat1 = make_mat(5, 5, arr);

        由于我的mat是通过结构体+指针实现,所以要通过mat* 来定义。同时,在定义时,需要输入row和coluumn。输入的数组是一维的,make_mat函数会有限排布第一列,然后第二列……(就像matlab那样)

(2)对角阵:

        mat* _diag(int n, double data[]);

	double diag_num[5] = { 1, 2, 3, 4, 5 };
	mat* mat1 = _diag(5,diag_num);

        通过对角阵定义矩阵,需要输入行数(列数),以及对角线元素组成的数组。

(3)单位阵:

        mat* _eye(int n);

mat* mat1 = _eye(5);

(4)全0矩阵:

        mat* _zeros(int n);

mat* mat1 = _zeros(5);

(5)随机数矩阵:

        mat* _rand(int row, int column, int max_num, int min_num);

mat* mat1 = _rand(5, 5,-2,4);

        前两个变量表示几行几列,第三个和第四个变量表示最小和最大范围(但因为写的时候不小心,把他写成了int,想用double边界话可能需要配合后续的矩阵乘法)

(6)标准正态初始化:

        mat* _randn(int row, int column);

mat* mat2 = _randn(5, 5);

        两个变量表示行和列。

(7)矩阵复制:

        mat* copy_mat(mat* mat1);

	mat* mat1 = _rand(5, 5,-2,4);
	mat* mat2 = copy_mat(mat1);

2.矩阵展示

(1)打印矩阵

        由于我的矩阵全是用链表写的,在IDE里看也会比较麻烦,因此写了void print_mat(mat* mat1);函数。用法如下:

	mat* mat1 = _rand(5, 5,-2,4);
	print_mat(mat1);

         打印出的效果为:

1.846065     1.355144     0.473281     1.493942     1.046419
2.161199     3.540025     2.194891     1.440657     1.652150
3.642201     -1.685965     3.414228     0.209052     0.146428
2.223273     -0.796594     -1.569506     -0.918546     3.016877
-0.502335     -1.694388     2.877346     -1.076754     -1.518052

(2)行列信息

        可以通过结构体的.row 和 .column查看行列数量。

	mat* mat1 = _rand(5, 4,-2,4);
	printf("%d %d", mat1->row, mat1->column);

3.二元运算

(0)简介

        所有的二元运算,都实现了广播机制(即,一个矩阵可以加上一个行或者列与其相等的向量)。同时,为了能让矩阵与常数的运算和矩阵和矩阵的运算能在相同的函数执行(但c语言本身不支持重载),所以在一些二元运算中加入了参数ismat。二元运算的第二个参数是mat,就填1,是double就填0.不支持填写int或者float。

        由于定义时用到了void*,所以传递double时,需要取其地址。

(1)加法

       

mat* _add(mat* mat1, void* Mat2, int is_mat);

        可以理解为matlab中矩阵加法。mat1必须是mat,但Mat2可以是mat或者是double(注:向量就是行或者列为1的矩阵)。

        eg:

	mat* mat1 = _randn(2, 2); 
	mat* mat2= _randn(2, 2);
	mat* mat3 = _randn(1, 2);
	double m4 = 0.2;
	print_mat(mat1);
	print_mat(mat2);
    print_mat(mat3);
	print_mat(_add(mat1 , mat2,1));
	print_mat(_add(mat1, mat3, 1));
	print_mat(_add(mat1, &m4, 0));

        结果:

-0.756292     -0.172082
2.056440     1.413671

0.917249     2.019091
0.377900     -2.635316

0.888399     0.507530

0.160957     1.847009
2.434340     -1.221646

0.132107     0.335448
2.944839     1.921200

-0.556292     0.027918
2.256440     1.613671

(2)减法

mat* _minus(mat* mat1, void* Mat2, int is_mat);

        eg:

	mat* mat1 = _randn(2, 2); 
	mat* mat2= _randn(2, 2);
	mat* mat3 = _randn(1, 2);
	double m4 = 0.2;
	print_mat(mat1);
	print_mat(mat2);
	print_mat(mat3);
	print_mat(_minus(mat1 , mat2,1));
	print_mat(_minus(mat1, mat3, 1));
	print_mat(_minus(mat1, &m4, 0));

        结果:

-0.583496     1.025506
0.162181     -0.950831

0.250197     1.230143
0.301872     0.043749

-0.448799     0.375249

-0.833694     -0.204637
-0.139690     -0.994580

-0.134697     0.650257
0.610981     -1.326079

-0.783496     0.825506
-0.037819     -1.150831

(3)乘法

        乘法分为按元素相乘、矩阵乘法、叉乘。按元素相乘逻辑同加、减;矩阵乘法就是线性代数中的矩阵乘法。叉乘含有长度为2的向量的叉乘和长度为3的向量的叉乘。

(a)按元素乘法

mat* _mult(mat* mat1, void* Mat2, int is_mat);

        eg:

	mat* mat1 = _randn(2, 2); 
	mat* mat2= _randn(2, 2);
	mat* mat3 = _randn(1, 2);
	double m4 = 0.2;
	print_mat(mat1);
	print_mat(mat2);
	print_mat(mat3);
	print_mat(_mult(mat1 , mat2,1));
	print_mat(_mult(mat1, mat3, 1));
	print_mat(_mult(mat1, &m4, 0));

        结果:

-0.454299     0.031380
1.922025     -2.636598

1.433463     -0.072987
-0.150606     -0.838485

-0.913822     0.183255

-0.651221     -0.002290
-0.289469     2.210747

0.415149     0.005751
-1.756389     -0.483169

-0.090860     0.006276
0.384405     -0.527320

(b)矩阵乘法

mat* mult(mat* mat1, mat* mat2);

        需要注意的是,两种乘法的函数名只有“_”的区别。

        eg:

	mat* mat1 = _randn(2, 2); 
	mat* mat2= _randn(2, 2);
	print_mat(mat1);
	print_mat(mat2);
	print_mat(mult(mat1 , mat2));

        结果:

-0.902194     0.001637
-0.626816     1.224671

0.083982     -0.773807
-0.360299     0.791817

-0.076358     0.699420
-0.493889     1.454750

(c) 叉乘

        叉乘实现的是二维向量和三维向量的叉乘。前者返回一个1*1的mat,后者返回一个三维向量。虽然这里叫做向量,但其实定义的是1*2、1*3或者2*1、3*1的mat.

mat* _cross(mat* mat1, mat* mat2);

        eg_1:3维列向量叉乘

	double v3_1[3] = { 5, 3, 6 };
	double v3_2[3] = { 1,3,9.5 };
	mat* mat1 = make_mat(3,1, v3_1);
	mat* mat2 = make_mat(3, 1, v3_2);
	print_mat(mat1);
	print_mat(mat2);
	print_mat(_cross(mat1, mat2));

        结果1:

5.000000
3.000000
6.000000

1.000000
3.000000
9.500000

10.500000
-41.500000
12.000000

        eg_2:二维行向量叉乘

	double v3_1[3] = { 5, 3 };
	double v3_2[3] = { 1,3 };
	mat* mat1 = make_mat(1,2, v3_1);
	mat* mat2 = make_mat(1, 2, v3_2);
	print_mat(mat1);
	print_mat(mat2);
	print_mat( _cross(mat1,mat2));

        运行结果:

5.000000     3.000000

1.000000     3.000000

12.000000

(4)除法

        除法分成了两个部分:按元素除法和求线性方程组。矩阵求逆在后续

(a)按元素除法

mat* _divide(mat* mat1, void* Mat2, int is_mat);

        eg:

	mat* mat1 = _randn(2, 2);
	mat* mat2 = _randn(2, 2);
	mat* mat3 = _randn(1, 2);
	double m4 = 0.2;
	print_mat(mat1);
	print_mat(mat2);
	print_mat(mat3);
	print_mat(_divide(mat1, mat2, 1));
	print_mat(_divide(mat1, mat3, 1));
	print_mat(_divide(mat1, &m4, 0));

        结果:

-0.979410     0.141544
0.793791     0.367250

-0.420435     0.420065
0.146240     0.803229

-0.858580     0.255745

2.329514     0.336958
5.428000     0.457217

1.140732     0.553459
-0.924540     1.436001

-4.897049     0.707721
3.968957     1.836248

(b)解线性方程组

        采用的算法:列主高斯-乔丹消去。

mat* _solve(mat* mat1, mat* mat2);

        eg:

	double A[4] = { 5, 3, 6, 2 };
	double b[2] = { 8, 9 };
	mat* mat1 = make_mat(2,2,A);
	mat* mat2 = make_mat(2, 1, b);
	print_mat(mat1);
	print_mat(mat2);
	print_mat(_solve(mat1, mat2));

        结果:

5.000000     6.000000
3.000000     2.000000

8.000000
9.000000

4.750000
-2.625000

4.按元素的初等函数

        包括如下函数:

mat* _pow(mat* mat1, int p);
mat* _exp(mat* mat1);
mat* _log(mat* mat1);
mat* _log10(mat* mat1);
mat* _sin(mat* mat1);
mat* _cos(mat* mat1);
mat* _tan(mat* mat1);
mat* _asin(mat* mat1);
mat* _acos(mat* mat1);
mat* _atan(mat* mat1);
mat* _abs(mat* mat1);

        这里实现了幂函数、指数函数、对数函数(10和e为底),三角、反三角、绝对值。

未完待续……

5.一元矩阵运算、操作

(0)简介

        这一部分包括了:矩阵求逆、转置、计算行列式、行列交换

(1)矩阵求逆

        采用的算法为列主高斯-乔丹消去。函数不自带检测条件数的功能,可能因此报错。

mat* _inv(mat* mat1);

        eg:

	double arr[4] = { -5, 3 ,5.4 ,2};
	mat* mat1 = make_mat(2, 2, arr);
	print_mat(mat1);
	print_mat(_inv(mat1));

        结果:

-5.000000     5.400000
3.000000     2.000000

-0.076336     0.206107
0.114504     0.190840

        matlab计算结果:

   -0.0763    0.2061
    0.1145    0.1908

(2)转置

        声明:

mat* _trans(mat* mat1);

        eg:

	double arr[4] = { -5, 3 ,5.4 ,2};
	mat* mat1 = make_mat(2, 2, arr);
	print_mat(mat1);
	print_mat(_trans(mat1));

        结果:

-5.000000     5.400000
3.000000     2.000000

-5.000000     3.000000
5.400000     2.000000

(3)行列式计算

        行列式计算分为两步:第一步,进行LU分解;第二步,提取U对角阵元素并连乘。

(a)LU分解

        LU分解的声明为:

mat_m1_m2* _LU(mat* mat1);

        返回的结果为mat_m1_m2* 类。可以通过.m1获取L矩阵,.m2获取U矩阵。

        eg:

	double arr[25] = { 0.814723686393179,	0.905791937075619,	0.126986816293506,	0.913375856139019,	0.632359246225410,
	0.0975404049994095,	0.278498218867048,	0.546881519204984,	0.957506835434298,	0.964888535199277,
	0.157613081677548,	0.970592781760616,	0.957166948242946,	0.485375648722841,	0.800280468888800,
	0.141886338627215,	0.421761282626275,	0.915735525189067,	0.792207329559554,	0.959492426392903,
	0.655740699156587,	0.0357116785741896,	0.849129305868777,	0.933993247757551,	0.678735154857774 };
	mat* mat1 = make_mat(5, 5, arr);
	mat_m1_m2* ans = _LU(mat1);
	print_mat(mat1);
	print_mat(ans->m1);
	print_mat(ans->m2);
	print_mat(mult(ans->m1, ans->m2));

        结果:

0.814724     0.097540     0.157613     0.141886     0.655741
0.905792     0.278498     0.970593     0.421761     0.035712
0.126987     0.546882     0.957167     0.915736     0.849129
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735

1.000000     0.000000     0.000000     0.000000     0.000000
1.111778     1.000000     0.000000     0.000000     0.000000
0.155865     3.126510     1.000000     0.000000     0.000000
1.121087     4.987539     2.353907     1.000000     0.000000
0.776164     5.228788     2.239765     0.810083     1.000000

0.814724     0.097540     0.157613     0.141886     0.655741
0.000000     0.170055     0.795362     0.264015     -0.693326
0.000000     0.000000     -1.554106     0.068174     2.914614
0.000000     0.000000     0.000000     -0.844122     -3.203886
0.000000     0.000000     0.000000     0.000000     -0.137606

0.814724     0.097540     0.157613     0.141886     0.655741
0.905792     0.278498     0.970593     0.421761     0.035712
0.126987     0.546882     0.957167     0.915736     0.849129
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735

  (b)获取对角线元素

        通过如下函数能够获取矩阵的对角线元素,结果为列向量:

mat* _get_diag(mat* mat1);

        eg:

	double arr[25] = { 0.814723686393179,	0.905791937075619,	0.126986816293506,	0.913375856139019,	0.632359246225410,
	0.0975404049994095,	0.278498218867048,	0.546881519204984,	0.957506835434298,	0.964888535199277,
	0.157613081677548,	0.970592781760616,	0.957166948242946,	0.485375648722841,	0.800280468888800,
	0.141886338627215,	0.421761282626275,	0.915735525189067,	0.792207329559554,	0.959492426392903,
	0.655740699156587,	0.0357116785741896,	0.849129305868777,	0.933993247757551,	0.678735154857774 };
	mat* mat1 = make_mat(5, 5, arr);
	print_mat(mat1);
	print_mat(_get_diag(mat1));

        结果:

0.814724     0.097540     0.157613     0.141886     0.655741
0.905792     0.278498     0.970593     0.421761     0.035712
0.126987     0.546882     0.957167     0.915736     0.849129
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735

0.814724
0.278498
0.957167
0.792207
0.678735

(c)行列式计算

        行列式计算的函数内部封装了LU分解、获取U矩阵对角线元素,以实现求行列式。其声明如下:

double _det(mat* mat1);

       需要注意的是,返回值是double而不是mat*

         eg:

	double arr[25] = { 0.814723686393179,	0.905791937075619,	0.126986816293506,	0.913375856139019,	0.632359246225410,
	0.0975404049994095,	0.278498218867048,	0.546881519204984,	0.957506835434298,	0.964888535199277,
	0.157613081677548,	0.970592781760616,	0.957166948242946,	0.485375648722841,	0.800280468888800,
	0.141886338627215,	0.421761282626275,	0.915735525189067,	0.792207329559554,	0.959492426392903,
	0.655740699156587,	0.0357116785741896,	0.849129305868777,	0.933993247757551,	0.678735154857774 };
	mat* mat1 = make_mat(5, 5, arr);
	print_mat(mat1);
	printf("%f", _det(mat1));

        结果:

0.814724     0.097540     0.157613     0.141886     0.655741
0.905792     0.278498     0.970593     0.421761     0.035712
0.126987     0.546882     0.957167     0.915736     0.849129
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735

-0.025011

        matlab验算:

mat=[0.814724     0.097540     0.157613     0.141886     0.655741
0.905792     0.278498     0.970593     0.421761     0.035712
0.126987     0.546882     0.957167     0.915736     0.849129
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735];
>> det(mat)

ans =

   -0.0250

(4)行列交换

        行列交换是矩阵的初等变换(其余的初等变换,诸如某一行乘以多少,这种需要配合后续会提到的切片来处理)。这里分别展示行变换、列交换的声明:

mat* change_row(mat* mat1, int row1, int row2);
mat* change_column(mat* mat1, int column1, int column2);

        eg:

	double arr[25] = { 0.814723686393179,	0.905791937075619,	0.126986816293506,	0.913375856139019,	0.632359246225410,
	0.0975404049994095,	0.278498218867048,	0.546881519204984,	0.957506835434298,	0.964888535199277,
	0.157613081677548,	0.970592781760616,	0.957166948242946,	0.485375648722841,	0.800280468888800,
	0.141886338627215,	0.421761282626275,	0.915735525189067,	0.792207329559554,	0.959492426392903,
	0.655740699156587,	0.0357116785741896,	0.849129305868777,	0.933993247757551,	0.678735154857774 };
	mat* mat1 = make_mat(5, 5, arr);
	print_mat(mat1);
	print_mat(change_row(mat1,0,2));
	print_mat(change_column(mat1, 1, 4));

        结果:

0.814724     0.097540     0.157613     0.141886     0.655741
0.905792     0.278498     0.970593     0.421761     0.035712
0.126987     0.546882     0.957167     0.915736     0.849129
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735

0.126987     0.546882     0.957167     0.915736     0.849129
0.905792     0.278498     0.970593     0.421761     0.035712
0.814724     0.097540     0.157613     0.141886     0.655741
0.913376     0.957507     0.485376     0.792207     0.933993
0.632359     0.964889     0.800280     0.959492     0.678735

0.814724     0.655741     0.157613     0.141886     0.097540
0.905792     0.035712     0.970593     0.421761     0.278498
0.126987     0.849129     0.957167     0.915736     0.546882
0.913376     0.933993     0.485376     0.792207     0.957507
0.632359     0.678735     0.800280     0.959492     0.964889

6.尚未在此说明的功能(但代码里包含)

(1)按行、列求最大、最小、均值、和

(2)切片

(3)替换

(4)横向、纵向拼接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值