矩阵的压缩存储


矩阵压缩:

指为多值相同的元素只分配一个存储空间

对零元素不分配空间

特殊矩阵的压缩存储
  • 对称矩阵

定义:A[i][j] = A[j][i]

开辟空间大小:n * n 个元素压缩至n*(n-1)/2个元素

  • 使用一维数组s[k]以行来存储对称矩阵A(i,j)的下三角元素,则
  • 存储与实现:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
/**对称矩阵压缩存储**/
void PrintMatrix(double *s,int n);
/**存储对称矩阵**/
void storeMatrix(int n){
	double *s = new double[n];
	int len = n*(n+1)/2;
	printf("Input the UP_Matrix's element by the order of row\n");
	for(int i = 0;i<len;i++){
		scanf("%d",&s[i]);
	}
	PrintMatrix(s,n);
}
/**打印矩阵**/
void PrintMatrix(double *s,int n){
	int k = 0;
	for(int i =0;i<n;i++){
		for(int j = 0;j<n;j++){
			//以行为主存储下三角矩阵
			if(i>=j)	k = i*(i+1)/2+j;
			else	k = j*(j+1)/2+i;
			printf("%d\t",s[k]);
		}
		printf("\n");
	}
	
}
void main(){
	int n = 0 ;
	printf("Input the order of the matrix:");
	cin >> n;
	storeMatrix(n);
	system("pause");
}
  • 三角矩阵
  • 分为上三角矩阵,下三角矩阵
  • 与对称矩阵差别不大,仅在上三角矩阵中 矩阵的存储相当于对称矩阵的以列为主的压缩存储k = i*(2n-i+1)/2 + i
  • 对角矩阵

所有非零元素集中在主对角线两侧的带状区域内。m对角矩阵是指非零元素在每行中有m个。


机器学习 矩阵压缩 矩阵压缩的意义_机器学习 矩阵压缩

  • 特点:
  • 第一行和最后一行有两个非零元素,第2~n-1行有3个非零元素
  • 使用一维数组存储,需要存储3*n-2个元素
  • 一维数组s[k]A[i][j]的对应关系为:k = 2*i+j
稀疏矩阵的压缩存储

稀疏矩阵:非零元较零元少,且分布没有一定规律的矩阵

压缩存储原则:只存矩阵行列维数,和非零元的行列下标及值

  • 三元组表

即用顺序存储结构表示三元组表,记录非零元素的行和列号

  • 类型定义【行优先存储】:
#define MAXSIZE 100
Typedef struct{
    int i,j;
    ElemType e;
}Tripe;

Typedef struct{
    Tripe data[MAXSIZE+1];
    int mu,nu,tu;
}TSMatrix;
  • 转置的实现:
  • 方法一、常规思路
  • 将矩阵的行、列维数互换
  • 将每个三元组中的i 和 j 相互调换
  • 重排三元组次序
bool TransposeSMatrix(TSMatrix M, TSMatrix T)
{
    T.mu = M.nu;T.nu = M.mu; T.tu = M.tu;
    if(M.tu)
    {
        q = 1;
        for(int col = 1; col <= M.mu; 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++;
                }
            }
        }
     }else 
        return false;
    return true;
}

其实,效率并不高,基本操作是将M.data中的元素转到T.data中,利用两个循环完成,多次遍历M.data。

所以,我们就想,能不能在只扫描一次的基础上,完成M到T的转置?

  • 方法二、快速转置算法
  • M中非零元以行为主序存储,故各列的分零元的存储位置不是“连续”,但是,存储顺序却是一致的。
  • 思路:
  • M中各列非零元个数num[ ];
  • M中各列第一个非零元在T.data中的下标cpos[ ];
  • M.data进行一次扫描, 遇到col列的第一个非零元三元组时,按cpos[col ]的位置,将其放至T.data中,当再次遇到col列的非零元三元组时,只须顺序放到col列元素的后面
bool FastTransposeSMatrix(TSMatrix M, TSMatrix T)
{
    T.mu=M.nu; T.nu=M.mu; T.tu=M.tu;
	if(M.tu)
    {
       for (col=1; col<=M.nu; ++col)   			        	
           	num[col]=0;
       for (t=1; t<=M.tu; ++t) 
            ++num[M.data[t].j];      //求M中每一列非零元个数
    //求第 col列中第一个非零元在T.data中的序号
    cpot[1]=1;
    for(col=2; col<=M.tu;  ++col) 
        cpot[col]=cpot[col-1]+num[col-1];
    for(p=1; 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];
    }
    }
}//只有在tu非常小的时候才有意义
  • 十字链表
  • 当矩阵的非零元个数和位置在操作过程中变化较大时,采用链式存储的方式,设行指针数组和列指针数组,分别指向行、列第一个非零元,构成十字链表。
  • 类型定义:
typedef struct OLNode{
    int i,j;
    ElemType e;
    struct OLNode *right,*down;
}OLNode,*OList;

typedef struct{
    OLink *Rhead,*Chead;
    int mu,nu,tu;
}CrossList;
  • 建立稀疏矩阵并用十字链表存储
bool CreateSMatrix(CrossList M)
{
    OLNode *p,*q;
    cin >> m >> n >> t;//读入行、列及非零元个数
    M->mu = m;M->nu = n;M->tu = t;
    //声明指针数组,并初始化为NULL
    M->Rhead = (OLink*)malloc(m*sizeof(OLink));
    M->Chead = (OLink*)malloc(n*sizeof(OLink));
    for(int k = 0; k < m; k++)
        M->Rhead[k] = NULL;
    for(int k = 0; k < m; k++)
        M->Chead[k] = NULL;
    /*下面进行结点的输入
    操作为 拿到一个结点,分别插入 行链数组相应的行链表,及列链数组相应的列链表,插入两种情况【以行链表为例】,一种为行链表为空,或是要插入结点列大小小于链表已有结点的列大小,插在前面;另一种为 列数较大,需插在后面,一个循环去搜应该插到哪里
    */
    for(int index = 0; index < t; index++)
    {
        cin >> i >> j >> e;
        p = (OLink)malloc(sizeof(OLNode));
        p->i = i;
        p->j = j;
        p->e = e;
        if(M->Rhead[i] == NULL || M->Rhead[i]->j > j)
        {
            p->right = M->Rhead[i];
        	M->Rhead[i] = p;
        }else
        {
            for(q = M->Rhead[i];q->right && q->right->j < j;q = q->right)
            ;
        p->right = q->right;
        q->right = p;
        }
        if(M->Chead[j] == NULL || M->Chead[j]->i > i)
        {
            p->down = M->Chead[j];
            M->Chead[j] = p;
        }else 
        {
            for(q = M->Chead[j]; q->down && q->down->i < i; q = q->down)
                ;
            p->down = q->down;
            q->down = p;
        }
    }
    return true;
}