矩阵转置
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、前 言
三元组稀疏矩阵的快速转置算法在《数据结构》课程中是一个难点,处理这个知识点很费时间,在教学过程中,往往受课时限制,采取略过不讲的办法,有的教材就干脆把它省去不提。日常生产和科研过程中,数字计算中大量使用了矩阵,为了减少运算量,提高运算效率,这个算法很有用处。这里给出三元组稀疏矩阵的快速转置算法详细过程,并给出C语言描述的实例。
矩阵转置定义为: m行n列矩阵M(m,n) 转换为n行m列N(n,m) 矩阵,即行列互换。例如:如M(3,4)转置为N(4,3)。如图-1矩阵转置。
二、数组的三元组表示与稀疏矩阵
在实际应用中,往往会遇到一种大多数元素的值为零的矩阵,只有少部分为非零元素,这些非零元素在矩阵中的分布又没有一定的规律,我们把它记作“零元素个数>>非零元素个数”,即零元素个数远远大于非零元素个数。这种矩阵称为稀疏矩阵(sparse matrix)。例如,下面所示的矩阵M和它的转置矩阵N,在30个元素中只有8个非零元素,显然是个稀疏矩阵。如图-2,稀疏矩阵M转置成N的示意图。
为了节省空间,可以进行压缩存储,只需存储稀疏矩阵的非零元素。但是,为了实现矩阵的各种运算,除了存储非零元素的值外,还必须同时记下它所在的行和列。可以用一个三元组(i, j, aij)唯一地确定矩阵中的一个非零元素,其中i, j分别表示非零元素的行号和列号,aij表示非零元素的值。
下面就是(算式1)式中矩阵M的(5行6列共有)8个非零元素的三元组表示:
{ (1,1, 8), (1,3, 9) , (2,2,2) , (3,4,3) , (3,6,7) , (4,1,6) , (4,3,4) , (5,4,5)}
若以某种方式(以行为主或以列为主的顺序)将8个三元组排列起来,再加上一个表示矩阵M的行数,列数及非零元素的个数的特殊的三元组(5,6,8),则所形成的表就能唯一地确定稀疏矩阵。
三元组表: 假设以顺序存储结构来表示三元组表(triple table),则得到稀疏矩阵的一种压缩存储方式,即三元组顺序表,简称三元组表。其结构描述如下:
typedef struct
{int I,j;
ElemType v;
}Mat;
typedef struct
{int m,n,t; /* m 表示行数, n 表示列数,t 表示非零元素个数 */
Mat data[MAXSIZE]; /* 所存储元素的最大个数 */
)Spmatrix;
Spmatrix a,b;
要假定结构中data域表示非零元素的三元组是以行为主顺序排列的。(算式1)表示了稀疏矩阵M和N中data域的排列情况。这种表示方法,在矩阵足够稀疏的情况下,对存储空间的需求量比通常的方法少得多。
例如, 我们所说的5×6的矩阵M,若用三元组表来表示,在每个元素占一个存储单元的情况下,
则只需要27个存储单元(包括特殊三元组所占的3个单元,可以使用数组[0]单元);若直接用二维数组,按通常办法表示,则需30个单元。矩阵越大,越稀疏,其优越性越明显。
三、稀疏矩阵的转置算法。
矩阵的转置运算是变换元素的位置,把位于(i, j)的元素换到(j, i)位置上。也就是说,把元素的行和列对换。所以一个m×n的矩阵M,它的转置矩阵是一个n×m的矩阵,且N[i,j]=M[j,i],其中,1≤i≤n,1≤j≤m。例如, (算式1)中的矩阵N就是矩阵M的转置矩阵,矩阵N也是一个稀疏矩阵,其非零元素的排列情况如表-1(b)所示。求矩阵M的转置矩阵N,实际上就是由表-1(a)求表-1(b)。
从表-1可以看出,对每个非零元素而言,从M转置到N,分两步,第一步,把a.data的第一列数和第二列数对换,得到的b".data。因为b’.data是按非零元素在N中的列序排列的,又因为我们已约定,b.data中的非零元素应按照在矩阵N中的行序排列。第二步,将b’.data转换成按非零元素在矩阵N中的行序排列的b.data。转换方法如下:
普通算法分析:按b.data中三元组的次序进行转置。也就是说,按照矩阵M的列序进行转置。显然,为了找到M中的每一列的所有的非零元素,需要对a.data从第1行起整个扫描一遍。由于a.data是以M的行序来存放每一个非零元素的,因此,这样得到的顺序恰好是b.data应有的顺序。其具体算法描述如下:
#define MaxSize 100
#define ElemType int
typedef struct
{ int i,j; ElemType v;
}Mat;
typedef struct
{ int m,n,t; Mat data[MaxSize];
} Spmatrix;
Spmatrix pa,*pb;
void transmat(Spmatrix a,Spmatrix *b)
{ int p,q,col;
b->m=a.n; b->n=a.m; b->t=a.t;
if (a.t!=0)
{ q=0;
for (col=1; col<=a.n; col++)
for (p=0; p if(a.data
.j==col)
{ b->data[q].j=a.data
.i;
b->data[q].i=a.data
.j;
b->data[q].v=a.data
.v;
q++;
}
}
}
该算法使用了一个二重循环语句。外循环来控制扫描的次数,每执行完一次外循环体,矩阵N相当于排好了一行;内循环则用来控制扫描a.data的第0一t-1行,判断每个元素在该轮中是否需要转换。
分析这个算法,除少数附加空间,例如p,q,col和t外,它所需要的存储量仅为两个三元组表a,b所需要的空间。因此,当非零元素个数t < m*n/3时,其所需存储空间比直接用二维数组要省。至于执行时间,因算法的主要工作是在col和p的二重循环中完成的,故算法的执行时间为O(n*t)。当非零元素个数t的数量级为m,n时,其执行时间变为O(m*n*n)。
这比直接用二维数组表示矩阵的转置算法的时间量级O(m*n)要差。不难看出,此
算法之所以耗费时间,问题在于其每形成转置矩阵的一行,都必须对a.data从头到尾扫描一遍。能否对a.data只扫描一次,又不引起元素的移动就能得到要求的b->data呢?为此,我们可使用另一种快速方法。
四、快速转置算法
这两种算法的函数中,a是已知的三元组表,为了使函数得到转置后新的三元组表b, 这里使用的是传址调用的形参*b。
按照a.data 中三元组的次序进行转置。转置后的元素不连续存放,直接放到b->data 中应有的位置上,这样既可以避免元素移动,又只需对a.data扫描一次。为了确定矩阵M 中的每一列(即N中每一行)的第一个非零元素在b->data中应有的位置,需要先求得矩阵M 中的每一列中非零元素的个数。为此,需设置两个数组num[1..n]和pot[1..n],分别存放在矩阵M中每一列的非零元素个数和每一列第1个非零元素在b->data中的位置,即
num[col] 表示矩阵M中第col列中非零元素的个数:
pot[col] 的初值表示M中第col列的第一个非零元素在b->data中的位置。
于是有
pot[1]=0
pot[co1]=pot[col-1] + num[col-1] (2≤ c01 ≤n)
对于式(5.8)中的矩阵M,其num和pot的值如表5.4所示。
算法:
pot[1]=0; M第1列第1个a(1,1,8) 放在b.data的第0个(1,1,8)(第2 个a(4,1,6) 放在b.data的第1个(1,4,6))
Pot[2]=pot[1]+num[1]=0+2=2; M第2列第1个a(2,2, 2) 放在b.data的第2个(2,2,2)
Pot[3]=pot[2]+num[2]=2+1=3; M第3列第1个a(1,3,9) 放在b.data的第3个(3,1,9)(第2 个a(4,3,4) 放在b.data的第4个(3,4,4)
Pot[4]=pot[3]+num[3]=3+2=5; M第4列第1个a(3,4,3) 放在b.data的第5个(4,3,3)(第2 个a(5,4,5) 放在b.data的第6个(4,5,5)
Pot[5]=pot[4]+num[4]=5+2=7;M第5列第1个a(X,5,X) 放在b.data的第7个(5,X,X)(本行无非零元素,空第7个)
Pot[6]=pot[5]+num[5]=7+0=7; M第6列第1个a(3,6,7) 放在b.data的第7个(6,3,7)
5.快速转置算法程序:
void fastran(Spmatrix a,Spmatrix *b)
{ int k,p,q,col;
int num[10],pot[10];
b->m=a.n; b->n=a.m; b->t=a.t;
if (a.t!=0)
{
for (col=1; col<=a.n; col++)
{ num[col]=0; printf("col=%d num[col]=%d
",col,num[col]);}
for (k=0; k { num[a.data[k].j]++;
//if (num[a.data[k].j]==col) num[col]++;
printf("num[a.data[%d].j=%d
",k,num[a.data[k].j]);
}
pot[1]=0;
for (col=2; col<=a.n; col++)
{ pot[col]=pot[col-1]+num[col-1];
printf("pot[%d]=%d
",col,pot[col]);
}
for (p=0;p { col=a.data
.j; q=pot[col];
b->data[q].i=a.data
.j;
b->data[q].j=a.data
.i; fastran(
b->data[q].v=a.data
.v;
pot[col]++;
}
}
}
五、结束语
这个算法仅比前一个算法多用了两个辅助数组。从时间上看
,算法中有四个并列的循环语句,它们分别执行n,t,n- 1和t次,因此,算法的执行时间为O(n+t)。在t和m,n等量级时,该算法的执行时间上升到O(m*n),但在t 参考文献:[1] 《数据结构》 严蔚敏等 清华大学出版社 1992年 [2] 《数据结构与算法》 殷人昆等 机械工业版社 1996年 (作者单位:鹤壁职业技术学院)
#include
#define MAXSIZE 12500//假设非零元个数的最大值为12500
#define OK 1
#define ERROR 0
using namespace std;
typedef int ElemType;//定义ElemType的类型
typedef struct{
int i,j;//该非零元的行下表和列下表
ElemType e;
}Triple;
typedef struct{
Triple data[MAXSIZE+1];//非零元三元组表
int mu,nu,tu;//矩阵的行数、列数和非零元个数
}TSMatrix;
int CreateSMatrix(TSMatrix &M){
//采用三元组顺序表存储表示,创建稀疏矩阵M
cout<<"请输入稀疏矩阵的行数、列数和非零元个数:"<
if((M.mu<=0)||(M.nu<=0)||(M.tu<=0)||(M.tu>M.mu*M.nu))
//判断行值、列值、元素个数是否合法
return ERROR;
for(int i=1;i<=M.tu;i++){//输入稀疏矩阵元素
cout<<"请输入元素坐标及大小:"<
if((M.data[i].i<=0)||(M.data[i].j<=0)){
cout<<"输入错误,请重新输入"<
}//if
}//for i
return OK;
}
int DestroySMatrix(TSMatrix &M){
//清除采用三元组顺序表存储表示的稀疏矩阵M
for(int i=1;i<=M.tu;i++){
M.data[i].i=0;
M.data[i].j=0;
M.data[i].e=0;
}//for i
M.mu=0;
M.nu=0;
M.tu=0;
return OK;
}
int PrintSMatrix(TSMatrix M){
//输出采用三元组顺序表存储表示的稀疏矩阵M
if((M.mu<=0)||(M.nu<=0)||(M.tu<=0))
return ERROR;
cout<
for(int col=1;col<=M.nu;col++){
for(int i=1;i<=M.tu;i++){
if((M.data[i].i==row)&&(M.data[i].j==col)){
cout<
}//if
}//for i
cout<<"0"<<" ";
loop:;
}//for col
cout<
return OK;
}
int AddSMatrix(TSMatrix M,TSMatrix N,TSMatrix &Q){
//求采用三元组顺序表存储表示的稀疏矩阵M和N的和,,结果赋给矩阵Q
if((M.mu<=0)||(M.nu<=0)||(M.tu<=0)||(N.mu<=0)||(N.nu<=0)||(N.tu<=0))
return ERROR;
if(M.mu!=N.mu||M.nu!=N.nu)
return ERROR;
Q.mu=M.mu;
Q.nu=M.nu;
Q.tu=0;
int x=0,y=0;
for(int i=1;i<=Q.mu;i++){
for(int j=1;j<=Q.nu;j++){
for(int p=1;p<=M.tu;p++){
if((i==M.data[p].i)&&(j==M.data[p].j)){
x=M.data[p].e;
break;
}
else x=0;
}//for p
for(int q=1;q<=N.tu;q++){
if((
i==N.data[q].i)&&(j==N.data[q].j)){
y=N.data[q].e;
break;
}
else y=0;
}//for q
if((x+y)!=0){
Q.data[Q.tu+1].i=i;
Q.data[Q.tu+1].j=j;
Q.data[Q.tu+1].e=x+y;
Q.tu++;
}//if
}//for j
}//for i
return OK;
}
int SubSMatrix(TSMatrix M,TSMatrix N,TSMatrix &Q){
//求采用三元组顺序表存储表示的稀疏矩阵M和N的差,结果赋给矩阵Q
if((M.mu<=0)||(M.nu<=0)||(M.tu<=0)||(N.mu<=0)||(N.nu<=0)||(N.tu<=0))
return ERROR;
if(M.mu!=N.mu||M.nu!=N.nu)
return ERROR;
Q.mu=M.mu;
Q.nu=M.nu;
Q.tu=0;
int x=0,y=0;
for(int i=1;i<=Q.mu;i++){
for(int j=1;j<=Q.nu;j++){
for(int p=1;p<=M.tu;p++){
if((i==M.data[p].i)&&(j==M.data[p].j)){
x=M.data[p].e;
break;
}
else x=0;
}//for p
for(int q=1;q<=N.tu;q++){
if((i==N.data[q].i)&&(j==N.data[q].j)){
y=N.data[q].e;
break;
}
else y=0;
}//for q
if((x-y)!=0){
Q.data[Q.tu+1].i=i;
Q.data[Q.tu+1].j=j;
Q.data[Q.tu+1].e=x-y;
Q.tu++;
}//if
}//for j
}//for i
return OK;
}
int FastTransposeSMatrix(TSMatrix M,TSMatrix &T){
//采用三元组顺序表存储表示,求稀疏矩阵M的转职矩阵T
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu){
int num[M.nu],cpot[M.nu];
for(int col=1;col<=M.nu;col++) num[col]=0;
for(int t=1;t<=M.tu;t++) num[M.data[t].j]++;//求M中每一列含有非零元个数
cpot[1]=1;//求第col列中第一个非零元在b.data中的序号
for(int col=2;col<=M.nu;col++) cpot[col]=cpot[col-1]+num[col-1];
for(int p=1,q=1,col;p<=M.tu;p++){
col=M.data[p].j;
q=cpot[col];
T.data[q].i=M.data[p].j;
T.data[q].j=M.data[p].i;
T.data[q].e=M.data[p].e;
++cpot[col];
}//for p
}//if T.tu
return OK;
}
int main(){
system ("CLS");
TSMatrix M,N,T,Q;//声明M、N、T、Q为稀疏矩阵
cout<<"1.矩阵加法运算\n2.矩阵减法运算\n3.矩阵转置运算\n4.退出\n请输入1~4:";
int i;
while(cin>>i){
switch(i){
case 1: CreateSMatrix(M);//创建稀疏矩阵M
PrintSMatrix(M);//显示稀疏矩阵M
CreateSMatrix(N);//创建稀疏矩阵N
PrintSMatrix(N);//显示稀疏矩阵N
AddSMatrix(M,N,Q);//将稀疏矩阵M与N的和存入矩阵Q中
cout<<"加法结果:";
PrintSMatrix(Q);//显示矩阵Q
break;
case 2: CreateSMatrix(M);//创建稀疏矩阵M
PrintSMatrix(M);//显示稀疏矩阵M
CreateSMatrix(N);//创建稀疏矩阵N
PrintSMatrix(
N);//显示稀疏矩阵N
SubSMatrix(M,N,Q);//将稀疏矩阵M与N的差存入矩阵Q中
cout<<"减法结果:";
PrintSMatrix(Q);//显示矩阵Q
break;
case 3: CreateSMatrix(M);//创建稀疏矩阵M
PrintSMatrix(M);//显示稀疏矩阵M
FastTransposeSMatrix(M,T);//将稀疏矩阵M转置,结果存入矩阵T中
cout<<"转置矩阵:";
PrintSMatrix(T);//显示稀疏矩阵T
break;
case 4: return 0;
default: system ("CLS");
cout<<"\n*输入错误,请重新输入*\n"<
}
cout<<"1.矩阵加法运算\n2.矩阵减法运算\n3.矩阵转置运算\n4.退出\n请输入1~4:";
}
}