【稀疏矩阵乘法】行索引稀疏矩阵乘法改c++版(严蔚敏版)_行索引建立稀疏矩阵-程序员宅基地

技术标签: 算法  代码  稀疏矩阵  矩阵乘法  实现  数据结构  

行索引稀疏矩阵乘法(严蔚敏版)

原理

行索引稀疏矩阵查找某一列的元素没那么方便,所以在做矩阵乘法时(这里以M乘N=Q为例),严书的做法是:在求Q的某一行的值是,用M的一行去遍历N的每一行,对结果中同列的值进行累加,最后稀疏化存入Q的当前行中,这个过程还原成正常矩阵比较容易理解:
求Q(2,2)的第一行时,肯定是M的第一行和N的第一列逐乘再累加,然后再M的第一行和N的第二列逐乘累加
M(2,3)* N(3,2):
矩阵相乘
直接算的话就是:
Q[1,1] = 1*1 + 2*0 + 3*1 = 1+0+3 = 4
Q[1,2] = 1*0 + 2*1 + 3*1 = 0+2+3 = 5
这回我们直接同时求两列的值,其实就是改变一下计算顺序:
改变计算顺序
这个就相当于严书代码中ctemp的作用,只不过ctemp是直接累加.

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int MAX_E = 1e+4+5, MAX_R = 105;

//严版特色,下标从1开始
struct Triple{
    int i,j,e;};
struct RLSMatrix{
    
    Triple data[MAX_E];
    int rpos[MAX_R];//行首索引, rpos[i]指向第i行中的首元素在data中的序号, 则rpos[i+1]-1指向第i行中最后一个非0元素在N.data中的序号.
    int rows, cols, elems;
};

int ctemp[MAX_R];//Q的第i行元素结果累加器,算完一行就压缩存储进Q.data中


RLSMatrix mul(RLSMatrix M, RLSMatrix N){
    
    RLSMatrix Q;
    Q.rows = M.rows;Q.cols = N.cols;Q.elems = 0;
    if(M.elems * N.elems != 0){
                                  // Q是非零矩阵
        for(int arow = 1; arow <= M.rows; arow++){
    
            memset(ctemp, 0, sizeof(ctemp));              //到了下一行,清零
            Q.rpos[arow] = Q.elems+1;                  //新一行的行首索引是当前data中元素个数+1,从该处继续向data中存三元组

            int tp;      //用tp指向data中该行行末的下一个位置,方便遍历
            if(arow < M.rows) tp = M.rpos[arow+1];    //如果不是最后一行, tp指向下一行行首
            else tp = M.elems+1;   //最后一行, tp直接指向data中最后一个+1

            for(int p = M.rpos[arow];p < tp; p++){
      //对当前行找到的每一个非零元
                int brow = M.data[p].j;     //找到对应元在N中的行号(M中某行第三列的元素, 肯定和N中第三行某列的元素相乘)
                int t;//和tp同样的套路
                if(brow < N.rows) t = N.rpos[brow+1];
                else t = N.elems+1;

                for(int q = N.rpos[brow]; q < t; q++){
    //这里不是直接算出M的某行乘N的某列的值
                    //而是用M的某行,去遍历N的所有行,然后对每行的应该在同列的值进行累加
                    int ccol = N.data[q].j;
                    ctemp[ccol] += M.data[p].e * N.data[q].e;//累加器
                }
            }//求得Q中第crow(=arow)行的非零元

            for(int ccol = 1; ccol <= Q.cols; ++ccol){
    //用M的一行遍历完了整个N矩阵
                if(ctemp[ccol]){
    
                    Q.data[++Q.elems] = {
    arow, ccol, ctemp[ccol]};//压缩存储
                }
            }
        }
    }
    return Q;
}

