数据结构-稀疏矩阵的乘法(C语言)

引言

稀疏矩阵是什么?人们无法给出确切的定义,它只是一个凭人们的直觉来了解的概念。

假设在m×n的矩阵中,有t个元素不为零。令σ=\frac{t}{m×n},称σ为矩阵的稀疏因子。通常认为σ<=0.05时称为稀疏矩阵。

三元组顺序表

// 稀疏矩阵的三元组顺序表储存表示
typedef struct
{
    int i, j; // 非零元的行下标和列下标
    int e;
} Triple;

typedef struct
{
    Triple data[MAXSIZE + 1]; // 非零元三元组表,data[0]未用
    int ru, cu, nu;           // 矩阵的行数,列数,非零元个数
} TSMatrix;

在讲矩阵乘法之前我们先讨论矩阵的转置,因为其算法很相近。

矩阵转置

转置运算时一种最简单的矩阵运算,对于一个m×n的矩阵M,它的转置矩阵T是一个n×m的矩阵,且T(i,j)=M(j,i),1<=i<=n,1<=j<=m。

对于一个三元组顺序表储存的矩阵M,

1212
139
31-3
3614
4324
5218
6115
64-7

它的转置矩阵T应该是

13-3
1615
2112
2518
319
3424
46-7
6314

由图可知,M和转置后的T都是按顺序排列的,我们应该从M中找出列为1(也就是在T中行为1)的元素,由于是顺序表,我们不用担心T中同一行列数的顺序,每找到一个,它就是T中的一个元素,并且M.data[i].i=T.data[cnt].j ; M.data[i].j=T.data[cnt],i ; M.data[i].e=T.data[cnt].e ; cnt++;代码如下:

void TransposeSMatrix(TSMatrix M)
{
    T.ru=M.ru,T.cu=M.cu,T.nu=M.nu;
    int cnt=1;
    for(int j=1;j<=T.cu;j++)  // 以列为单位开始遍历
    {
        for(int i=1;i<=M.nu;i++)  //遍历M的每一个元素
        {
            if(M.data[i].j==j)
            {
                T.data[cnt].i=j;
                T.data[cnt].j=M.data[i].i;
                T.data[cnt].e=M.data[i].e;
                cnt++;
            }
        }
    }
    for(int i=1;i<=T.nu;i++)
    {
        printf("%d %d %d\n",T.data[i].i,T.data[i].j,T.data[i].e);
    }
}

矩阵转置的改进

上面算法的时间复杂度为O(cu*nu),我们在每次开始新一列的时候都会遍历一遍M矩阵,T矩阵是从1开始的,但是如果我们知道M矩阵中每一列元素的第一个非零元在T中的位置以及每一列非零元的个数,问题就解决了!

此时我们只需遍历M矩阵的每一个元素,就知道对应列在T矩阵的位置,定义T,此时T矩阵不是从1开始的

我们有cpot[M.cu]表示M矩阵某一列第一个非零元在T中的位置,num[MAXSIZE]表示某一列非零元的个数。

cpot[1]=1;

cpot[col]=cpot[col-1]+num[col-1];

int num[MAXSIZE];
void TransposeSMatrix_(TSMatrix M)
{
    T.ru = M.ru, T.cu = M.cu, T.nu = M.nu;
    int cnt = 1;
    int cpot[T.cu];
    for (int i = 1; i <= M.nu; i++)
    {
        num[M.data[i].j]++;
    }
    cpot[1] = 1;
    for (int i = 2; i <= M.cu; i++)
    {
        cpot[i] = cpot[i - 1] + num[i - 1];
    }
    for (int i = 1; i <= M.nu; i++)
    {
        T.data[cpot[M.data[i].j]].i=M.data[i].j;//M矩阵中第j列的元素在T中的位置
        T.data[cpot[M.data[i].j]].j=M.data[i].i;
        T.data[cpot[M.data[i].j]].e=M.data[i].e;
        cpot[M.data[i].j]++;
    }
    for (int i = 1; i <= T.nu; i++)
    {
        printf("%d %d %d\n", T.data[i].i, T.data[i].j, T.data[i].e);
    }
}

