稀疏矩阵 一般用 (x, y, value) 三元组来存储矩阵非零元素,当矩阵只有少量非零元素时,可以大大减少存储空间。
本文实现的稀疏矩阵只实现了
- 一般矩阵 和 稀疏矩阵 之间的转换
- 读取稀疏元素
- 存储稀疏元素
package matrix;
import java.util.HashMap;
import java.util.Map;
public class SparseMatrix{
Map<String, Double> pairs= new HashMap<>();
int rowNum,colNum;
boolean sizeFixed;
// 如果没有初始化矩阵维数,稀疏矩阵大小可变
public SparseMatrix(){
sizeFixed = false;
}
public SparseMatrix(int r, int c){
sizeFixed = true;
rowNum = r;
colNum = c;
}
public int getRowNum() {
return rowNum;
}
public int getColNum() {
return colNum;
}
// 添加矩阵元素
public void put(int x, int y, double v) throws IndexException{
checkIndex(x,y);
if(v != 0)
pairs.put(SparseIndex(x,y),v);
}
// 获取矩阵元素
public Double get(int x, int y) throws IndexException{
checkIndex(x,y);
Double v = pairs.get(SparseIndex(x,y));
if(v == null) {
v = Double.valueOf(0);
}
return v;
}
// 转换为 一般矩阵
public double[][] toDensity(){
double[][] mat = new double[rowNum][colNum];
for(int i=0; i<rowNum; ++i) {
for(int j=0; j<colNum; ++j) {
try {
mat[i][j] = get(i,j);
} catch (IndexException e) {
}
}
}
return mat;
}
// 从一般矩阵 转换为 稀疏矩阵
static public SparseMatrix fromDensity(double [][] mat){
SparseMatrix sm = new SparseMatrix(mat.length,mat[0].length);
for(int i=0; i<mat.length; ++i) {
for(int j=0; j<mat[0].length; ++j) {
try {
if(mat[i][j] != 0) {
sm.put(i,j,mat[i][j]);
}
} catch (IndexException e) {
}
}
}
return sm;
}
// 把矩阵索引转换成字符串,用于哈希索引
static public String SparseIndex(int x, int y){
return String.valueOf(x)+","+String.valueOf(y);
}
// 检查是否越界,下标越界则抛出异常
private void checkIndex(int x, int y) throws IndexException{
if(sizeFixed) {
if(x >= rowNum || y >= colNum) {
throw new IndexException(
"Index out of bound: ("+String.valueOf(x)+","+String.valueOf(y)+"), "+
"(rowNum,colNum)=("+String.valueOf(rowNum)+","+String.valueOf(colNum)+")."
);
}
}else {
rowNum = rowNum>x+1?rowNum:x+1;
colNum = colNum>y+1?colNum:y+1;
}
}
// 自定义异常
class IndexException extends Exception {
public IndexException (String message){
super (message);
}
public IndexException (String message, Exception cause) {
super(message,cause);
}
}
// 测试
public static void main(String[] args) {
SparseMatrix sm = new SparseMatrix();
try {
sm.put(0, 0, 1.0);
sm.put(2, 1, 2.0);
sm.put(1, 4, -9);
} catch (SparseMatrix.IndexException e) {
e.printStackTrace();
}
double [][] mat = SparseMatrix.fromDensity(sm.toDensity()).toDensity();
for(int i=0; i<sm.getRowNum(); ++i) {
for(int j=0; j<sm.getColNum(); ++j) {
System.out.print(mat[i][j]);
System.out.print(" ");
}
System.out.println();
}
}
}
测试结果:
1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 -9.0
0.0 2.0 0.0 0.0 0.0
为了方便读取某一行更方便,稍作修改:
package matrix;
import java.util.HashMap;
import java.util.Map;
public class SparseMatrix{
protected Map<Integer,Map<Integer, Double>> rows = new HashMap<>();
int rowNum,colNum;
boolean sizeFixed;
Double DEFAULT_VALUE = Double.valueOf(0); //矩阵元素默认值, 在图的邻接矩阵中可设为无穷大
// 如果没有初始化矩阵维数,稀疏矩阵大小可变
public SparseMatrix(){
sizeFixed = false;
}
public SparseMatrix(int r, int c){
sizeFixed = true;
rowNum = r;
colNum = c;
}
public int getRowNum() {
return rowNum;
}
public int getColNum() {
return colNum;
}
public void setDefaultValue(double def) {
DEFAULT_VALUE = Double.valueOf(def);
}
// 添加矩阵元素
public void put(int x, int y, double v) throws IndexException{
checkIndex(x,y);
if(v != DEFAULT_VALUE) {
Map<Integer,Double> row = rows.get(x);
if(null == row) {
row = new HashMap<Integer,Double>();
}
row.put(y, v);
rows.put(x, row);
}
}
// 获取矩阵元素
public Double get(int x, int y) throws IndexException{
checkIndex(x,y);
Map<Integer,Double> row = rows.get(x);
if(row == null) {
return DEFAULT_VALUE;
}
Double v = row.get(y);
if(v == null) {
v = DEFAULT_VALUE;
}
return v;
}
// 转换为 一般矩阵
public double[][] toDensity(){
double[][] mat = new double[rowNum][colNum];
for(int i=0; i<rowNum; ++i) {
for(int j=0; j<colNum; ++j) {
try {
mat[i][j] = get(i,j);
} catch (IndexException e) {
}
}
}
return mat;
}
// 从一般矩阵 转换为 稀疏矩阵
static public SparseMatrix fromDensity(double [][] mat){
SparseMatrix sm = new SparseMatrix(mat.length,mat[0].length);
for(int i=0; i<mat.length; ++i) {
for(int j=0; j<mat[0].length; ++j) {
try {
if(mat[i][j] != 0) {
sm.put(i,j,mat[i][j]);
}
} catch (IndexException e) {
}
}
}
return sm;
}
// 检查是否越界,下标越界则抛出异常
private void checkIndex(int x, int y) throws IndexException{
if(sizeFixed) {
if(x >= rowNum || y >= colNum) {
throw new IndexException(
"Index out of bound: ("+String.valueOf(x)+","+String.valueOf(y)+"), "+
"(rowBound,colBound)=("+String.valueOf(rowNum)+","+String.valueOf(colNum)+")."
);
}
}else {
rowNum = rowNum>x+1?rowNum:x+1;
colNum = colNum>y+1?colNum:y+1;
}
}
// 自定义异常
public class IndexException extends Exception {
public IndexException (String message){
super (message);
}
public IndexException (String message, Exception cause) {
super(message,cause);
}
}
// 测试
public static void main(String[] args) {
SparseMatrix sm = new SparseMatrix();
try {
sm.put(0, 0, 1.0);
sm.put(2, 1, 2.0);
sm.put(1, 4, -9);
} catch (SparseMatrix.IndexException e) {
e.printStackTrace();
}
double [][] mat = SparseMatrix.fromDensity(sm.toDensity()).toDensity();
for(int i=0; i<sm.getRowNum(); ++i) {
for(int j=0; j<sm.getColNum(); ++j) {
System.out.print(mat[i][j]);
System.out.print(" ");
}
System.out.println();
}
}
}
最后,我们来把稀疏矩阵的元素类型变成 Object,就可以适用各种元素类型的矩阵了。在 java 中使用 Object 要比用泛型方便的多,省时又省力。
这么做的初衷是在图优化的最大流问题中,需要存储每条边的上下界,这样一来,每条边对应了两个数,就需要用个对象来表示了。
来,上代码!
package matrix;
import java.util.HashMap;
import java.util.Map;
public class SparseMatrix {
protected Map<Integer, Map<Integer, Object>> rows = new HashMap<>();
int rowNum, colNum;
boolean sizeFixed;
Object DEFAULT_VALUE = null; // 矩阵元素默认值, 在图的邻接矩阵中可设为无穷大
// 如果没有初始化矩阵维数,稀疏矩阵大小可变
public SparseMatrix() {
sizeFixed = false;
}
public SparseMatrix(int r, int c) {
sizeFixed = true;
rowNum = r;
colNum = c;
}
// getter & setter
public int getRowNum() {
return rowNum;
}
public int getColNum() {
return colNum;
}
public void setDefaultValue(Object def) {
DEFAULT_VALUE = def;
}
// 添加矩阵元素
public void put(int x, int y, Object v) throws IndexException, TypeException {
checkIndexAndType(x, y, v);
if (v != DEFAULT_VALUE) {
Map<Integer, Object> row = rows.get(x);
if (null == row) {
row = new HashMap<Integer, Object>();
}
row.put(y, v);
rows.put(x, row);
}
}
// 获取矩阵元素
public Object get(int x, int y) throws IndexException {
checkIndex(x, y);
Map<Integer, Object> row = rows.get(x);
if (row == null) {
return DEFAULT_VALUE;
}
Object v = row.get(y);
if (v == null) {
v = DEFAULT_VALUE;
}
return v;
}
// 转换为 一般矩阵
public Object[][] toDensity() {
Object[][] mat = new Object[rowNum][colNum];
for (int i = 0; i < rowNum; ++i) {
for (int j = 0; j < colNum; ++j) {
try {
mat[i][j] = get(i, j);
} catch (IndexException e) {
e.printStackTrace();
}
}
}
return mat;
}
// 从一般矩阵 转换为 稀疏矩阵
public SparseMatrix fromDensity(Object[][] mat) {
SparseMatrix sm = new SparseMatrix(mat.length, mat[0].length);
sm.setDefaultValue(DEFAULT_VALUE);
try {
for (int i = 0; i < mat.length; ++i) {
for (int j = 0; j < mat[0].length; ++j) {
if (mat[i][j] != DEFAULT_VALUE) {
sm.put(i, j, mat[i][j]);
}
}
}
} catch (IndexException | TypeException e) {
e.printStackTrace();
}
return sm;
}
// 检查是否越界,下标越界则抛出异常
private void checkIndex(int x, int y) throws IndexException {
if (sizeFixed) {
if (x >= rowNum || y >= colNum) {
throw new IndexException("Index out of bound: (" + String.valueOf(x) + "," + String.valueOf(y) + "), "
+ "(rowBound,colBound)=(" + String.valueOf(rowNum) + "," + String.valueOf(colNum) + ").");
}
} else {
rowNum = rowNum > x + 1 ? rowNum : x + 1;
colNum = colNum > y + 1 ? colNum : y + 1;
}
}
// 检查元素类型是否和默认值一致
private void checkIndexAndType(int x, int y, Object o) throws TypeException, IndexException {
checkIndex(x, y);
if (o.getClass() != this.DEFAULT_VALUE.getClass()) {
throw new TypeException("Invalid element type at location(" + x + "," + y + "). Expected: "
+ this.DEFAULT_VALUE.getClass() + ", Got: " + o.getClass());
}
}
// 自定义异常
public class IndexException extends Exception {
public IndexException(String message) {
super(message);
}
public IndexException(String message, Exception cause) {
super(message, cause);
}
}
// 自定义异常
public class TypeException extends Exception {
public TypeException(String message) {
super(message);
}
public TypeException(String message, Exception cause) {
super(message, cause);
}
}
// 测试
public static void main(String[] args) {
SparseMatrix sm = new SparseMatrix();
sm.setDefaultValue(Double.valueOf(0));
try {
sm.put(0, 0, 1.0);
sm.put(2, 1, 2.0);
sm.put(1, 4, -9.0);
} catch (SparseMatrix.IndexException | TypeException e) {
e.printStackTrace();
}
Object[][] mat = sm.fromDensity(sm.toDensity()).toDensity();
for (int i = 0; i < sm.getRowNum(); ++i) {
for (int j = 0; j < sm.getColNum(); ++j) {
System.out.print(mat[i][j]);
System.out.print(" ");
}
System.out.println();
}
}
}