9 二维数组&矩阵乘&稀疏矩阵的快速转置

本文介绍了C语言中二维数组的存储特性,包括行优先和列优先的内存布局。讨论了矩阵乘法的基本算法,并对比了不同矩阵访问方式的影响。此外,还探讨了稀疏矩阵转置的优化方法,通过三元组顺序表实现快速转置,避免不必要的遍历,提高效率。
摘要由CSDN通过智能技术生成

一、二维数组

老师讲二维数组其实核心是说明了C语言定义多维数组的一些特点。

1.在栈里面直接定义一个数组,无论是多少维,都是一维的,在内存空间中都是连续的。

2.在栈里面定义一个二维数组,在C语言中是行优先,所以在内存中先排行,对于科学计算经典语言Fortran,是列优先。

3.在堆中使用malloc申请空间,一维数组很简单。但是多维数组其实是有问题的。行与行之间是不连续的。如果想要得到全部连续的内存,这个还需要另外再去对一位申请出来的一级指针进行操作。

二、矩阵乘

矩阵是个好东西啊。几乎可以说是线性代数的基础了,但线性代数又是很多科学计算程序中所应用的,以至于开发出了需要高性能的线代库,BLAS是其中的一个典型。矩阵乘的朴素算法很简单,三层for循环:

static void _local_gemm_rrr(const float *A, const int LDA, const float *B, const int LDB, float *C, const int LDC, int M, int N, int K)
{
    for (int i = 0; i < M; i++)
        for (int j = 0; j < N; j++)
            for (int k = 0; k < K; k++)
                C[i * LDC + j] += A[i * LDA + k] * B[k * LDB + j];
}

这是rrr形式的矩阵乘算法,r代表row,A,B,C矩阵的访问方式都是Row优先。

同样还有rcr形式的矩阵乘:代表A矩阵行优先,B矩阵列优先,C矩阵行优先

static void _local_gemm_rcr(const float *A, const int LDA, const float *B, const int LDB, float *C, const int LDC, int M, int N, int K)
{
    for (int i = 0; i < M; i++)
        for (int j = 0; j < N; j++)
            for (int k = 0; k < K; k++)
                C[i * LDC + j] += A[i * LDA + k] * B[k + j * LDB];
}

这个算法的时间复杂度是O(n3),其实还有一些其他的方法不需要改变算法的复杂度,更改一些算法的层次,使其更好地利用硬件和Cache,在实际的应用中也可以获得很好的加速情况。

三、稀疏矩阵的转置

实际应用中很多矩阵的元素都为0,一个高维矩阵其实没有多少个有用元素,这个时候就用到了三元组和一个包括矩阵行数列数和一个三元组数组。

所谓三元组 是用来储存有效元素在矩阵中的行位置列位置和value值。

如果朴素地对稀疏矩阵进行转置,只需要将三元组中的i和j调换顺序,然后按照行的大小进行排序。但是有一些人呀,就采用了一种比较调转的利用三个特征向量来进行简化的一种转置。

上代码

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 1250
 
#define    OK      1
#define    ERROR   0
#define    TRUE    1
#define    FLASE   0
 
typedef    int     ElemType;
 
typedef struct{
    int   i, j;       //该非零元的行下标和列下标
    ElemType e;       //非零元对应的值
}Triple;
 
typedef struct{
    Triple   data[MAXSIZE];       //非零元三元组表
    int      mu, nu, tu;            //矩阵的行数,列数,非零元个数
}TSMatrix;
 
void FastTransposeSMatrix(TSMatrix M, TSMatrix &T)              //快速转置
{                                                      //采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
    T.mu = M.nu;
    T.nu = M.mu;
    T.tu = M.tu;
    if(T.tu)
    {
        int col;
        int num[100], cpot[100];
        for (col = 0; col < M.nu; col++)
            num[col] = 0;                 //num数组的初始化
        for (int t = 0; t < M.tu; t++)
            ++num[M.data[t].j];         //求M中每一列含有的非零元个数
        cpot[0] = 0;
        for (col = 1; col < M.nu; col++)
            cpot[col] = cpot[col - 1] + num[col - 1];         //求cpot向量
        int q;
        for (int p = 0; 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];
        }//for
    }//if
    return;
}//FastTransposeSMatrix
 
int main()
{
    TSMatrix M;
    TSMatrix T;
    scanf("%d %d", &M.mu, &M.nu);
    int x,y,z;
    M.tu=0;
    for (int i = 0; ; i++)
    {
        scanf("%d%d%d", &x, &y, &z);
        if(x==0&&y==0&&z==0)break;
        M.data[i].i=x;M.data[i].j=y;M.data[i].e=z;
        M.tu++;
    }
 
    FastTransposeSMatrix(M, T);
 
    for (int t = 0; t < T.tu; t++)
        printf("%d %d %d\n", T.data[t].i, T.data[t].j, T.data[t].e);
    return 0;
}

为什么叫做快速转置,本质就是可以快速找到转置后每一行的第一个非0元素,这个元素前面的就不需要遍历了,从而省去了一些遍历时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值