基于C语言的矩阵运算库

最近本着锻炼自己编程能力的目的,捣鼓了一下矩阵运算,看到网上很多的矩阵运算库都是基于C++的,很少有基于C语言的,于是自己想要写一个纯C语言的矩阵运算库。大神看到了不要喷我,我只是个小白。
个人感觉矩阵运算最重要的是数据结构的安排,我看到有些使用C语言编写的矩阵运算库里面的矩阵元素是使用以为数组存储的,这样的好处是确定了矩阵的行和列,直接将矩阵结构中的指针指向对应的数组即可;其形式如下:

typedef struct 
{
  int row;
  int column;
  float* data;
}Matrix_t;

这种一维数组有个缺点就是,运算的时候下标不好操作,需要程序员编程功底较好,对于我这种半吊子,还是使用简单点儿的二维数组吧,下标和各个元素一一对应,方便操作。这里使用动态内存分配的方式来管理矩阵的数据存储的分配和释放。

typedef unsigned int uint16_t;
typedef struct
{
	uint16_t row;
	uint16_t column;
	float **data;
}Matrix_t;

首先创建矩阵,创建矩阵时只需要矩阵的行数和列数,矩阵创建函数会自动根据矩阵的行和列分配一片动态内存给矩阵。为了方便起见,将所有的矩阵元素全部置为0;

/*
create a matrix with 0
*/
Matrix_t create_mat(uint16_t row, uint16_t column)
{
	Matrix_t mat;
	if (row <= 0||column<=0)
	{
		printf("error, in create_mat: row <= 0||column<=0\n");
		exit(1);
	}
	if (row > 0 && column > 0)
	{
		mat.row = row;
		mat.column = column;
		mat.data = (float **)malloc(row*sizeof(float *));//先指针的指针
		if (mat.data == NULL)
		{
			printf("error, in create_mat: mat.data==NULL");
			exit(1);
		}
		uint16_t i;
		for (i = 0; i < row; i++)
		{
			*(mat.data + i) = (float *)malloc(column*sizeof(float));//再分配每行的指针
			if (mat.data[i] == NULL)
			{
			  printf("error, in create_mat: mat.data==NULL");
			  exit(1);
			}
		}
		clear_mat(&mat);
	}
	return mat;
}

矩阵元素置0函数很简单,输入指向对应矩阵的指针,将每个元素设置为0;

/*
set all datas in matrix to zero
*/
void clear_mat(Matrix_t* mat)
{
	uint16_t i, j;
	for (i = 0; i < mat->row; i++)
	{
		for (j = 0; j < mat->column; j++)
		{
			mat->data[i][j] = 0;
		}
	}
}

由于创建矩阵过程中使用malloc函数分配了内存,在矩阵完成运算之后,一定要将矩阵的内存用free释放掉,以防止出现大量内存碎片,使程序崩溃。

/*
free a matrix
*/
void free_mat(Matrix_t *mat)
{
	uint16_t i;
	for (i = 0; i < mat->row; i++)
		free(mat->data[i]);/*释放行*/
	free(mat->data);/*释放头指针*/
}

需要注意的是释放的顺序必须是后申请的内存先释放,否则释放不彻底。
对于矩阵的元素设定需要设计一个函数,该函数将待设置矩阵和一维数组作为输入,将一维数组中的元素一次拷贝到矩阵的二维数组中。之前试过使用指向二维数组的指针,觉得不是很好,还是用一维数组吧。

/*
set datas to the matrix
*/
void set_mat_data(Matrix_t* mat,const float *data)
{
	uint16_t i, j;
	for (i = 0; i < mat->row; i++)
	{
		for (j = 0; j < mat->column; j++)
		{
			mat->data[i][j] = data[i*mat->column+j];
		}
	}
}

矩阵的加减实现方法区别不大,只需对应下标的元素相互加减即可,以下是矩阵相加的情形:

/*
mat=mat1+mat2
*/
Matrix_t add_mat(const Matrix_t* mat1, const Matrix_t* mat2)
{
	
	if (mat1->row != mat2->row)
	{
		printf("error, in add_mat: mat1->row != mat2->row\n");
		exit(1);
	}
	if (mat1->column != mat2->column)
	{
		printf("error, in add_mat: mat1->column != mat2->column\n");
		exit(1);
	}
	Matrix_t mat;
	uint16_t i, j;
	mat = create_mat(mat1->row, mat1->column);
	for (i = 0; i < mat1->row; i++)
	{
		for (j = 0; j < mat1->column; j++)
			mat.data[i][j] = mat1->data[i][j] + mat2->data[i][j];
	}
	return mat;
}

