爲節省存儲空間並且加快處理速度,需要對這類矩陣進行壓縮存儲,壓縮存儲的原則是:不重複存儲相同元素;不存儲零值元素。
一、相關概念
㈠特殊矩陣:矩陣中存在大多數值相同的元,或非0元,且在矩陣中的分佈有一定規律。
⒈對稱矩陣:矩陣中的元素滿足
aij=aji 1≤i,j≤n
⒉三角矩陣:上(下)三角矩陣指矩陣的下(上)三角(不包括對角線)中的元素均爲常數c或0的n階矩陣。
⒊對角矩陣(帶狀矩陣):矩陣中所有非0元素集中在主對角線爲中心的區域中。
㈡稀疏矩陣:非0元素很少(≤ 5%)且分佈無規律。
二、存儲結構及算法思想
1、對稱矩陣
存儲分配策略: 每一對對稱元只分配一個存儲單元,即只存儲下三角(包括對角線)的元,所需空間數爲: n(n+1)/2。
存儲分配方法: 用一維數組sa[n(n+1)/2]作爲存儲結構。
sa[k]與aij之間的對應關係爲:
2、三角矩陣
也是一個n階方陣,有上三角和下三角矩陣。下(上)三角矩陣是主對角線以上(下)元素均爲零的n階矩陣。設以一維數組sb[0..n(n+1)/2]作爲n階三角矩陣B的存儲結構,仍採用按行存儲方案,則B中任一元素bi,j和sb[k]之間仍然有如上的對應關係,只是還需要再加一個存儲常數c的存儲空間即可。如在下三角矩陣中,用n(n+1)/2的位置來存儲常數。
對特殊矩陣的壓縮存儲實質上就是將二維矩陣中的部分元素按照某種方案排列到一維數組中,不同的排列方案也就對應不同的存儲方案
2、稀疏矩陣
常見的有三元組表示法、帶輔助行向量的二元組表示法(也即行邏輯鏈表的順序表),十字鏈表表示法等。
1)、三元組表示法
三元組表示法就是在存儲非零元的同時,存儲該元素所對應的行下標和列下標。稀疏矩陣中的每一個非零元素由一個三元組(i,j,aij)唯一確定。矩陣中所有非零元素存放在由三元組組成的數組中。
在此,data域中表示非零元的三元組是以行序爲主序順序排列的。
以下看如何利用三元組表示法來實現矩陣的轉置。
(1)按照b.data中三元組的次序依次在a.data中找出相應的三元組進行轉置。換句話說,按照矩陣M的列序進行轉置。爲了找到M的每一列中所有的非零元素,需要對其三元組表a.data從第一行起整個掃描一遍。由於a.data是以M的行序爲存放每個非零元的,由此得到的恰好是b.data應有的順序。
2)、帶輔助行向量的二元組表示法及十字鏈表表示法在下一節中學習介紹。
三、存儲結構及C語言描述
1、三元組表示法
(1)按照b矩陣中的行次序依次在a.data中找到相應的三元組進行轉置。
(2)快速轉置:按照a.data中三元組的次序進行轉置,並將轉置後的三元組放到b.data中的恰當位置。
恰當位置的確定:首先計算M矩陣的每一列(即T的每一行)中非0元的個數,然後求得M矩陣每一列第一個非0元在b.data中的位置。
算法基本思想:
設置兩個向量:
num[col]:第col列的非零元素個數。
cpot[col]:第col列第一個非零元在b.data中的恰當位置。
在轉置過程中,指示該列下一個非零元在b.data中的位置。
1、num[col]的計算:
順序掃描a.data三元組,累計各列非0元個數。
2、cpot[col]計算:
2)、帶輔助行向量的二元組表示法及十字鏈表表示法在下一節中學習介紹。
四、算法的C語言實現
#include "stdio.h"
#include "stdlib.h"
#define MAXSIZE 12500
#define OK 1
typedef int ElemType;
typedef struct
{
int i,j;
ElemType e;
}Triple;
typedef struct
{
Triple data[MAXSIZE+1];
int mu,nu,tu; //矩陣行數,列數和非0元個數
}TSMatrix;
int cpot[MAXSIZE+1],num[MAXSIZE+1];
int TransposeSMatrix(TSMatrix M,TSMatrix &T)
{
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu)
{
int q=1;
for(int col=1;col<=M.nu;++col)
for(int p=1;p<=M.tu;++p)
if(M.data[p].j==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;
++q;
}//if
}//if
return OK;
}//TransposeSMatrix
int InPutM(TSMatrix &M)
{
printf("input nu mu tu(With a space interval)of a Matrix:\n");
scanf("%d %d %d",&M.nu,&M.mu,&M.tu); //row,colume,and tu
printf("Please input the data of Matrix:\n");
for(int c=1;c<=M.tu;c++)
{
scanf("%d",&M.data[c].i);
scanf("%d",&M.data[c].j);
scanf("%d",&M.data[c].e);
}//for
return 1;
}//InPut
int PrintM(TSMatrix T)
{
printf("Matrix after transpose is:\n");
for(int c=1;c<=T.tu;c++)
{
printf("%d %d %d\n",T.data[c].i,T.data[c].j,T.data[c].e);
}//for
return 1;
}//InPut
int FastTransposeSMatrix(TSMatrix M,TSMatrix &T)
{
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu)
{
for(int col=1;col<=M.mu;++col) num[col]=0;
for(int t=1;t<=M.tu;++t) ++num[M.data[t].j]; //記述M.data[t].j列
//非0元的個數
cpot[1]=1;
//求第col列中第一個非零元在b.data(T)中的序號
for(int col=2;col<=M.mu;++col)
cpot[col]=cpot[col-1]+num[col-1];
for(int p=1;p<=M.tu;++p)
{
int col=M.data[p].j;
int 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
}//if
return OK;
}//FastTransposeSMatrix
int main()
{
TSMatrix M,T;
InPutM(M);
//TransposeSMatrix(M,T);
FastTransposeSMatrix(M,T);
PrintM(T);
return OK;
}
上一節中我們討論的是稀疏矩陣的轉置,現在我們討論稀疏矩陣的相乘,相加,相減。如果預先知道不是稀疏矩陣,則用二維數組相乘法就可以了。如果是,則我們用來存儲矩陣的結構稱之爲行邏輯連接的順序表,就是加入一個行表來記錄稀疏矩陣中每行的非零元素在三元組表中的起始位置。
一、算法思想
在M.data和N.data中找到相應的各對元素進行相乘即可。此時矩陣的存儲結構是
在此,我們引入rpos[row]用來指示矩陣N的第row行中第一個非零元在N.data中的序號,則rpos[row+1]-1指示矩陣的第row行中最後一個非零元在N.data中的序號。而最後一行中最後一個非零元在N.data的位置就是N.tu了。
如上圖中,N的rpos值爲:
二、C語言描述
三、C語言實現
#include "stdio.h"
#include "stdlib.h"
#define MAXSIZE 12500
#define MAXRC 100
#define OK 1
#define ERROR -1
typedef int ElemType;
typedef int Status;
typedef struct
{
int i,j;
ElemType e;
}Triple;
typedef struct
{
Triple data[MAXSIZE+1];
int rpos[MAXRC+1];//各行第一個非零元的位置表
int mu,nu,tu; //矩陣行數,列數和非0元個數
}RLSMatrix;
int cpot[MAXSIZE+1],num[MAXSIZE+1];
Status CreateSMatrix(RLSMatrix &M)
{ // 創建稀疏矩陣M
int i;
Triple T;
Status k;
printf("請輸入矩陣的行數,列數,非零元素數:(Separated by commas)\n");
scanf("%d,%d,%d",&M.mu,&M.nu,&M.tu);
M.data[0].i=0; // 爲以下比較做準備
for(i=1;i<=M.tu;i++)
{
do
{
printf("請按行序順序輸入第%d個非零元素所在的行(1~%d),列(1~%d),元素值:",i,M.mu,M.nu);
printf("the 3 num Separated by commas\n");
scanf("%d,%d,%d",&T.i,&T.j,&T.e);
k=0;
if(T.i<1||T.i>M.mu||T.j<1||T.j>M.nu) // 行、列超出範圍
k=1;
if(T.i<M.data[i-1].i||T.i==M.data[i-1].i&&T.j<=M.data[i-1].j) // 沒有按順序輸入非零元素
k=1; //&&優先級比||高
}while(k); // 當輸入有誤,重新輸入
M.data[i]=T;
}
for(i=1;i<=M.tu;i++) // 計算rpos[]
if(M.data[i].i>M.data[i-1].i)
for(T.i=0;T.i<M.data[i].i-M.data[i-1].i;T.i++)
M.rpos[M.data[i].i-T.i]=i;
for(i=M.data[M.tu].i+1;i<=M.mu;i++) // 給最後沒有非零元素的幾行賦值
M.rpos[i]=M.tu+1;
return OK;
}//CreateSMatrix
void DestroySMatrix(RLSMatrix &M)
{ // 銷燬稀疏矩陣M(使M爲0行0列0個非零元素的矩陣)
M.mu=0;
M.nu=0;
M.tu=0;
}//DestroySMatrix
void PrintSMatrix(RLSMatrix M)
{ // 輸出稀疏矩陣M
int i;
printf("%d行%d列%d個非零元素。\n",M.mu,M.nu,M.tu);
printf("行 列 元素值\n");
for(i=1;i<=M.tu;i++)
printf("%2d%4d%8d\n",M.data[i].i,M.data[i].j,M.data[i].e);
for(i=1;i<=M.mu;i++)
printf("第%d行的第一個非零元素是本矩陣第%d個元素\n",i,M.rpos[i]);
}//PrintSMatrix
Status MultSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q)
{ // 求稀疏矩陣乘積Q=M*N。
int arow,brow,p,q,ccol,ctemp[MAXRC+1];
if(M.nu!=N.mu) // 矩陣M的列數應和矩陣N的行數相等
return ERROR;
Q.mu=M.mu; // Q初始化
Q.nu=N.nu;
Q.tu=0;
M.rpos[M.mu+1]=M.tu+1; // 爲方便後面的while循環臨時設置
N.rpos[N.mu+1]=N.tu+1;
if(M.tu*N.tu!=0) // M和N都是非零矩陣
{
for(arow=1;arow<=M.mu;++arow)
{ //從M的第一行開始,到最後一行,arow是M的當前行
for(ccol=1;ccol<=Q.nu;++ccol)
ctemp[ccol]=0; //Q的當前行的各列元素累加器清零
Q.rpos[arow]=Q.tu+1; //Q當前行的第1個元素位於上1行最後1個元素之後
for(p=M.rpos[arow];p<M.rpos[arow+1];++p)
{ // 對M當前行中每一個非零元
brow=M.data[p].j; //找到對應元在N中的行號(M當前元的列號)
for(q=N.rpos[brow];q<N.rpos[brow+1];++q)
{
ccol=N.data[q].j; //乘積元素在Q中列號
ctemp[ccol]+=M.data[p].e*N.data[q].e;
}//for
} //求得Q中第arow行的非零元
for(ccol=1;ccol<=Q.nu;++ccol) //壓縮存儲該行非零元
if(ctemp[ccol])
{
if(++Q.tu>MAXSIZE)
return ERROR;
Q.data[Q.tu].i=arow;
Q.data[Q.tu].j=ccol;
Q.data[Q.tu].e=ctemp[ccol];
}//if
}//for
}//if
return OK;
}//MultSMatrix
int main()
{
RLSMatrix M,N,Q;
CreateSMatrix(M);
CreateSMatrix(N);
MultSMatrix(M,N,Q);
PrintSMatrix(Q);
DestroySMatrix(M);
DestroySMatrix(N);
DestroySMatrix(Q);
return OK;
}
轉自:http://blog.163.com/zhoumhan_0351/blog/static/39954227201001112526244/