RLSMatrix makeMat(){
    
    /*
     * 输入rows, cols, 三元组个数
     * 输入三元组
     */
    RLSMatrix A;
    cin>>A.rows>>A.cols>>A.elems;
    for(int i = 1;i <= A.elems;i++){
    
        int x,y,e;
        cin>>x>>y>>e;
        A.data[i] = {
    x,y,e};
    }

    /*
     根据乘法中这段代码写的初始化rpos数组:
         int tp;      //用tp指向data中该行行末的下一个位置,方便遍历
         if(arow < M.rows) tp = M.rpos[arow+1];    //如果不是最后一行, tp指向下一行行首
         else tp = M.elems+1;   //最后一行, tp直接指向data中最后一个+1
         for(int p = M.rpos[arow];p < tp; p++) ...
        可以看出如果某行没有元素,则让tp = M.rpos[arow]则可不进行遍历,也就是M.rpos[arow] = M.rpos[arow+1]
        而当最后几行为空时,并不会执行else tp = M.elems+1;这时应该把rpos最后几行空的填成M.elems+1
     */

    int row = 1;
    for(int e = 1;e <= A.elems; e++){
    
        int arow = A.data[e].i;
        while(row <= arow){
    
            A.rpos[row++] = e;
        }
    }
    while(row <= A.rows){
    //如果最后几行没有元素,让索引指到elems+1的位置
        A.rpos[row++] = A.elems+1;
    }
    return A;
}

void printMat(RLSMatrix A){
    
    cout<<"rows:"<<A.rows<<" cols:"<<A.cols<<" elems:"<<A.elems<<endl;
    cout<<"-------------------------"<<endl;
    cout<<"data:"<<endl;
    for(int i = 1;i <= A.elems;i++) {
    
        cout<<setw(4)<<A.data[i].i<<setw(4)<<A.data[i].j<<setw(4)<<A.data[i].e<<endl;
    }
    cout<<"-------------------------"<<endl;
    cout<<"rpos: ";
    for(int j = 1;j <= A.rows; j++){
    
        cout<<A.rpos[j]<<' ';
    }
    cout<<endl<<endl;
}

int main(){
    
    ios::sync_with_stdio(false);
    RLSMatrix A = makeMat();
    printMat(A);
    RLSMatrix B = makeMat();
    printMat(B);
    RLSMatrix C = mul(A, B);
    printMat(C);
    return 0;
}
/*
 严书上的例子
 3 4 4
 1 1 3
 1 4 5
 2 2 -1
 3 1 2

 4 2 4
 1 2 2
 2 1 1
 3 1 -2
 3 2 4
 */

结果

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ZAX130/article/details/103372269

智能推荐

初始化时checkbox选中问题-程序员宅基地