矩阵转置时将矩阵的行和列对换;

/*
transpose the matrix, mat=mat'
*/
Matrix_t transpose_mat(const Matrix_t* mat)//mat'
{
	Matrix_t mat_T;
	mat_T = create_mat(mat->row, mat->column);
	uint16_t i, j;
	for (i = 0; i < mat->row; i++)
	{
		for (j = 0; j < mat->column; j++)
		{
			mat_T.data[i][j] = mat->data[j][i];
		}
	}
	return mat_T;
}

两个矩阵相乘,根据线性代数中的方法,第一个矩阵的第一行乘以第二个矩阵的每一列,得到结果矩阵的第一行的值。。。。。实现起来并不困难。

*
mat=mat1*mat2
*/
Matrix_t mult_mat(const Matrix_t *mat1, const Matrix_t* mat2)
{
	Matrix_t mat;
	if (mat1->column != mat2->row)
	{
		printf("error,In mult_mat: mat1->column != mat2->row\n");
		exit(1);
	}	
	else
	{
		mat = create_mat(mat1->row, mat2->column);
		clear_mat(&mat);
		uint16_t i, j;
		for (i = 0; i < mat1->row; i++)
		{
			for (j = 0; j < mat2->column; j++)
			{
				uint16_t m;
				for (m = 0; m < mat1->column; m++)
				{
					mat.data[i][j] += mat1->data[i][m] * mat2->data[m][j];
				}
			}
		}
	}
	return mat;
}

举证行列式的值的计算是比较复杂的,在线性代数中,一般采用的方法是:将矩阵通过行变换(列主元消去法)变换为上三角矩阵,需要特别注意的是,矩阵两行互换时,行列式的值变号,编程时需要考虑。

/*
get matrix's derterminent value
*/
float det_mat(Matrix_t *m)
{
	uint16_t i, j, n, max_row;
	char swap_f;
	float max, k;
	float det=1;
	swap_f = 0;
	if (m->column != m->row)
	{
		printf("error:In det_mat (m->column != m->row)\n");
		exit(1);
	}
	Matrix_t mat = copy_mat(m);
	for (i = 0; i < mat.row-1; i++)
	{
		max = abs(mat.data[i][i]);
		max_row = i;
		for (j = i + 1; j < mat.row; j++)
		{
			if (max < abs(mat.data[j][i]))
			{
				max = abs(mat.data[j][i]);
				max_row = j;
			}
		}
		if (i != max_row)
		{
			swap_row_mat(&mat, i, max_row);
			swap_f++;
		}
		for (j = i + 1; j < mat.row; j++)
		{
			k = -mat.data[j][i]/mat.data[i][i];
			for (n= 0; n < mat.column; n++)
			{
				mat.data[j][n] = mat.data[i][n] * k + mat.data[j][n];
			}
		}
	}
	if (swap_f%2==1)swap_f = -1;
	else swap_f = 1;
	det = 1;
	for (i = 0; i < mat.column; i++)
		det *= mat.data[i][i];
	det *= swap_f;
	//show_mat("in det_mat: mat_upper",&mat);
	free_mat(&mat);
	return det;
}

矩阵的逆矩阵,这是难点所在,其实编程上容易实现的还是Gauss-Jordan列主元消去法,即:将 A|I 通过行变换为 I| A − 1 A^{-1} A1