矩阵乘法

根据线性代数的知识,我们知道两个矩阵相乘:

Q=M×T

其中,M是m1×n1矩阵,T是m2×n2矩阵,当n1=m2时有:

for (i = 1; i <= m1; i++)
{
    for (j = 1; j <= n2; j++)
    {
        Q[i][j] = 0;
        for (k = 1; k <= n1; k++)
            Q[i][j] += M[i][k] * T[k][j];
    }
}

此时算法的时间复杂度时O(m1*n2*n1)。

在三元组顺序表中该乘法变现为:

void MultSMatrix(TSMatrix M, TSMatrix T)
{
    if (M.cu == T.ru)
    {
        Q.ru = M.ru, Q.cu = T.cu;
        int cnt = 1;
        for (int i = 1; i <= M.nu; i++)
        {
            for (int j = 1; j <= T.nu; j++)
            {
                if (M.data[i].j == T.data[j].i)
                {
                    Q.data[cnt].i = M.data[i].i;
                    Q.data[cnt].j = T.data[i].j;
                    Q.data[cnt].e = (M.data[i].e) * (T.data[j].e);
                    cnt++;
                }
            }
        }
        Q.nu = cnt - 1;
        for (int i = 1; i <= Q.nu; i++)
            printf("%d %d %d\n", Q.data[i].i, Q.data[i].j, Q.data[i].e);
    }
}

举个例子,比如M=

113
145
22-1
312

T=

122
211
31-2
324

在做矩阵乘法时,我们遍历到了M和T中的每一个元素,当M中元素的列数和T中元素的行数相同时,他们就可以相乘,此时Q矩阵的行数等于M矩阵的行数,Q矩阵的列数等于T矩阵的列数,Q矩阵的值等于M和T的值的乘积。

此时算法的时间复杂度是O(nu*nu)。

矩阵乘法的改进

我们想一想刚才的转置,我们在相乘时是不是也可以保存一些值来减少我们的计算量呢,其实思路和转置一摸一样!

我们如果知道M矩阵每一列元素在T中的位置,我们就不需要费那么大劲去找了!

我们有cpos[M.cu]为M矩阵中某一列在T矩阵中对应行的位置,num[MAXSIZE]为M矩阵某一列在T矩阵中对应行的个数

cpos[1]=num[1];

cpos[col]=cpos[col-1]+num[col-1];

int num[MAXSIZE];
void MultSMatrix_(TSMatrix M, TSMatrix T)
{
    if (M.cu == T.nu)
    {
        int cpos[T.ru + 1];
        int cnt = 1;
        for (int i = 1; i <= T.nu; i++)
        {
            num[T.data[i].i]++;
        }
        cpos[1] = num[1];
        for (int i = 2; i <= T.ru; i++)
        {
            cpos[i] = cpos[i - 1] + num[i - 1];
        }
        for (int i = 1; i <= M.nu; i++)
        {
            int tmp = cpos[M.data[i].j];
            if (tmp && tmp < T.nu)
            {
                for (int j = 1; j <= num[M.data[i].j]; j++)
                {
                    Q.data[cnt].i = M.data[i].i;
                    Q.data[cnt].j = T.data[tmp].j;
                    Q.data[cnt].e += (M.data[i].e) * (T.data[tmp].e);
                    tmp++;
                }
                cnt++;
            }
        }
        Q.nu = cnt - 1;
        for (int i = 1; i <= Q.nu; i++)
            printf("%d %d %d\n", Q.data[i].i, Q.data[i].j, Q.data[i].e);
    }
}

对于M矩阵每一列在T矩阵对应行的乘积运算应该是累加的!

此时的时间复杂度是O(nu+ru+nu*num),其中num为矩阵T每一行非零元的个数。

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值