在矩阵中,如果很多元素都是零,这种矩阵称为稀疏矩阵,在对稀疏矩阵存储中,常常不存储零元素来节省空间的,矩阵常用操作有转置,加法,乘法等操作,其中转置和乘法最常用。
假设有a和b两个矩阵,其中b是a的转置矩阵,a和b矩阵表示如图:
矩阵结构体
typedef struct
{
//矩阵对应的列值
int col;
//矩阵对应的行值
int row;
//矩阵行和列对应的数值
int value;
} term;
其稀疏矩阵转置快速算法的实现:
核心代码:
//转置稀疏矩阵的核心代码,时间复杂度为(colums+elements)
void fast_transpose(term a[],term b[])
{
//统计转置矩阵中,每一行的元素个数,以及第几行在转置矩阵中的起始位置
int row_terms[MAX_COL],starting_pos[MAX_COL];
int i,j,num_cols=a[0].col,num_items=a[0].value;
b[0].row=num_cols;b[0].col=a[0].row;
b[0].value=num_items;
for(i=0;i<num_cols;i++)
{
row_terms[i]=0;
}
//统计转置矩阵中,每一行的元素个数
for(i=1;i<=num_items;i++)
{
row_terms[a[i].col]++;
}
starting_pos[0]=1;
//第几行在转置矩阵中的起始位置
for(i=1;i<num_cols;i++)
{
starting_pos[i]=starting_pos[i-1]+row_terms[i-1];
}
for(i=1;i<=num_items;i++)
{
j=starting_pos[a[i].col]++;
b[j].row=a[i].col;
b[j].col=a[i].row;
b[j].value=a[i].value;
}
}
时间复杂度为O(columns+elements),对于二维数组矩阵转置的时间复杂度O(columns.elements)快多了。
稀疏矩阵乘法核心算法:
//d是存储a和b两个矩阵相乘的结果的矩阵
void mmult(term a[],term b[],term d[])
{
//totalb,totald,totala分别代表b,d,a的非零元素个数
//row是当前与B的列相乘的A的行号,column是当前与A的行相乘的B的列号
int i,j,column,totalb=b[0].value,totald=0;
int row_a=a[0].row,cols_a=a[0].col;
int totala=a[0].value,cols_b=b[0].col;
//row_begin代表a当前行第一个元素的位置,sum代表两个矩阵对应行和列相乘的结果
int row_begin=1,sum=0,row=a[1].row;
//存放b转置矩阵
term new_b[MAX_TERMS];
if(cols_a!=b[0].row)
{
fprintf(stderr,"Incompatible matrices\n");
exit(1);
}
//转置b矩阵
fast_transpose(b,new_b);
a[totala+1].row=row_a;
new_b[totalb+1].row=cols_b;
new_b[totalb+1].col=0;
for(i=1;i<totala;i++)
{
//记录当前与A的行相乘的B的列号
column=new_b[i].row;
for(j=1;j<totalb;j++)
{
//如果当前行与a[i]的行不相等,说明已得出一行与一列相乘的值,故要进入下一列与当前行相乘
if(a[i].row!=row)
{
storesum(d,&totala,row,column,&sum);
i=row_begin;
for(;new_b[j].row==column;j++);
column=new_b[j].row;
}
//如果当前列与b[j]的列不相等,说明已得出一行与一列相乘的值,故要进入下一列与当前行相乘
else if(b[j].row!=column)
{
storesum(d,&totald,row,column,&sum);
i=row_begin;
column=new_b[j].row;
}
else switch(compare(a[i].col,b[j].col))
{
case -1:
i++;
break;
case 0:
sum+=(a[i++].value+b[j++].value);
break;
case 1:
j++;
break;
}
}
//进入下一行
for(;a[i].row==row;i++);
row=a[i].row;
row_begin=i;
}
d[0].row=a[0].row;
d[0].col=b[0].col;
d[0].value=totald;
}
//将相乘结果存储到d矩阵中
void storesum(term d[],int *totald,int row,int column,int *sum)
{
if(*sum)
{
if(*totald<MAX_TERMS)
{
d[++*totald].row=row;
d[*totald].col=column;
d[*totald].value=*sum;
}
else
{
fprintf(stderr,"Numbers of terms in product exceeds %d\n",MAX_TERMS);
exit(1);
}
}
}
int compare(int a,int b)
{
if(a>b)
return 1;
else if(a==b)
return 0;
else
return -1;
}
**时间复杂度O(∑n0(colsb.termsrow+totalb),nϵ[0,rowa]=O(cols_b.total_a+rows_a.totalb),而标准数组的矩阵相乘复杂度为O(rows_a.cols_a.cols_b),注意:如果在最坏情况下,当totala=cols_a.rows_a或totalb=cols_a.cols_b时,复杂度要比其标准数组的复杂度慢常数因子倍,如果,totala和totalb远小于其最大值时,其复杂度就优于经典算法。