/*
get inverse matrix
use main column element of Gauss-Jordan algrithm: A|I  --->  I|A^(-1)
*/
Matrix_t inverse_mat(Matrix_t* m)
{
	if (det_mat(m) == 0)
	{
		printf("error: In inverse_mat(det_mat(mat) == 0)\n");
		exit(1);
	}

	Matrix_t mat = copy_mat(m);
	Matrix_t inv_mat = eye(m->row);
	
	int i, j, n,max_row;
	int swap_f = 0;
	float max,k;
	for (i = 0; i < mat.row - 1; i++)
	{
		max = abs(mat.data[i][i]);
		max_row = i;
		for (j = i + 1; j < mat.row; j++)
		{
			if (max < abs(mat.data[j][i]))
			{
				max = abs(mat.data[j][i]);
				max_row = j;
			}
		}
		if (i != max_row)
		{
			swap_row_mat(&mat, i, max_row);
			swap_row_mat(&inv_mat, i, max_row);
			swap_f++;
		}
		for (j = i + 1; j < mat.row; j++)
		{
			k = -mat.data[j][i] / mat.data[i][i];
			for (n = 0; n < mat.column; n++)
			{
				mat.data[j][n] = mat.data[i][n] * k + mat.data[j][n];
				inv_mat.data[j][n] = inv_mat.data[i][n] * k + inv_mat.data[j][n];
			}
		}
	}

	for (i = 0; i < mat.row; i++)
	{
		k = 1/mat.data[i][i];
		scale_row_mat(&mat,i, k);
		scale_row_mat(&inv_mat, i, k);
	}
	for (i = mat.row-1; i>0; i--)
	{
		for (j = i - 1; j >=0; j--)
		{
			k = -mat.data[j][i] / mat.data[i][i];
			for (n = 0; n < mat.column; n++)
			{
				mat.data[j][n] = mat.data[j][n] + k*mat.data[i][n];
				inv_mat.data[j][n] = inv_mat.data[j][n] + k*inv_mat.data[i][n];
			}
		}
		
	}
	//show_mat("in inverse_mat: mat", &mat);
	//show_mat("in inverse_mat: inv_mat", &inv_mat);
	free_mat(&mat);
	return inv_mat;
}

测试一下:

#include<iostream>
#include"myMatrix.h"

using namespace std;

int main()
{
	Matrix_t mat=create_mat(4,4);
	float data[16] = { 1, 2, 3, 4,
						2, 1, 4, 5,
						5, 4, 3, 5,
						7, 6, 5, 4 };
	set_mat_data(&mat, data);
	show_mat("mat", &mat);
	Matrix_t inv_mat=inverse_mat(&mat);
	show_mat("inv_mat", &inv_mat);

	system("pause");
	return 0;
}

这里写图片描述
以上是实现该矩阵运算库的核心代码了,感兴趣的可以下载本代码:
https://download.csdn.net/download/shuoyueqishilove/10433912

  • 13
    点赞
  • 133
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
请问您需要的是C语言实现的卡尔曼滤波的算法说明,还是完整代码实现呢?以下是基于C语言的卡尔曼滤波算法的实现说明。 卡尔曼滤波是一种常用的估计算法,其目的是通过系统模型和观测模型,从测量值中推算系统状态,最大化状态估计的准确性。卡尔曼滤波的主要思想是利用过去的测量值和系统模型预测下一时刻的状态,接着利用当前时刻的测量值对预测值进行修正,最终得到更加准确的状态估计。 以下是基于C语言的卡尔曼滤波算法的实现说明: 1. 矩阵计算的函数 在C语言中,矩阵的基本运算需要使用函数进行实现。例如,在计算矩阵的逆时,可以使用GSL(GNU Scientific Library)中的gsl_matrix_inverse函数;在计算矩阵的乘法时,可以使用gsl_blas_dgemm函数。因此,在实际使用卡尔曼滤波算法时,需要导入相应的函数,并熟练掌握使用方法。 2. 初始化卡尔曼滤波器 卡尔曼滤波器需要进行初始化,包括初始化系统模型、观测模型和状态向量。系统模型和观测模型需要根据实际情况进行设定,并计算对应的状态转移矩阵和观测矩阵。状态向量则初始化为0,表示初始状态未知。 3. 卡尔曼滤波迭代 卡尔曼滤波迭代过程中,需要进行状态预测和状态修正两个步骤。 状态预测: 根据系统模型和状态向量,可以计算下一时刻的状态预测值。具体而言,需要计算状态转移矩阵A和状态向量x的乘积,并加上过程噪声向量,即 x_k = Ax_{k-1} + w_k 其中,w_k为零均值的高斯白噪声。 状态修正: 由于测量误差的存在,系统的观测值往往与预测值不一致。因此,需要利用测量值对状态进行修正。具体而言,根据观测矩阵和测量向量,可以计算修正向量v_k。同时,需要对修正向量进行加权平均,得出最终的状态估计值。 v_k = z_k - Hx_k K_k = PH^T(HPH^T + R)^{-1} x_k = x_k + K_kv_k 其中,K_k为卡尔曼增益,P为预测误差协方差矩阵,R为观测噪声方差矩阵。 4. 递归卡尔曼滤波 在处理时序数据时,常常需要使用递归卡尔曼滤波算法。递归卡尔曼滤波算法类似于递归神经网络等序列模型,需要对历史数据进行循环迭代。在实现过程中需要注意内存占用和计算效率等问题。 以上是基于C语言的卡尔曼滤波算法的实现说明,希望能对您有所帮助。如果还有问题,欢迎继续提出。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值