一、二维数组
老师讲二维数组其实核心是说明了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元素,这个元素前面的就不需要遍历了,从而省去了一些遍历时间。