一. 引入
我们知道矩阵是一个非常强大的数据结构,在动态规划以及各种图论算法上都有广泛的应用。
当然矩阵有着不足的地方就是空间和时间复杂度都维持在N²上,比如1w个数字建立一个矩阵,在内存中会占用1w*1w=1亿的类型空间,这时就会遇到outofmemory。。。
那么面临的一个问题就是如何来压缩矩阵,当然压缩的方式有很多种,这里就介绍一个顺序表的压缩方式:三元组。
二. 介绍三元组
有时候我们的矩阵中只有零星的一些非零元素,其余的都是零元素,那么我们称之为稀疏矩阵,当然没有绝对的说有多少个零元素才算稀疏。简单说,稀疏矩阵就是非零元素个数远远小于元素个数的矩阵。相对于稀疏矩阵来说,一个不稀疏的矩阵也称作稠密矩阵。
针对上面的这个无规律的存放非零元素,三元组提出了一种方法,就是仅仅记录矩阵中的非零元素以及**它的行,列以及值**N(x,y,v)构成的一个三元组,标识一个稀疏矩阵的话,还要记录该矩阵的阶数,这样我们就将一个二维的变成了一个一维,极大的压缩的存储空间。
这里要注意的就是,三元组的构建采用“行”是从上到下,“列”也是从左到右的方式构建的顺序表。
package xm.math.matrix;
/**
* 三元组处理稀疏矩阵
*
* @author xuming
*/
public class Node {
public int x;
public int y;
public double value;
public Node(int r, int c, double v) {
this.x = r;
this.y = c;
this.value = v;
}
public Node() {
this(0, 0, 0.0);
}
}
其实说到这里也就差不多了,我们只要知道三元组是用来做矩阵压缩的一个顺序存储方式即可,然后知道怎么用三元组表来做一些常规的矩阵运算,好了,既然说已经做成线性存储了,那就做个转置(“行列置换”)试试。
三. 矩阵转置
做行列置换很容易,也就是交换”非零元素”的(x,y)坐标,要注意的就是,原先我们的三元组采用的是”行优先“,所以在做转置的时候需要遵循”列优先“。
public Matrix convertMatrix(Matrix node) {
Matrix matrix = new Matrix();
matrix.rows = node.rows;
matrix.cols = node.cols;
matrix.count = node.count;
for (int col = 0; col < node.cols; col++) {
for (int triple = 0; triple < node.count; triple++) {
Node t = node.nodes.get(triple);
if (col == t.y) {
matrix.nodes.add(new Node(t.y, t.x, t.value));
}
}
}
return matrix;
}
具体完整代码见我的github。https://github.com/shibing624/BlogCode/blob/master/src/main/java/xm/math/matrix/Matrix.java
结果
四.矩阵乘法
已知稀疏矩阵A(m1× n1)和B(m2× n2),求乘积C(m1× n2)。
稀疏矩阵A、B、C 及它们对应的三元组表A.data、B.data、C.data如下图:
计算方法相比大家都知道,线性代数的简单运算,口诀是:A的第一行跟B的第一列分别对应相乘,然后相加;往后依次A的第一行与B的第二列;等等。
/**
* 两稀疏矩阵相乘。
* 前提:1.矩阵元素能够相乘
* 2.一个矩阵的列等于另一个矩阵的行
* 原理:假设两矩阵M与N相乘,前提M的列M.col要等于N的行N.row(反之亦可)
* 得到结果矩阵Q, Q.row=M.row, Q.col = N.col
* 而且Q[i][j] += M[i][k] * N[k][j] 0<i<M.row,0<j<N.col,0<k<M.col或N.row
*/
public Integer[][] multiMatrix(Integer[][] m, Integer[][] n){
Integer[][] q = new Integer[m.length][n[0].length];
for(int i=0;i<m.length;i++){
for(int j=0;j<n[0].length;j++){
int num = 0;
for(int k=0;k<n.length;k++){
num += (m[i][k]==null?0:m[i][k]) * (n[k][j]==null?0:n[k][j]);
}
q[i][j] = num;
}
}
//打印结果
for(int i=0;i<q.length;i++){
for(int j=0;j<q[0].length;j++){
System.out.print(q[i][j]+" ");
}
System.out.println();
}
return q;
}
完整代码见github。
给一个矩阵计算的线上计算器:https://matrixcalc.org/zh/#%7B%7B1,1%7D,%7B2,2%7D%7D%2A%7B%7B1,1%7D,%7B2,2%7D%7D