Click here to load reader
Upload
faxon
View
151
Download
3
Embed Size (px)
DESCRIPTION
第 17 次课 5.3.2 稀疏矩阵 什么是稀疏矩阵?简单说,设矩阵 A 中有 s 个非零元素,若 s 远远小于矩阵元素的总数(即 s≦m×n ),则称 A 为稀疏矩阵。精确点,设在的矩阵 A 中,有 s 个非零元素。令 e=s/(m*n) ,称 e 为矩阵的稀疏因子。通常认为 e≦0.05 时称之为稀疏矩阵。 - PowerPoint PPT Presentation
Citation preview
第 17次课5.3.2 稀疏矩阵
什么是稀疏矩阵?简单说,设矩阵 A 中有 s 个非零元素,若 s 远远小于矩阵元素的总数(即 s m×n≦ ),则称 A 为稀疏矩阵。精确点,设在的矩阵 A 中,有 s个非零元素。令 e=s/(m*n) ,称 e 为矩阵的稀疏因子。通常认为 e 0.05≦ 时称之为稀疏矩阵。
在存储稀疏矩阵时,为了节省存储单元,很自然地想到使用压缩存储方法。但由于非零元素的分布一般是没有规律的,因此在存储非零元素的同时,还必须同时记下它所在的行和列的位置( i,j) 。反之,一个三元组 (i,j,aij) 唯一确定了矩阵 A 的一个非零元。因此,稀疏矩阵可由表示非零元的三元组及其行列数唯一确定。
例如,下列三元组表((1,2,12)(1,3,9),(3,1,- 3),(3,6,14),(4,3,24), (5,2,18),(6,1,15),(6,4,-7))
加上 (6,7) 这一对行、列值便可作为下列矩阵 M的另一种描述。
0 12 9 0 0 0 0
0 0 0 0 0 0 0
-3 0 0 0 0 14 0
0 0 24 0 0 0 0
0 18 0 0 0 0 0
15 0 0 -7 0 0 0
而由上述三元组表的不同表示方法可引出稀疏矩阵不同的压缩存储方法。1. 三元组表示
假设以前面学过的顺序存储结构来表示三元组表,则可得到稀疏矩阵的一种压缩存储方法——三元顺序表。
设 A 为 TSMatrix 型的结构变量,图 5.5 中所示的稀疏矩阵的三元组的表示如下:
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
下面以矩阵的转置为例,说明在这种压缩存储结构上如何实现矩阵的运算。 一个 m×n 的矩阵 A ,它的转置 B 是一个 n×m 的矩阵,且 a[i][j]=b[j][i] , 0 i m≦ ≦ , 0 j n≦ ≦ ,即 A 的行是 B 的列, A的列是 B 的行。 将 A 转置为 B ,就是将 A 的三元组表 a.data 置换为表 B的三元组表 b.data ,如果只是简单地交换 a.data 中 i 和 j 的内容,那么得到的 b.data 将是一个按列优先顺序存储的稀疏矩阵 B ,要得到按行优先顺序存储的 b.data ,就必须重新排列三元组的顺序。 由于 A 的列是 B 的行,因此,按 a.data 的列序转置,所得到的转置矩阵 B 的三元组表 b.data 必定是按行优先存放的。
按这种方法设计的算法,其基本思想是:对 A 中的每一列 col(0 col n-1)≦ ≦ ,通过从头至尾扫描三元表 a.data ,找出所有列号等于 col 的那些三元组,将它们的行号和列号互换后依次放入 b.data 中,即可得到 B 的按行优先的压缩存储表示。
三元组表示的稀疏矩阵的转置
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
第 一 趟
不为 1 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
第 一 趟
不为 1 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
第 一 趟
为 1 ,填入到右表
中
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
第 一 趟
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
第 一 趟
不为 1 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
第 一 趟
不为 1 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
第 一 趟
不为 1 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
第 一 趟
为 1 ,填入到右表
中
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
第 一 趟
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
第 一 趟
不为 1 ,且已经是最后一个,本趟结束
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
第 二 趟
为 2 ,写入右表
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
第 二 趟
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
第 二 趟
不为 2 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
第 二 趟
不为 2 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
第 二 趟
不为 2 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
第 二 趟
不为 2 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
第 二 趟
为 2 ,写入右表
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
2 5 18
第 二 趟
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
2 5 18
第 二 趟
不为 2 ,考察下一
个
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
1 3 -3
1 6 15
2 1 12
2 5 18
第 二 趟
不为 2 ,且到达最后,
本趟结束
同理找出转置后行号为 3 、 4 、5 、 6 的元组。
矩阵转置算法如下:#include <iostream>#include<vector>// 顺序表模板using namespace std;class Triple// 定义一个三元组类{public:
int i,j; int e;
public:Triple(){}Triple(int row,int col,int elem){
i=row,j=col,e=elem;}~Triple(){}
};
class TSMatrix// 存储三元组的顺序表类{public:
vector<Triple> data;// 存储三元组int mu,nu,tu;// 矩阵的行数、列数、非零元个数
public:TSMatrix(){mu=nu=tu=0;};TSMatrix(int row,int col){
mu=row,nu=col,tu=0;}void InsertTriple(Triple elem){
data.push_back(elem);tu=data.size();
}void print(){
for(int i=0;i<data.size();i++)cout<<data[i].i<<" "<<data[i].j<<" "<<data[i].e<<endl;
}};
void TransposeSMatrix(TSMatrix M,TSMatrix& N)// 矩阵转置{
N.mu=M.mu,N.nu=M.nu;if(M.tu){
int total,col;for(col=0;col<M.nu;col++){
for(total=0;total<M.tu;total++){
int j=M.data[total].j;if(j==col){
Triple p=M.data[total];Triple temp(p.j,p.i,p.e);N.InsertTriple(temp);
}
}}
}}
int main(int argc, char* argv[]){
struct element{int i,j,e;};element s[8]={{1,2,12},{1,3,9},{3,1,-3},{3,6,14},{4,3,24},{5,2,18},{6,1,15},{6,4,-7}};int row=6,colum=7;TSMatrix m(row,colum),t;for(int i=0;i<=7;i++){
m.InsertTriple(Triple(s[i].i,s[i].j,s[i].e));}cout<<"Matrix M is:"<<endl;m.print();::TransposeSMatrix(m,t);cout<<"Matrix T is:"<<endl;t.print();return 0;
}
分析这个算法,主要的工作是在 total 和 col 的两个循环中完成的,故算法的时间复杂度为O(t*n) ,即矩阵的列数和非零元的个数的乘积成正比。而一般传统矩阵的转置算法为: for(col=0;col<=n-1;++col) for(row=0;row<=m;++row) t[col][row]=m[row][col]; 其时间复杂度为 O(n*m) 。当非零元素的个数 t 和 m*n 同数量级时,算法 transmatrix 的时间复杂度为 O(m*n2) 。三元组顺序表虽然节省了存储空间,但时间复杂度比一般矩阵转置的算法还要复杂,同时还有可能增加算是法的难度。因此,此算法仅适用于 t<<m*n 的情况。
有一种称为快速转置的算法,他对上述算法进行改造,增加了一些空间,但是却能使算法的时间复杂度达到 O(n*m) 。由于快速转置算法并没有改变矩阵的存储结构,只是在算法上面采用了一些技巧,所以我们在此不再赘述,仅介绍其基本思想。有兴趣的同学可以参考课本。
快速转置
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 0 0 0 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 0 0 0 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 0 1 0 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 0 1 0 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 0 1 1 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 0 1 1 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 1 1 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 1 1 0 0 0 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 1 1 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 1 1 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 1 2 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 1 2 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 2 2 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 1 2 2 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 2 2 2 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 2 2 2 0 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
通过对待转置三元组的一趟扫描可以得到矩阵中每列有几个元素,也就是转置后每行有几个元素。
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
根据每列元素的个数,可以算出来每列的第一个元素在转置后的表中的位置。如:如果第一列的第一个元素存储位置为 0 的话,那么第二列的第一个元素的存储位置应该为: 0+ 第一列元素的个数;同理第三列的第一个元素的位置应该为:第二列的第一个元素的位置 + 第二列元素的个数。
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2 4
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2 4 6
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2 4 6 7
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2 4 6 7 7
列 1 2 3 4 5 6 7
元素数 2 2 2 1 0 1 0
各列第一个元素在转置后元组中存储位置的求法:
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2 4 6 7 7 8
有了此表后,我们通过一次扫描就可以把三元组进行转置。当第 2 列的第一个元素被扫描出来且存入到合适位置后,那么第二个元素将成为未被扫描的第一个元素,且其位置是原第一个元素位置加 1 。
列 1 2 3 4 5 6 7
首元素数地址: cpot
0 2 4 6 7 7 8
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 0 2 4 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0
1
2
3
4
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 0 3 4 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0
1
2 2 1 12
3
4
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 0 3 4 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0
1
2 2 1 12
3
4
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 0 3 5 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0
1
2 2 1 12
3
4 3 1 9
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 0 3 5 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0
1
2 2 1 12
3
4 3 1 9
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 3 5 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3
4 3 1 9
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 3 5 6 7 7 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3
4 3 1 9
5
6
7
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 3 5 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3
4 3 1 9
5
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 3 5 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3
4 3 1 9
5
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 3 6 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3
4 3 1 9
5 3 4 24
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 3 6 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3
4 3 1 9
5 3 4 24
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 4 6 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3 2 5 18
4 3 1 9
5 3 4 24
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 1 4 6 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1
2 2 1 12
3 2 5 18
4 3 1 9
5 3 4 24
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 2 4 6 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1 1 6 15
2 2 1 12
3 2 5 18
4 3 1 9
5 3 4 24
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 2 4 6 6 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1 1 6 15
2 2 1 12
3 2 5 18
4 3 1 9
5 3 4 24
6
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 2 4 6 7 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1 1 6 15
2 2 1 12
3 2 5 18
4 3 1 9
5 3 4 24
6 4 6 -7
7 6 3 14
快速转置过程列 1 2 3 4 5 6 7首元素数地址: cpot 2 4 6 7 7 8 8
i j e
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
i j e
0 1 3 -3
1 1 6 15
2 2 1 12
3 2 5 18
4 3 1 9
5 3 4 24
6 4 6 -7
7 6 3 14
转置过程结束。
二、带行表的三元组矩阵的乘法:已知三元组表示的矩阵 M ( 6*7 )、 N
( 7*6 )如下
M: i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
N: i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
现在要计算他们的乘积 Q( 6*6),该如何运算。
我们知道:Q ( 1 , 1 ) =M(1,1)*N(1,1)+M(1,2)*N(2,1)+M(1,3)*N(3,1)+…M(1,7)*N(7,1)+是一个累加的过程。同理 Q ( 1 , 2 )、 Q( 1 , 3 )… ..Q ( 1 , 6 )都是累加和。我们看一下,求 Q 的第一行元素的处理过程。这个过程只涉及到 M 的第一行元素,却可能涉及到 N 的所有元素。
矩阵相乘
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
0 0 0 0 0 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
0 0 0 0 0 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
1 0 0 0 0 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
1 0 0 0 0 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
1 0 0 0 1 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
1 0 0 0 1 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
1 0 0 0 1 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
2 0 0 0 1 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
2 0 0 0 1 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
1 2 3 4 5 6
2 0 0 1 1 0sum
此时 Q 的第 1行元素已经存
在 sum 中
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 1 2
Q
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 1 2
Q
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 1 2
Q
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 1 2
1 4 1
Q
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 1 2
1 4 1
1 5 1
Q
1 2 3 4 5 6
2 0 0 1 1 0sum
i j e
1 1 2
1 4 1
1 5 1
Q
这是如何求 Q 中的第一行元素,同理可以求其他各行元素。经过分析可以发现,算法的时间将会浪费在如何在 M 、N 中确定元组的范围。比如下面的 M 中 i=1 的元组的范围,N 中 i=2 的元组的范围。如果每次都采取顺序扫描的话,将会非常浪费时间。
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
为了解决这个问题,可以像求矩阵的转置而引入列表一样,引进一个行表,则问题将会变得简单了。
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
i j e
1 3 1
1 6 1
2 1 1
2 5 1
3 1 1
3 4 1
4 6 1
6 3 1
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
行号 1 2 3 4 5 6
行中元素个数
其求解过程跟求列表相似,不再赘述。
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
行号 1 2 3 4 5 6
行中元素个数 2 0 2 1 1 2
同理可以求每行第一个元素在三元组表中的位置。
i j e
1 2 1
1 3 1
3 1 1
3 6 1
4 3 1
5 2 1
6 1 1
6 4 1
行号 1 2 3 4 5 6
行中元素个数 2 0 2 1 1 2
行号 1 2 3 4 5 6
行中首元素位置 rpos 0 2 2 4 5 6
那么我们想求第一行所有元素的范围时,可以如下来计算:
第一行第一个元素的位置为: rpos[1]
第一行最后一个元素的位置为: rpos[2]-1
同理,求其它各行元素的范围也是如此。
这样寻找各行元素的范围就容易得多了,算法的时间复杂度就能够降低。
矩阵相乘的思想介绍完毕
带行表的三元组表。其类型描述如下:#include<vector>using namespace std;class Triple// 三元组类{public:
int i,j; int e;
public:Triple(){}Triple(int row,int col,int elem){
i=row,j=col,e=elem;}~Triple(){}
};
class RLSMatrix{public:
vector<Triple> data;vector<int> rpos;//rpos[i] 的值表示第 i 行的第一
个元素在 data 数组中的位置int mu,nu,tu;// 矩阵的行数、列数、非零元个数
public:RLSMatrix(){mu=nu=tu=0;};RLSMatrix(int row,int col){ mu=row,nu=col,tu=0; }void InsertTriple(Triple temp){ data.push_back(temp);tu=data.size();}
void MakeRpos(){//初始化 rpos 数组
vector<int> num(mu+1);for(int i=0;i<=mu;i++)
num[i]=0;for(i=0;i<data.size();i++){//num[row] 记录了第 row 行共有多少个非 0 元素
int row=data[i].i;num[row]++;
}rpos.resize(mu+1);
rpos[1]=0;for(i=2;i<rpos.size();i++)
rpos[i]=rpos[i-1]+num[i-1];num.clear();
}
int getStart(int i){
if(i>=1&&i<=rpos.size()-1)return rpos[i];
else return -1;}int getEnd(int i){
if(i>=1&&i<rpos.size()-1)return rpos[i+1]-1;
else if(i==rpos.size()-1)return data.size()-1;// 最后一行的最后一个
元素的位置就是 data 数组的最后一个元素else return -1;
}
// 下面是矩阵的乘法:void MulSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix & Q)
{
if(M.nu!=N.mu) return;
Q.mu=M.mu,Q.nu=N.nu,Q.tu=0;
M.MakeRpos();//生成 M 的行表N.MakeRpos();//生成 N 的行表
int i;
for(i=1;i<=M.mu;i++)
{
vector<int> sum(Q.nu+1);
for(int clear=1;clear<=Q.nu;clear++)
sum[clear]=0;
int i_start=M.getStart(i);
// 得到第 i 行的开始位置int i_end=M.getEnd(i);
// 得到第 i 行的结束位置
int i_index;for(i_index=i_start;i_index<=i_end;i_index++){
int k=M.data[i_index].j;int k_start=N.getStart(k);// 得到第 k 行的开始位置int k_end=N.getEnd(k);// 得到第 k 行的结束位置int k_index;
for(k_index=k_start;k_index<=k_end;k_index++){
int j=N.data[k_index].j;
sum[j]+=M.data[i_index].e*N.data[k_index].e;}
}
// 第一行元素计算完毕,将其加入到 Q 中for(int j=1;j<=Q.nu;j++)
{
if(sum[j])
{
Triple temp(i,j,sum[j]);
Q.InsertTriple(temp);
}
}
}
}
void print()
{
for(int i=0;i<data.size();i++)
cout<<data[i].i<<" "<<data[i].j<<" "<<data[i].e<<endl;
cout<<"***********************"<<endl;
}
};
int main(int argc, char* argv[])
{
RLSMatrix M(6,7),N(7,6),Q(6,6);
M.InsertTriple(Triple(1,2,1));
M.InsertTriple(Triple(1,3,1));
M.InsertTriple(Triple(3,1,1));
M.InsertTriple(Triple(3,6,1));
M.InsertTriple(Triple(4,3,1));
M.InsertTriple(Triple(5,2,1));
M.InsertTriple(Triple(6,1,1));
M.InsertTriple(Triple(6,4,1));
//////N.InsertTriple(Triple(1,3,1));N.InsertTriple(Triple(1,6,1));N.InsertTriple(Triple(2,1,1));N.InsertTriple(Triple(2,5,1));N.InsertTriple(Triple(3,1,1));N.InsertTriple(Triple(3,4,1));N.InsertTriple(Triple(4,6,1));N.InsertTriple(Triple(6,3,1));/////M.MulSMatrix(M,N,Q);M.print();N.print();Q.print();
// 下面是用数组作为矩阵的存储结构,并进行矩阵的乘法
int m[6][7]={ {0,1,1,0,0,0,0},{0,0,0,0,0,0,0},
{1,0,0,0,0,1,0},{0,0,1,0,0,0,0},{0,1,0,0,0,0,0},{1,0,0,1,0,0,0}
};int n[7][6];//n 是 m 的转置矩阵int q[6][6];//q 作为 m*n 的结果int i,j;for(i=0;i<6;i++)// 转置
for(j=0;j<7;j++)n[j][i]=m[i][j];
for(i=0;i<6;i++)// 乘积{
for(j=0;j<7;j++){
q[i][j]=0;for(int k=0;k<7;k++){
q[i][j]+=m[i][k]*n[k][j];}
}}
for(i=0;i<6;i++)//打印结果for(j=0;j<6;j++)
{
if(q[i][j])cout<<i+1<<" "<<j+1<<" "<<q[i][j]<<endl;
}
cout<<"****************"<<endl;
return 0;
}
本节内容,大家应该把理解思想作为首要任务。算法及其实现,有能力的同学可以多下点功夫。