矩阵(稀疏矩阵)的压缩存储

为节省存储空间并且加快处理速度,需要对这类矩阵进行压缩存储,压缩存储的原则是:不重复存储相同元素;不存储零值元素。

一、相关概念

㈠特殊矩阵:矩阵中存在大多数值相同的元,或非0元,且在矩阵中的分布有一定规律。

⒈对称矩阵:矩阵中的元素满足

                   aij=aji    1i,jn

⒉三角矩阵:上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元素均为常数c0n阶矩阵。

⒊对角矩阵(带状矩阵):矩阵中所有非0元素集中在主对角线为中心的区域中。

㈡稀疏矩阵:非0元素很少(≤ 5%)且分布无规律。

二、存储结构及算法思想

1、对称矩阵

存储分配策略: 每一对对称元只分配一个存储单元,即只存储下三角(包括对角线)的元,所需空间数为:  n(n+1)/2

存储分配方法: 用一维数组sa[n(n+1)/2]作为存储结构。

             sa[k]aij之间的对应关系为:

35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

2、三角矩阵

也是一个n阶方阵,有上三角和下三角矩阵。下()三角矩阵是主对角线以上(下)元素均为零的n阶矩阵。设以一维数组sb[0..n(n+1)/2]作为n阶三角矩阵B的存储结构,仍采用按行存储方案,则B中任一元素bi,jsb[k]之间仍然有如上的对应关系,只是还需要再加一个存储常数c的存储空间即可。如在下三角矩阵中,用n(n+1)/2的位置来存储常数。

35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

对特殊矩阵的压缩存储实质上就是将二维矩阵中的部分元素按照某种方案排列到一维数组中,不同的排列方案也就对应不同的存储方案

2、稀疏矩阵

常见的有三元组表示法、带辅助行向量的二元组表示法(也即行逻辑链表的顺序表),十字链表表示法等。

1)、三元组表示法

三元组表示法就是在存储非零元的同时,存储该元素所对应的行下标和列下标。稀疏矩阵中的每一个非零元素由一个三元组(i,j,aij)唯一确定。矩阵中所有非零元素存放在由三元组组成的数组中。

35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地
35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

在此,data域中表示非零元的三元组是以行序为主序顺序排列的。

以下看如何利用三元组表示法来实现矩阵的转置。

35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

1)按照b.data中三元组的次序依次在a.data中找出相应的三元组进行转置。换句话说,按照矩阵M的列序进行转置。为了找到M的每一列中所有的非零元素,需要对其三元组表a.data从第一行起整个扫描一遍。由于a.data是以M的行序为存放每个非零元的,由此得到的恰好是b.data应有的顺序。

2)、带辅助行向量的二元组表示法及十字链表表示法在下一节中学习介绍。

三、存储结构及C语言描述

1三元组表示法

1按照b矩阵中的行次序依次在a.data中找到相应的三元组进行转置。35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

2)快速转置:按照a.data中三元组的次序进行转置,并将转置后的三元组放到b.data中的恰当位置。

    恰当位置的确定:首先计算M矩阵的每一列(即T的每一行)中非0元的个数,然后求得M矩阵每一列第一个非0元在b.data中的位置。

    算法基本思想:

    设置两个向量:

     num[col]:col列的非零元素个数。

     cpot[col]:col列第一个非零元在b.data中的恰当位置。

         在转置过程中,指示该列下一个非零元在b.data中的位置。

    1num[col]的计算:

         顺序扫描a.data三元组,累计各列非0元个数。

    2cpot[col]计算:

35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地35、矩阵(稀疏矩阵)的压缩存储(一) - 墨涵 - 墨涵天地

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.dataN.data中找到相应的各对元素进行相乘即可。此时矩阵的存储结构是

36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地

         在此,我们引入rpos[row]用来指示矩阵N的第row行中第一个非零元在N.data中的序号,则rpos[row+1]-1指示矩阵的第row行中最后一个非零元在N.data中的序号。而最后一行中最后一个非零元在N.data的位置就是N.tu了。
36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地
36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地

     如上图中,Nrpos值为:

36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地

二、C语言描述

36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地

36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地

36、矩阵(稀疏矩阵)的压缩存储(二) - 墨涵 - 墨涵天地

三、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(使M000个非零元素的矩阵)

   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=MN

   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) // MN都是非零矩阵

   {

     for(arow=1;arow<=M.mu;++arow)

     { //M的第一行开始,到最后一行,arowM的当前行

       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/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值