前言:在学习Google搜索技术特点时,了解到了Google创始人Larry Page和Sergey Brin利用了稀疏矩阵的计算技巧和多次迭代的方法,将亿级的网页矩阵不断简化,得出网页排名,便深入学习了一下稀疏矩阵算法并优化了自己编写的Java矩阵类
什么是稀疏矩阵?
概念:在一个n*m的矩阵中,当非0的元素远远少于为0的元素,且非0的元素分布没有规律时我们便称它为稀疏矩阵,反之则为稠密矩阵。
稀疏因子δ:当稀疏因子的值<=0.05时我们则称这个矩阵为稀疏矩阵。
公式:
t为非0的元素的个数。
稀疏矩阵压缩
通常我们用二维数组存储矩阵,但是当一个矩阵为稀疏矩阵,大量的0元素存储十分浪费内存,于是我们可以将0元素压缩并将非0元素存储下来,就可以实现稀疏矩阵压缩,存储有效数据。
1,COO压缩(Coordinate)
概念:这个十分简单,利用三个数组,Row(行),Cloumn(列),Value(值),分别将非0数的坐标和值存储下来。
如图(别人的图)
2,CSR压缩(CompressedSparsityRow)
概念:相比COO压缩,CSR节省了更多空间,还是利用三个数组,RowOffset(行偏移),Cloumn(列),Value(值)。所谓的行偏移即某一行第一个元素在Values数组里面的起始偏移值,如1相对于Values数组起始位置偏移0,2相对于Values起始位置偏移2,以此类推。
如图(别人的图)
同理还有CSC 列压缩,和CSR一样就不再赘述了。
除了以上三种矩阵压缩外还有 对角线压缩 (DIA),ELLPACK(ELL),Hybrid (HYB)等几种压缩方式,详情请见:如何高效存储稀疏矩阵?
JAVA代码
以下是本人的小框架代码,GeniusArrayList即ArrayList。
COO压缩
private GeniusMatrix CoordinateSparsityMatrix(GeniusMatrix Matrix){
int X = Matrix.getX();
int Y = Matrix.getY();
int Base = X*Y;
GeniusArrayList<GeniusArrayList<Double>> COOMatrix = new GeniusArrayList<GeniusArrayList<Double>>(3,new GeniusArrayList<Double>());
if(Base==0){
return null;
}
int nNot0 = 0;
double Num;
for(int i=0;i<Y;i++){
for(int j=0;j<X;j++){
if((Num=Matrix.get(i,j))!=0){
nNot0++;
COOMatrix.get(0).add(i*1.0);
COOMatrix.get(1).add(j*1.0);
COOMatrix.get(2).add(Num);
}
}
}
// defualtSparsityFactor = 0.05;
if((nNot0/Base)>defualtSparsityFactor){
return null;
}
else{
return new GeniusMatrix(COOMatrix);
}
}
CSR和CSC压缩
/** Type=0 is compressed row
Type=1 is compressed column **/
public GeniusArrayList<GeniusArrayList<Double>> CompressedSparsityMatrix(int Type){
if(Type==1||Type==0){
return CompressedSparsityMatrix(this,Type);
}
return null;
}
private GeniusArrayList<GeniusArrayList<Double>> CompressedSparsityMatrix(GeniusMatrix Matrix,int Type){
int X = Matrix.getX();
int Y = Matrix.getY();
int Base = X*Y;
if(Base==0){
return null;
}
GeniusArrayList<GeniusArrayList<Double>> COOMatrix = new GeniusArrayList<GeniusArrayList<Double>>(3,new GeniusArrayList<Double>());
int nNot0 = 0;
int Offset = 0;
double Num;
if(Type==1){
int temp = Y;
Y = X;
X = temp;
}
for(int i=0;i<Y;i++){
COOMatrix.get(0).add(Offset*1.0);
int LineNot0 = 0;
for(int j=0;j<X;j++){
if((Num=Matrix.get(i,j))!=0){
nNot0++;
LineNot0++;
COOMatrix.get(1).add(i*1.0);
COOMatrix.get(2).add(Num);
}
}
Offset+=LineNot0;
}
COOMatrix.get(0).add(nNot0*1.0);
if((nNot0/Base)>defualtSparsityFactor){
return null;
}
else{
return COOMatrix;
}
}