矩阵相乘--高效实现方法

假如有两个矩阵M,N相乘,Q= M X N,M是m1 X n1矩阵, N是 m2 X n2。 只有当n1 = m2时,才可以进行两个矩阵的相乘。一般的做法是:(1)我们用两个二维数组分别存放 M 和 N ,然后再用一个二维数组 存放 Q。实现 Q = M X N的算法如下:(下标从1开始)

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 ] * N[ k ][ j ];

    }

这种方法需要的内存空间是:sizeof( ElemType ) * ( m1 * n1 + m2 * n2 + m1 * n2)

时间复杂度分别是:O( m1 * n2 * n1);  

假如M和N都是稀疏矩阵(非零元素很少),我们假设M 的是 50 X 60的矩阵,非零元素为:10。 N 是 60 X 60的矩阵,非零元素为:10。因为两个元素相乘,其中一个为零结果也就为零。我们要的只是非零的数据。如果按上面那种方法,将会浪费很多空间。于是我们考虑用另一种方式存储矩阵。那就是典型的“三元组”,用个结构体表示这种存储结构。一个存行坐标,一个存列坐标。一个存它的值。这就是著名的三元组法。可以得出如果用三元组存储以前数据,需要的空间是 M 和N都只需要 10 * sizeof(struct)。比起存储 60 x 60和60 X 50的空间节省了很多。这从空间上解决了问题。两外如果用三元组方法存储数据(非零数据)也方便我们做乘法运算,减少了零元素的相乘。这不是一举两得吗。

  三元组结构:

#define  MAXSIZE  12500
struct  Triple {
     int  i, j;            //该非零元的行下标和列下标
     ElemType  e;    // 该非零元的值,ElemType表示元素的类型。如:int ,double
 } ;  // 三元组类型

 struct   TSMatrix{
     Triple  data[MAXSIZE + 1];    //把非零元的三元组对象放在一个数组中,一般我们按“以行序为主序”(顺序是先存第一行非零元,

                                                                                                                                                                            再存第二行)
      int     mu, nu, tu;                 // mu , nu ,tu 分别表示行数,列数,非零元个数
} ;  // 稀疏矩阵类型

在这里还有一个很重要的作法,因为我们把非零元素按三元组的形式存放在数组中,而且是按行序为主序存放的。所以为了方便快速知道第x行的开头在数组中第几个位置,我们引入了一个数组Cpot[]。num[]数组存的是每行非零元的个数。它用来存放每一行的开头非零元素在数组data的位置(既是下标)。作法如下 三元组M和N

///MN的Cpot

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];

}

cpot[1] = 1;
for (col=2; col<=M.nu; ++col)
cpot[col] = cpot[col-1] + num[col-1];

N的Cpot

if (N.tu) {
    for (col=1; col<=N.nu; ++col)  num[col] = 0;
    for (t=1; t<=M.tu; ++t)  ++num[M.data[t].j];

}

cpot[1] = 1;
for (col=2; col<=N.nu; ++col)
cpot[col] = cpot[col-1] + num[col-1];

 

到这里前面要处理的步骤基本已经完成。接下来就是实现相乘的算法。下面算法有点难理解,但我们知道它的思想就够了

Status MultSMatrix (TSMatrixM, TSMatrixN, TSMatrix&Q)

{
   if (M.nu != N.mu) return ERROR;
   Q.mu = M.mu; Q.nu = N.nu; Q.tu = 0;
   if (M.tu*N.tu != 0) {   // Q是非零矩阵
      for (arow=1; arow<=M.mu; ++arow)

       {
              ctemp[] = 0;                 // 当前行各元素累加器清零
            Q.rpos[arow] = Q.tu+1;      ///为Q也弄个rpos
            for (p=M.rpos[arow]; p<M.rpos[arow+1];++p) {
   //对当前行中每一个非零元
    brow=M.data[p].j;//找到对应元在N中的行号         
    if (brow < N.nu )  t = N.rpos[brow+1];
        else  { t = N.tu+1 }
        for (q=N.rpos[brow];  q< t;  ++q) {
          ccol = N.data[q].j;            // 乘积元素在Q中列号
          ctemp[ccol] += M.data[p].e * N.data[q].e;
        } // for q
      } // 求得Q中第crow( =arow)行的非零元
      for (ccol=1; ccol<=Q.nu; ++ccol) if (ctemp[ccol]) {
        if (++Q.tu > MAXSIZE) return ERROR;
        Q.data[Q.tu] = {arow, ccol, ctemp[ccol]};
      } // 压缩存储该行非零元

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值