先简单介绍一下稀疏矩阵:
稀疏矩阵中每一个元素都是一个三元组,有行数r,列数c,以及一个数据域elem
template<class T>
struct Triple
{
int r, c;
T elem;
Triple() :r(0), c(0), elem(0) {}
Triple(int rs, int cs, T elems) :r(rs), c(cs), elem(elems) {}
};
稀疏矩阵用一个vector容器存储每一个非零元,每一个稀疏矩阵都有相对应的行数和列数还有非零元的个数
template<class T>
class SpareMatrix
{
vector < Triple<T> >triList;
int rows, cols, num;//行,列,非零元个数
public:
SpareMatrix();//无参构造函数
SpareMatrix(Triple<T>* tlist, int rs, int cs, int n);//有参构造函数
}
template<class T>
SpareMatrix<T>::SpareMatrix()
{
rows = 0;
cols = 0;
num = 0;
}
template<class T>
SpareMatrix<T>::SpareMatrix(Triple<T>* tlist, int rs, int cs, int n)
{
this->rows = rs;
this->cols = cs;
this->num = n;
for (int i = 0; i < n; i++)
{
this->triList.push_back(tlist[i]);
}
}
这里重点讲三种相乘算法:
1.退步型
将两个稀疏矩阵转换为正常的二维数组,用vector容器实现,按照传统方法相乘
代码:
template<class T>
SpareMatrix<T> SpareMatrix<T>::mult1(SpareMatrix& B)//退步型
{
if (this->rows != B.cols)
{
cerr << "不能相乘!";
exit(1);
}
vector< vector<int> >temp1(this->rows, vector<int>(this->cols, 0));
for (int i = 0; i < this->num; i++)
{
temp1[this->triList[i].r][this->triList[i].c] = this->triList[i].elem;
}
vector< vector<int> >temp2(B.rows, vector<int>(B.cols, 0));
for (int j = 0; j < B.num; j++)
{
temp2[B.triList[j].r][B.triList[j].c] = B.triList[j].elem;
}
vector< vector<int> >temp3(this->rows, vector<int>(B.cols, 0));
int cc = 0;
for (int i = 0; i < this->rows; i++)
{
for (int j = 0; j < B.cols; j++)
{
int sum = 0;
for (int k = 0; k < this->cols; k++)
{
sum += temp1[i][k] * temp2[k][j];
}
temp3[i][j] = sum;
}
}
vector< Triple<T> >t;
for (int i = 0; i < this->rows; i++)
{
for (int j = 0; j < B.cols; j++)
{
if (temp3[i][j] != 0)
{
Triple<T> tt(i, j, temp3[i][j]);
t.push_back(tt);
}
}
}
Triple<T>* t1 = new Triple<T>[t.size()];
for (int k = 0; k < t.size(); k++)
{
t1[k] = t[k];
}
SpareMatrix<T> a(t1, this->rows, B.cols, t.size());
delete[]t1;
return a;
}
2.改进型
思路:
思路:
设有两个稀疏矩阵
步骤一:
分别算出两个稀疏矩阵每一行第一个非零元的个数以及其相对应的位置:Arnum[]记录矩阵A在该行的非零元个数,Arpos[]记录矩阵A在该行的非零元位置,Brnum[]记录矩阵B在该行的非零元个数,Brpos[]矩阵B在该行的非零元位置
步骤二:
要知道,最后得到的矩阵为 ,所以遍历A的每一行,也就是遍历了C的每一行
一行一行处理A,也就是一行一行处理C:
C有k列,设置一个辅助数组ctemp[k],用来记录C每一行j列的元素的大小
遍历第i行,找到第i行中矩阵A的首非零元位置Arpos[i],遍历此行的所有非零元。在此位置的非零元有相对应的行r和列c,显然行数r就是i(这个不重要),然后找到矩阵B中c行的首非零元位置Brpos[c],遍历此行的每个非零元,将非零元的元素elem分别与矩阵A当前位置的非零元的元素elem相乘,相乘的结果累加放在ctemp[B当前位置非零元的列],遍历完B的c行后继续遍历A在i行的下一个非零元,重复上述步骤,直到遍历完矩阵A在i行的所有非零元
总共有k列,得到的ctemp[1],ctemp[2]......ctemp[k]就是矩阵C中第i行k列的非零元的元素elem,每遍历完A的一行,就可以把相对应的元素放在矩阵C中了,可以先用一个vector容器将矩阵C的每一个非零元先存起来
然后继续遍历矩阵A的i+1行,直到遍历到m行
代码:
template<class T>
SpareMatrix<T> SpareMatrix<T>::mult2(SpareMatrix& B)//改进矩阵相乘
{
if (this->rows != B.cols)
{
cerr << "不能相乘!";
exit(1);
}
//计算A与B矩阵的每一行非零元位置
vector<int>Arnum(this->rows,0);//A每一行非零元个数
vector<int>Arpos(this->rows);//A每一行第一个非零元位置
vector<int>Brnum(B.rows,0);//B每一行非零元个数
vector<int>Brpos(B.rows);//B每一行第一个非零元位置
for (int i = 0; i < this->num; i++)
{
Arnum[this->triList[i].r]++;
}
Arpos[0] = 0;
for (int row = 1; row < this->rows; row++)
{
Arpos[row] = Arnum[row - 1] + Arpos[row - 1];
}
for (int i = 0; i < B.num; i++)
{
Brnum[B.triList[i].r]++;
}
Brpos[0] = 0;
for (int row = 1; row < B.rows; row++)
{
Brpos[row] = Brnum[row - 1] + Brpos[row - 1];
}
//矩阵相乘
vector< Triple<T> >t;
int p = 0;
if (this->num * B.num != 0)//不是零矩阵
{
for (int i = 0; i < this->rows; i++)//一行一行处理
{
vector<int>ctemp(B.cols, 0);
for (int j = Arpos[i];j < Arpos[i]+Arnum[i]; j++)
{
for (int k = Brpos[this->triList[j].c]; k < Brpos[this->triList[j].c]+Brnum[this->triList[j].c]; k++)
{
int m = this->triList[j].elem * B.triList[k].elem;
ctemp[B.triList[k].c] += m;
}
}
for (int h = 0; h < B.cols; h++)
{
if (ctemp[h] != 0)
{
Triple<T> c(i, h, ctemp[h]);
t.push_back(c);
}
}
}
}
Triple<T>* t1 = new Triple<T>[t.size()];
for (int k = 0; k < t.size(); k++)
{
t1[k] = t[k];
}
SpareMatrix<T>C(t1, this->rows, B.cols, t.size());
return C;
}
3.改进型二--将矩阵B转置后与A相乘
分别算出两个稀疏矩阵每一行第一个非零元的个数以及其相对应的位置
先将B转置,然后利用两个循环,外层遍历A的每一行,内层遍历B的每一行,设置一个遍历temp初始值为0
在内层循环遍历中,用两个int型变量p,q分别记录两个矩阵A,B每行的首非零元位置,再进行一次循环,如果当前遍历到的两个非零元列数相等,那么就相乘加到temp中;如果A的列数大于B的列数,那么q++;如果A的列数小于B的列数,那么p++,结束条件为遍历完矩阵A在该行的非零元或者遍历完矩阵B在改行所有的非零元
遍历完A的第i行和B的第j行,将结果添加到相乘后的矩阵C中,此三元组为第i行第j列,数据域elem为temp的值
代码:
这里需要一个快速转置的代码:
template<class T>
void SpareMatrix<T>::transfast(SpareMatrix& B)
{
B.rows = this->cols;
B.cols = this->rows;
B.num = this->num;
B.triList.resize(num);
if (this->num == 0)
{
return;
}
vector<int>cnum(this->cols, 0);//记录每一列的元素个数
vector<int>cpot(this->cols);//记录每列第一个元素在B中的位置
for (int i = 0; i < this->num; i++)
{
cnum[this->triList[i].c]++;
}
cpot[0] = 0;
for (int col = 1; col < this->cols; col++)
{
cpot[col] = cpot[col - 1] + cnum[col - 1];
}
for (int j = 0; j < this->num; j++)
{
int col = this->triList[j].c;
int q = cpot[col];
B.triList[q].c = this->triList[j].r;
B.triList[q].r = this->triList[j].c;
B.triList[q].elem = this->triList[j].elem;
cpot[col]++;
}
}
矩阵相乘代码:
template<class T>
SpareMatrix<T> SpareMatrix<T>::mult3(SpareMatrix& B)//转置B后相乘
{
SpareMatrix<T> C;
B.transfast(C);
//计算A与B矩阵的每一行非零元位置
vector<int>Arnum(this->rows, 0);//A每一行非零元个数
vector<int>Arpos(this->rows);//A每一行第一个非零元位置
vector<int>Crnum(C.rows, 0);//B每一行非零元个数
vector<int>Crpos(C.rows);//B每一行第一个非零元位置
for (int i = 0; i < this->num; i++)
{
Arnum[this->triList[i].r]++;
}
Arpos[0] = 0;
for (int row = 1; row < this->rows; row++)
{
Arpos[row] = Arnum[row - 1] + Arpos[row - 1];
}
for (int i = 0; i < C.num; i++)
{
Crnum[C.triList[i].r]++;
}
Crpos[0] = 0;
for (int row = 1; row < C.rows; row++)
{
Crpos[row] = Crnum[row - 1] + Crpos[row - 1];
}
//矩阵相乘
vector< Triple<T> >t;
if (this->num * C.num != 0)//不是零矩阵
{
for (int i = 0; i < this->rows; i++)
{
for (int j = 0; j < C.rows; j++)
{
int p = Arpos[i];
int q = Crpos[j];
T temp = 0;
while ((p < Arpos[i] + Arnum[i]) && (q < Crpos[j] + Crnum[j]))
{
if (this->triList[p].c == C.triList[q].c)
{
temp += this->triList[p].elem * C.triList[q].elem;
p++;
q++;
}
else if (this->triList[p].c < C.triList[q].c)
{
p++;
}
else
{
q++;
}
}
Triple<T> tt(i, j, temp);
t.push_back(tt);
}
}
}
Triple<T>* t1 = new Triple<T>[t.size()];
for (int k = 0; k < t.size(); k++)
{
t1[k] = t[k];
}
SpareMatrix<T>D(t1, this->rows, B.cols, t.size());
return D;
}