文章浏览阅读746次。首先我们大家在写页面的时候可能回经常遇到checkbox、radio等一些使选中或者是不选中的问题。这是我在项目当中做的时候发现的一个小知识点,把它赶紧记录下来。以便以后复习与巩固。 现把代码写出来再解释: function operateCheckOrRadio() { var sForm = document.getElementById("sform"); var sStatus = d..._flutter checkbox用变量初始化无法设置为选中状态

UE5——问题——MediaPlayer的使用播放视频注意点_ue mediaplayer-程序员宅基地

文章浏览阅读1.1k次。UE5——问题——MediaPlayer的使用播放视频注意点_ue mediaplayer

毕设仿真分享 单片机非接触式红外感应体温计-程序员宅基地

文章浏览阅读311次,点赞9次,收藏7次。非接触式电子体温计主要利用红外测温原理,一切温度高于绝对零度(-273.35℃)的物体,由于分子热运动,物体会不停地向外辐射能量。物体辐射能量的大小与它的表面温度有十分密切的关系。因此,通过测量物体辐射的能量,就能够测量出物体的温度。本用户手册中的非接触式电子体温计就是利用这种测量方法,实现测量人体体温的功能。

Vista/Win7下普通权限进程动态提升权限_findwindow 没有权限-程序员宅基地

文章浏览阅读2k次。本文出自 “碧海笙箫” 博客,请务必保留此出处http://pyhcx.blog.51cto.com/713166/197073一、前提在Vista/Win7下,加强了对安全的管理,对注册表修改,系统目录的文件操作,都需要管理员权限才能完成(当然虚拟存储机制,表面上也相当于能操作)。所以,对于程序中有相关操作的,这时候,就要求我们的程序必须拥有管理员权限。通过mainfest文件,我们可以让程序总是需要管理员权限执行,但是,这将导致程序每次运行时,都需要弹出UAC框老骚扰用户,另外,有时候我们的程序只是在某_findwindow 没有权限

PCS7 入门指南 v9.0 SP3 v9.1 中文版 学习资料 (官方公开可用资料)_pcs7v9.1-程序员宅基地

文章浏览阅读1.8w次,点赞13次,收藏82次。链接:https://pan.baidu.com/s/1-p4h_QDL8BN04tnn3vSkOA提取码:nou3PCS7入门指南v9.0含APL(包含PDF和项目文件)官方地址:SIMATIC 过程控制系统 PCS 7 入门指南第1部分 (V9.0,含APL)https://support.industry.siemens.com/cs/document/109756196/simatic-%E8%BF%87%E7%A8%8B%E6%8E%A7%E5%88%B6%E7%B3%BB..._pcs7v9.1

c# 调用非托管代码_c# 声明kernel32 函数-程序员宅基地

文章浏览阅读956次,点赞3次,收藏5次。编程过程中,一般c#调用非托管的代码有两种方式:1.直接调用从DLL中导出的函数。2.调用COM对象上的接口方法。首先说明第1种方式,基本步骤如下:1.使用关键字static,extern声明需要导出的函数。2.把DllImport 属性附加到函数上。3.掌握常用的数据类型传递的对应关系。4.如果需要,为函数的参数和返回值指定自定义数据封送处理信息,这将重写.net framework默认的封送处理。简单举例如下:托管函数原型:DWORD GetShortPathName(LPCTST_c# 声明kernel32 函数

随便推点

敏捷软件开发宣言-程序员宅基地

文章浏览阅读507次。敏捷软件开发宣言 摘要:我们正在通过亲身实践以及帮助他人实践,揭示更好的软件开发方法。通过这项工作,我们认为: 个体和交互 胜过 过程和工具 可以工作的软件 胜过 面面俱到的文档 客户合作 胜过 合同谈判 响应变化 胜过 遵循计划虽然右项也具有价值,但我们认为左项具有更大的价值。Kent Beck James Grenning Robert C._敏捷软件开发宣言

Android实现通用的ActivityGroup(效果类似Android微博客户端主界面)-程序员宅基地

文章浏览阅读93次。转自http://www.cnblogs.com/answer1991/archive/2012/05/08/2489844.html ActivityGroup在实际的开发中是十分常见的,在我使用过的Android应用中,十个应用里面有九个应用的主界面都是使用ActivityGroup的。说起ActivityGroup,在国内好像直接使用它开发的并不多,基本都是使用Ta..._android 类似于微博tab,动态添加tab,并实现拖拽排序,编辑等

git:一、GIT介绍+安装+全局配置+基础操作_请确保本地完成了 git 的全局配置-程序员宅基地

文章浏览阅读490次。仅供学习使用。执行命令git init,发现文件夹出现.git目录即说明创建本地仓库成功。创建的文件名为空,拓展名是bashrc,所以要开启文件的拓展名选项并检查该文件的格式是否为Bash RC源文件。工作区的文件创建或修改后通过git add提交到暂存区,暂存区的文件通过git commit提交到仓库。创建一个测试用的文件夹,进入后右键打开Git Bash,设置用户信息。ATT:红色的是工作区的文件,绿色的是暂存区的文件。GIT的流程分为三大块:工作区、暂存区、仓库。_请确保本地完成了 git 的全局配置

文件服务(SMB)共享详解-程序员宅基地

文章浏览阅读517次。三种共享类型:微软的CIFS文件共享协议,可以让windows机器在网上邻居之间共享文件,也即是windows-windows之间的文件共享;NFS文件共享协议,可以让远程共享的共享目录挂载在本地端,就像本地端的一个partition分区一样,但是共享也只能在UNIX-UNIX之间来共享;SAMBA文件共享协议,可以实现WINDOWS-UNIX之间的文件的共享,WINDOWS机器在网上..._smb共享只抓到nbns报文

鸿蒙系统第五批升级名单,鸿蒙系统首批升级机型名单曝光,很多人失望了,支持的机型太少...-程序员宅基地

文章浏览阅读983次。5月30日早间,“鸿蒙系统首批升级机型名单”这则消息在微博刷屏。按照微博博主爆料称,华为Mate40系列、Mate X2、P40系列、nova8系列以及华为平板MatePadPro将在首批升级鸿蒙操作系统。值得注意的是,今年4月,华为消费者业务AI与智慧全场景业务部副总裁杨海松表示,16%的市场份额是一条生死线。5月30日上午,记者实探位于深圳的华为全球旗舰店发现,目前展示的机型仍搭载安卓系统。工..._鸿蒙5升级名单

ES6——Map和Set-程序员宅基地

文章浏览阅读608次。ES6新增的数据类型,Map、Set之间的关系,如何使用?Map和Set的属性和方法。