矩阵的压缩存储

三元组表 矩阵转置 快速转置

Posted by lvyonghao on December 24, 2018

矩阵的压缩存储

在编写程序时往往都是二维数组表示矩阵,然而在数值分析中经常出现一些阶数很高的的矩阵同时在距震中有很多值相同的元素,或者是零元素,为了节省空间,可以对这类矩阵进行压缩存储,所谓的压缩存储就是,多个值相同的元之分配一个存储空间,对零元不分配空间。 若值相同的元素或零元素在矩阵中分布有一定规律,则称矩阵为特殊矩阵,反之称为稀疏矩阵。


三元组

按照压缩存储的概念,只存储非零元素,所以需要储存非零元素的值,以及它在矩阵中所在的位置(i,j),一个三元组(i,j,aij)唯一确定了矩阵的一个非零元。


三元组顺序表存储结构

每个三元组需要非零元的下标,以及存放的数据。整个三元组表包括n个三元组以及三元组表信息,矩阵行数,列数,非零元个数。

typedef int Element;
#define MAXSIZE 12500   //假设非零元素的最大值为12500
typedef struct Triple{
    int i,j;    //该非零元素的行下标和列下标
    Element e;
}Triple;
typedef struct TSMatrix{
    Triple data[MAXSIZE + 1];   //非零元三元组表,data【0】未用
    int mu,nu,tu;   //矩阵的行数,列数和非零元素g个数
}TSMatrix;

创建稀疏矩阵

先创建一个系数矩阵,初始化行列数,以及非零元素个数,根据非零元素个数,输入元素所在位置,以及元素值。

//创建稀疏矩阵
TSMatrix CreateSMatrix(){
    TSMatrix S;
    int i,j,e,k;
    printf("情输入矩阵的矩阵行数列数和非零元素个数 eg: 5 5 5 表示创建一个5x5的非零元素为5的矩阵");
    scanf("%d %d %d",&S.mu,&S.nu,&S.tu);
    printf("%d %d %d",S.mu,S.nu,S.tu);
    printf("请输入所创建矩阵数据所在位置 eg:3 2 4两行三列数值为4。按照先行后列输入");
    for(k = 1;k <= S.tu; k++){
        scanf("%d %d %d", &i, &j, &e);
        S.data[k].i = i;
        S.data[k].j = j;
        S.data[k].e = e;
    }
    return S;
}

打印稀疏矩阵

初始设置变量k,用来遍历三元组表,通过两个for语句遍历整个稀疏矩阵,若对应的(i,j)等于在三元组表data【k】中的(i,j),则该位置有非零元素,输出该非零元素,k自增,否则输出零元素。在打印一行后换行,整个矩阵打印结束后也要换行。下面说明打印稀疏矩阵:

//打印稀疏矩阵
void PrintSMatrix(TSMatrix T){
    int i,j,k = 1;
    for(i = 1;i <= T.mu; i++) {     //行
        for (j = 1; j <= T.nu; j++) {       //列
            if (T.data[k].i == i && T.data[k].j == j) {     //对应ij输出e否则输出0;
                printf("%d   ", T.data[k].e);
                k++;
            } else {
                printf("0   ");
            }
        }
        printf("   \n");
    }
    printf("\n");
}

矩阵的转置

矩阵的转置是矩阵的基本操作,我们需要准备一个全是零元素的矩阵存储转置矩阵,初始化转置矩阵的行列,以及非零元素个数,设置一个变量q用来遍历三元组表待转置矩阵,设置变量col代表列(待转置矩阵的列就是转置矩阵的行),通过for循环列,通过for循环待转置矩阵的所有三元组,判断待转置矩阵的元素的列是否是要转置的列(保证从第一列开始转置),若是则:转置矩阵非零元素位置等于待转置矩阵行列交换的位置,并存储非零元素。下面说明转置矩阵:

TSMatrix TRansposeSMatrix(TSMatrix M,TSMatrix T){
    //采用三元组表存储表示,求稀疏矩阵M的转置矩阵T。
    T.mu = M.nu;
    T.nu = M.mu;
    T.tu = M.tu;
    if(T.tu){
        int q = 1;
        int col = 0;
        for (col = 1; col <= M.nu ; ++col){
            for(int p = 1;p <= M.tu; ++p){  
                if(M.data[p].j == col){    //从第一列开始转置
                    T.data[p].i = M.data[q].j;
                    T.data[p].j = M.data[q].i;
                    T.data[p].e = M.data[q].e;
                    ++q;
                }
            }
        }
    }
    return T;
}   //TransposeSMatrix

快速转置

若果能够预先确定待转置矩阵M中每一列的的非零元素,在转置矩阵T三元组表的位置,那么转置的时候,便可以直接放到相应的位置上。为了确定位置,在转置之前必须求得待转置矩阵M的每一列中非零元素个数,进而求得每一列的第一个非零元素在转置矩阵T中应有的位置,下面说明快速转置矩阵:。

TSMatrix FastTransposeSMatrix(TSMatrix M,TSMatrix T){
    //采用三元组顺序表存储表示,求稀疏矩阵M的的转置矩阵T。
    int col,t,cpot[MAXSIZE],p,q,num[MAXSIZE];
    T.mu = M.mu;
    T.nu = M.mu;
    T.tu = M.tu;
    if(T.tu){
        for(col = 1;col <= M.nu;++col){ //初始化num,每列的非零元素为零
            num[col] = 0;
        }
        for(t = 1;t <= M.nu; ++t){
            ++num[M.data[t].j]; //求M中每一列含非零元素个数 对应的j对应列对应num[序列号]自增
        }
        cpot[1] = 1;
        //求第col列中第一个非零元素在b.data中的序号
        for(col = 2 ; col <= M.nu; ++col){
            cpot[col] = cpot[col - 1] + num[col - 1];   //序号为之前的加上非零元素
        }
        for(p = 1;p <= M.tu; ++p){
            col = M.data[p].j;  //把每一个非零元素的列赋值给col
            q = cpot[col];  //把col列的非零元素位置赋值给q
            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];    //第col列后一个非零元素在b.data的序号
        }   //  for
    }   // if
    return T;
}

附上矩阵压缩存储完整代码Triple