稀疏矩阵

稀疏矩阵

稀疏矩阵的压缩存储

  • 稀疏矩阵的概念

    具有较多零元素且非零元素的分布无规律的矩阵为稀疏矩阵

稀疏矩阵的三元组表存储

  • 存储方式

    所有非零元素组成一个三元组表,按行优先顺序,将稀疏矩阵中的非零元素存放在一个有三元组组成的数组中

    例:

    5×6的稀疏矩阵如下图所示:
    稀疏矩阵A

    其对应的三元组存储表如下图所示:

三元组表

三元组表的基本操作

  1. 定义稀疏矩阵的三元组结点类

    定义属性:行号、列号、值

    定义构造方法:有参、无参

    public class TripleNode {
    	public int row;
    	public int column;
    	public int value;
    	
    	public TripleNode(int row,int column,int value) {
    		this.row = row;
    		this.column = column;
    		this.value = value;
    	}
    	public TripleNode() {
    		this(0,0,0);
    	}
    
    }
    `
    
  2. 定义稀疏矩阵的三元组表类

    定义属性:三元组结点类型的数组、行数、列数、非0元素个数

    定义构造方法:

    (1)为三元组表分配maxsize个存储单元、初始化行数、列数、非0元素个数

    (2)从一个稀疏矩阵创建三元组表:按先行序的原则依次扫描稀疏矩阵的所有元素,把非0元素插入到三元组表中

    //稀疏矩阵三元组表类
    public class SparseMatrix {
    	public TripleNode[] data;
    	public int rows;		//行数
    	public int cols;	   //列数
    	public int nums;		//非0元素个数
    	
    public SparseMatrix(int maxSize) {
    	data = new TripleNode[maxSize];
    	for(int i= 0;i<data.length;i++) {		//为顺序表分配存储单元
    		data[i] = new TripleNode();
    	}
    	rows = 0;
    	cols = 0;
    	nums = 0;
    	
    }
    
    //从一个稀疏矩阵创建三元组表
    public SparseMatrix(int mat[][]) {
    	int count = 0;
    	int k = 0;
    	
    	rows = mat.length;
    	cols = mat[0].length;
    	
    	//遍历二维数组,统计非0元素个数
    	for(int i = 0;i<mat.length;i++) {
    		for(int j = 0;j<mat[i].length;j++) {
    			if(mat[i][j] != 0) {
    				count++;
    			}
    		}
    	}
    	nums = count;
    	//申请三元组空间
    	data = new TripleNode[nums];
    	
    	//把非0元素存到三元组数组中
    	for(int i = 0;i<mat.length;i++) {
    		for(int j = 0;j<mat[i].length;j++) {
    			if(mat[i][j] != 0) {
    				data[k] = new TripleNode(i,j,mat[i][j]);
    				k++;
    			}
    		}
    	}
    }
    
    //打印输出稀疏矩阵
    public void printMatrix() {
    	System.out.println("稀疏矩阵的三元组存储结构:");
    	System.out.println("行数:" + rows +",列数:" + cols + ",非0元素个数:" + nums);
    	System.out.println("行下标" + "\t" + "列下标" + "\t" + "非0元素");
    	for(int i = 0;i < nums; i++) {
    		System.out.println(data[i].row + "\t" + data[i].column + "\t" + data[i].value);
    	}
    }
    

矩阵的普通转置与快速转置

矩阵转置:将矩阵中的每一个行列号互换,使一个m×n的矩阵变成一个n×m的矩阵。

  • 普通转置

    思路:在原来的三元组存放的列序号中,从头开始扫描列号0进行行列交换,找到则存放在一个新的三元组表数组的第0位置中,否则,继续扫描列号1进行行列交换,找到则存放在新三元组表数组的第1位置。以此类推…

    例:
    普通转置

    代码:

    //普通转置
    public SparseMatrix transpose() {
    	SparseMatrix tm = new SparseMatrix(nums);
    	tm.cols = rows;
    	tm.rows = cols;
    	tm.nums = nums;
    	int q = 0;
    	
    	for(int col = 0; col < cols;col++) {
    		for(int p = 0; p < nums;p++) {
    			if(data[p].column == col) {			//从三元组中遍历寻找列号为0的元素开始存放在新的三元组中
    				tm.data[q].row = data[p].column;
    				tm.data[q].column = data[p].row;
    				tm.data[q].value = data[p].value;
    				q++;
    			}
    		}
    	
        }
    	return tm;
    	
    }
    

    测试:

    //测试矩阵转置算法
    public class TestTranspose {
    	public static void main(String[] args) {
    		int t[][] = {{2,0,5,0,0},{4,0,0,3,0},{0,1,4,0,0}};
    		SparseMatrix sm = new SparseMatrix(t);
    		sm.printMatrix();
    	System.out.println("=========矩阵转置后===========");
    	
    	SparseMatrix tm = sm.transpose();
    	tm.printMatrix();
    	}
    }
    
  • 快速转置

    思路:求出原稀疏矩阵的每一列的第一个非0元素在转置后的三元组中的哪个位置,扫描原三元组列上元素依次存放到转置后的三元组位置。稀疏矩阵第一列中的第一个非0元素一定存放在转置后三元组数组的 0 位置,第二列的第一个非0元素的位置等于第一列第一个非0元素在转置后三元组数组的位置加上第一列的非0元素个数,以此类推。

    若能先计算出稀疏矩阵中每列非0元素存储在转置后三元组的某个位置,那只需对原三元组扫描一遍即可把元素放在相应的位置上。

    例【图1】

快速转置1

分析可知,可引入两个数组:一个存放每列的非0元素个数num[] 和存放第col列的第一个非0 元素在转置后三元组的位置cpot[]

即有:

cpot[0] = 0; //第一列的第一个非0 元素一定存放在转置后三元组的第 0 位置。

cpot[col] = cpot[col -1] + num[col -1] (1<=col<=cols -1)

根据上述,对于矩阵A的num和cpot取值如下:

快速转置2

用矩阵A举例:

num[0] =1
cpot[0] = 0 + 0 = 0; //第 0 列的第一个非0元素一定存放在转置后三元组的 0位置

num[1] = 0
cpot[1] = 0 + 1 = 1 //第 0 列 第一个非 0 元素在转置后三元组的位置(0) + 第 0
列的非0元素个数(1)

num[2] = 2
cpot[2] = 1 + 0 = 1 //第 1 列的 第一个非 0 元素在转置后三元组的位置(因为第1列中没有非0
元素,所以再看前一列的第一个非0元素在转置后三元组的位置:1) + 第 1 列的非0元素个数(0)

num[3] = 1
cpot[3] = 1 + 2 = 3 //第 2 列 第一个非 0 元素在转置后三元组的位置(1) + 第 2
列的非0元素个数(2)

num[4] = 1
cpot[4] = 3 + 1 = 4 //第 3 列 第一个非 0 元素在转置后三元组的位置(3) + 第 3
列的非0元素个数(1)

num[5] = 0
cpot[5] = 4 + 1 = 5 //第 4 列 第一个非 0 元素在转置后三元组的位置(4) + 第 4
列的非0元素个数(1)

算法实现:

//快速转置
public SparseMatrix fastranspose() {
	SparseMatrix tm = new SparseMatrix(nums);
	tm.cols = rows;
	tm.rows = cols;
	tm.nums = nums;
	int j = 0;
	int k = 0;
	int[] num;
	int[] cpot;
	if(nums>0) {
		num = new int[cols];
		cpot = new int[cols];
		//每列非0元素个数数组num初始化
		for(int i= 0;i < cols; i++) {		//①
			num[i] = 0;
		}
		//计算每列非0元素个数
		for(int i = 0;i < nums;i++) { 		//②
			/*
			 * 从头扫描原三元组,利用num数组记录在j列的非0元素个数。
			 * 若是扫描到不同的列号时,num[j] = num[j] + 1 = 0(因为前面已经把num中所有的 数据都初始化为0) + 1 = 1。
			 * 若遇到相同的列号则原来的num[j] + 1;
			 * 直至扫描完整个三元组
			 */
			j = data[i].column;			
			num[j]++;
		}
		cpot[0] = 0;
		//计算每一列的第一个非0元素在转置后三元组的位置
		for(int i = 1;i < cols;i++) {		//③
			cpot[i] = cpot[i -1 ] + num[i - 1];
		}
		
		//转置操作
		for(int i = 0 ;i < nums; i++) {		//④
			j = data[i].column;
			k = cpot[j];			//转置后三元组的位置
			tm.data[k].row = data[i].column;
			tm.data[k].column = data[i].row;
			tm.data[k].value = data[i].value;
			/*
			 * 该列的下一个非0元素的存放位置
			 * 存放完第一个数据后,应将cpot[j] = cpot[j] + 1 ;否则遇到同列的元素时会把原来的元素覆盖。
			 */
			cpot[j]++;
			
		}
	}
	return tm;
	
}
  • 普通转置与快速转置的区别

    1、
    (1)普通转置需要对原三元组进行循环扫描;
    (2)快速转置值需对原三元组进行扫描一遍就可将对应的元素放入相应的位置。

    2、
    (1)普通转置的时间复杂度为:O(n×t),n为稀疏矩阵的列数,t为非0元素的个数;
    (2) 快速转置的时间复杂度为:观察上面的算法,可知有4个循环,第一个循环的时间复杂度为 n,第二个为t,第三个为 n-1,第四个为 t。所以 n+t+n-1+t = 2n+2t -1,去掉常数项,得到的时间复杂度为O(n+t),n为稀疏矩阵的列数,t为非0元素个数。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
稀疏矩阵求逆的c语言代码比较复杂,需要使用一些高级的线性代数库或者自己实现一些算法。以下是一个简单的示例代码,仅供参考: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define N 100 // 稀疏矩阵的维数 #define M 10 // 稠密矩阵的字宽 #define MAX_ITER 1000 // 最大迭代次数 #define EPS 1e-6 // 迭代终止精度 // 稀疏矩阵的压缩存储结构 typedef struct { int row; int col; double value; } SparseMatrix; // 稠密矩阵的存储结构 typedef double DenseMatrix[M][M]; // 打印稀疏矩阵 void print_sparse_matrix(SparseMatrix matrix[], int n) { int i; printf("稀疏矩阵:\n"); for (i = 0; i < n; i++) { printf("%d %d %f\n", matrix[i].row, matrix[i].col, matrix[i].value); } } // 打印稠密矩阵 void print_dense_matrix(DenseMatrix matrix, int n) { int i, j; printf("稠密矩阵:\n"); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { printf("%f ", matrix[i][j]); } printf("\n"); } } // 将稀疏矩阵转换为稠密矩阵 void convert_to_dense(SparseMatrix matrix[], int n, DenseMatrix dense_matrix) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { dense_matrix[i][j] = 0.0; } } for (i = 0; i < n; i++) { dense_matrix[matrix[i].row][matrix[i].col] = matrix[i].value; } } // 矩阵乘法 void matrix_multiply(DenseMatrix matrix1, DenseMatrix matrix2, DenseMatrix result, int n) { int i, j, k; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { result[i][j] = 0.0; for (k = 0; k < n; k++) { result[i][j] += matrix1[i][k] * matrix2[k][j]; } } } } // 矩阵加法 void matrix_add(DenseMatrix matrix1, DenseMatrix matrix2, DenseMatrix result, int n) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { result[i][j] = matrix1[i][j] + matrix2[i][j]; } } } // 矩阵减法 void matrix_subtract(DenseMatrix matrix1, DenseMatrix matrix2, DenseMatrix result, int n) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { result[i][j] = matrix1[i][j] - matrix2[i][j]; } } } // 求矩阵的逆 void matrix_inverse(DenseMatrix matrix, DenseMatrix inverse, int n) { int i, j, k, iter; DenseMatrix identity_matrix; double norm, error, alpha; // 初始化迭代矩阵为单位矩阵 for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (i == j) { identity_matrix[i][j] = 1.0; } else { identity_matrix[i][j] = 0.0; } } } // 迭代求逆矩阵 for (iter = 0; iter < MAX_ITER; iter++) { // 计算残差矩阵 DenseMatrix residual_matrix; matrix_multiply(matrix, inverse, residual_matrix, n); matrix_subtract(identity_matrix, residual_matrix, residual_matrix, n); // 计算误差范数 norm = 0.0; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { norm += residual_matrix[i][j] * residual_matrix[i][j]; } } norm = sqrt(norm); if (norm < EPS) { break; } // 计算逆矩阵的增量 DenseMatrix increment_matrix; matrix_multiply(residual_matrix, inverse, increment_matrix, n); alpha = 2.0 / (1.0 + sqrt(1.0 + 4.0 * norm * norm)); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { increment_matrix[i][j] *= alpha; } } // 更新逆矩阵 matrix_add(inverse, increment_matrix, inverse, n); } } int main() { SparseMatrix sparse_matrix[] = { {0, 0, 1.0}, {0, 1, 0.0}, {0, 2, 0.0}, {1, 0, 0.0}, {1, 1, 2.0}, {1, 2, 0.0}, {2, 0, 0.0}, {2, 1, 0.0}, {2, 2, 3.0} }; int n = 3; DenseMatrix dense_matrix; DenseMatrix inverse_matrix; convert_to_dense(sparse_matrix, n, dense_matrix); matrix_inverse(dense_matrix, inverse_matrix, n); print_sparse_matrix(sparse_matrix, n); print_dense_matrix(dense_matrix, n); print_dense_matrix(inverse_matrix, n); return 0; } ``` 上述代码实现了稀疏矩阵求逆的最基本算法,但是存在一些问题,例如没有考虑稀疏矩阵的存储方式、没有对稠密矩阵求逆时的奇异性进行处理等。在实际应用中,需要根据具体情况选择适当的算法,并且需要进行更加严谨的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值