您的位置:首页 > 其它

关于稀疏矩阵的压缩存储与基本运算

2012-10-14 19:04 330 查看
对于基本类型数组,比如int数组,如果new了之后没有显式的初始化,数组中的元素值将自动初始化为0,如果是float数组值为0.0,

而对于对象数组将被初始化为null。

稀疏矩阵可以说是存在较多的0(int数组)或null值(对象数组),手动化初始的值较少的二维数组

(或多阶数组),稀疏矩阵的压缩存储是为了节省空间而对这类矩阵进行压缩存储。

所谓的压缩存储是:为多个相同的值分配一个存储空间,为null或0不分配空间。

前者只能是对特殊矩阵(比如对称矩阵或特殊矩阵)的压缩,这里只讨论的是后者。

直接上代码,注释完善

Java代码


import java.util.Arrays;

/**
* 关于稀疏矩阵的压缩存储与基本运算
* 压缩存储只存储矩阵的非空元素
* 数组索引都是从0开始
*/
public class MatrixDemo<E> {

public static void main(String[] args) {
MatrixDemo<Integer> md = new MatrixDemo<Integer>();
Integer[][] M = new Integer[6][7];
M[0][1] = 12; M[0][2] = 9;
M[2][0] = -3; M[2][5] = 14;
M[3][2] = 24; M[4][1] = 18;
M[5][0] = 15; M[5][3] = -7;

//md.compreMatrix(M);

Integer[][] m = new Integer[3][4];
m[0][0]=3;m[0][3]=5;
m[1][1]=-1;m[2][0]=2;
Integer[][] n = new Integer[4][2];
n[0][0]=5;n[0][1]=2; n[1][0]=1;
n[2][0]=-2; n[2][1]=4;
md.multiMatrix(m, n);
System.out.println();
md.multiMatrix(md.compreMatrix(m), md.compreMatrix(n));
}

/**
* 把稀疏矩阵进行压缩
*/
public Matrix compreMatrix(E[][] e){
Matrix matrix = new Matrix();
int mu = e.length;
int nu = e[0].length;
for(int i=0;i<mu;i++){
for(int j=0;j<nu;j++){
if(e[i][j] != null){ //压缩非空元素
Triple<E> triple = new Triple<E>(i,j,e[i][j]);
matrix.add(triple);
}
}
}
matrix.mu = mu;
matrix.nu = nu;

//下面是查看元素
for(int i=0;i<matrix.tu;i++){
Triple<E> t = matrix.data[i];
System.out.print(t.i + " " + t.j + " " + t.e);
System.out.println();
}
System.out.println("----------");
// transMatrix(matrix);
// transMatrix2(matrix);
return matrix;
}

/**
* 压缩矩阵的转置,方法一
* 原理:1.将矩阵的行列对应的值相互交换
* 2.将矩阵的行列下标交换
* 3.重新排列新矩阵的顺序(按行排,即行中非空元素出现的次序)
*/
public Matrix transMatrix(Matrix m){
Matrix t = new Matrix(); //转置后的矩阵
t.mu = m.nu;
t.nu = m.mu;

if(m.tu == 0) return null;
for(int col=0; col<m.nu;col++){ //根据m的列的顺序排列转换矩阵的顺序
for(int p=0;p<m.tu;p++){
if(m.data[p].j == col){
Triple<E> triple = new Triple<E>(m.data[p].j, m.data[p].i, (E)m.data[p].e);
t.add(triple);
}
}
}
System.out.println("矩阵置换方法一");
for(int i=0;i<t.tu;i++){
Triple<E> tr = t.data[i];
System.out.print(tr.i + " " + tr.j + " " + tr.e);
System.out.println();
}
return t;
}

/**
* 压缩矩阵的转置,方法二
* 原理:欲求得转置后的矩阵t的顺序,可以先求得m的每一列的第一个非空元素在
* t中的索引cpot[col],并确定m每一列中的非空元素的个数num[col]
* 公式很容易得到:1.cpot[0] = 0;
* 2.cpot[col] = cpot[col - 1]+ num[col-1] (1<col<m.nu)
*/
public Matrix transMatrix2(Matrix m){
Matrix t = new Matrix(); //转置后的矩阵
t.mu = m.nu;
t.nu = m.mu;

//num和cpot数组的初始化
int[] num = new int[m.nu];
for(int ti=0;ti<m.tu;ti++){ //num初始化
num[m.data[ti].j]++;
}
int[] cpot = new int[m.nu];
cpot[0] = 0;
//求第col列中第一个非空元素在t中的位置
for(int col=1;col<m.nu;col++){ //cpot初始化
cpot[col] = cpot[col-1] + num[col - 1];
}
//进行转换
for(int p=0;p<m.tu;p++){
int col = m.data[p].j;
int q = cpot[col];
Triple<E> triple = new Triple<E>(m.data[p].j, m.data[p].i, (E)m.data[p].e);
t.add(q, triple);
cpot[col]++; //这个位置的下一个位置存储此列的下一个元素
}
//查看
System.out.println("矩阵置换方法二");
for(int i=0;i<t.tu;i++){
Triple<E> tr = t.data[i];
System.out.print(tr.i + " " + tr.j + " " + tr.e);
System.out.println();
}
return t;
}

/**
* 两稀疏矩阵相乘。
* 前提: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;
}

/**
* 压缩矩阵的乘法运算
* 稀疏矩阵进行乘法运算时即使含有0元素也相乘了,
* 为避免这种情况,进行矩阵压缩后再相乘
* 压缩矩阵相乘原理:
* 1.先把稀疏矩阵进行压缩
* 2.求出m与n的每一行的第一个非0元素在m与n中的位置
* 3.遍历m每行的非0元素,
*/
public Matrix multiMatrix(Matrix m,Matrix n){
Matrix q = new Matrix();
q.mu = m.mu;
q.nu = n.nu;
//初始化各行第一个非0元素的位置表
int[] mNum = new int[m.mu];
for(int len=0;len<m.tu;len++){ //每行有多少个非0元素
mNum[m.data[len].i]++;
}
m.rpos = new int[m.mu];
m.rpos[0] = 0;
for(int mRow=1;mRow<m.mu;mRow++){ //每行第一个非0元素在m中的位置
m.rpos[mRow] = m.rpos[mRow-1] + mNum[mRow-1];
}
int[] nNum = new int[n.mu];
for(int len=0;len<n.tu;len++){ //每行有多少个非0元素
nNum[n.data[len].i]++;
}
n.rpos = new int[n.mu];
n.rpos[0] = 0;
for(int nRow=1;nRow<n.mu;nRow++){ //每行第一个非0元素在n中的位置
n.rpos[nRow] = n.rpos[nRow-1] + nNum[nRow-1];
}

//初始化完毕,开始计算
if(m.tu * n.tu !=0){
for(int arow=0;arow<m.mu;arow++){ //一行一行处理
int mlast=0;
if(arow < m.mu-1)
mlast = m.rpos[arow+1];
else
mlast = m.tu;
for(int p=m.rpos[arow];p<mlast;p++){ //从这一行第一个非0索引到最后一个非0索引
int brow = m.data[p].j; //由于m.j=n.i,由此可以求出与此m元素相乘的n元素
int nlast=0;
if(brow < n.mu-1)
nlast = n.rpos[brow+1];
else
nlast = n.tu;
for(int w=n.rpos[brow];w<nlast;w++){
int ccol = n.data[w].j; //同一行的非0元素的列索引必然不相同
int sum = (Integer)m.data[p].e * (Integer)n.data[w].e;
if(sum!=0){ //除去0元素
Triple<Integer> triple = new Triple<Integer>(arow, ccol , sum);
q.add(triple);
}
}
}
}
}
//打印结果
for(int i=0;i<q.tu;i++){
Triple<E> tr = q.data[i];
System.out.print(tr.i + " " + tr.j + " " + tr.e);
System.out.println();
}
return q;
}

/**
* 非空元素用三元组表示
*/
class Triple<E>{
int i; //元素的行下标
int j; //元素的列下标
E e; //元素值
Triple(int i,int j,E e) {
this.i = i;
this.j = j;
this.e = e;
}
}
/**
* 压缩矩阵
*/
class Matrix{
Triple[] data; //存储三元组的数组
int mu; //矩阵的行数
int nu; //矩阵的列数
int tu; //非空个数
int[] rpos; //各行第一个非0元素的位置表
public Matrix(){
this(10);
}

public Matrix(int capacity) {
data = new Triple[capacity];
}

/**
* 是否需要扩充容量
*/
public void ensureCapacity(int minCapacity) {
int oldCapacity = data.length;
if (minCapacity > oldCapacity) { //指定最小容量比原来容量大才扩充
int newCapacity = (oldCapacity * 3) / 2 + 1; //扩充原容量的1.5倍加1
if (newCapacity < minCapacity) //扩充后还是小于要求的最小容量,则扩充容量为最小容量
newCapacity = minCapacity;
data = Arrays.copyOf(data, newCapacity);
}
}
//添加元素到压缩矩阵
public boolean add(Triple triple){
ensureCapacity(tu + 1);
data[tu++] = triple; //size++
return true;
}

//在指定位置添加元素(此添加非彼添加)
public boolean add(int index,Triple triple){
if (index >= tu || index < 0) //检查是否越界
throw new IndexOutOfBoundsException("Index: " + index + ", Size: "+ tu);
ensureCapacity(tu + 1);
data[index] = triple;
tu++;
return true;
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: