【稀疏矩阵乘法】行索引稀疏矩阵乘法改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

智能推荐

GCC编译及GDB调试_编译gcc gdb-程序员宅基地

文章浏览阅读737次。目录前言一、GCC编译1.编译链接流程二、使用步骤1.引入库2.读入数据总结前言记录GCC和GDB的学习笔记一、GCC编译1.编译链接流程以File.c为例子File.i : 经过编译预处理的源代码;File.s : 汇编处理后的汇编代码;File.o : 编译后的目标文件,即含有最终编译出的机器码,但它里面所引用的其他文件中函数的内存位置尚未定义File.out:可执行文件C源程序头文件-->预编译处理(cpp)-->编译程序-->优化程序-->汇编程_编译gcc gdb

contest12 CF514div2 ooxxx oooox ooooo-程序员宅基地

文章浏览阅读1.3k次。未写转载于:https://www.cnblogs.com/ikihsiguoyr/p/10372635.html

决策表,决策树_决策表 决策树-程序员宅基地

文章浏览阅读293次。决策表决策表 - 百度百科决策表又称判断表,是一种呈表格状的图形工具,适用于描述处理判断条件较多,各条件又相互组合、有多种决策方案的情况。精确而简洁描述复杂逻辑的方式,将多个条件与这些条件满足后要执行动作相对应。但不同于传统程序语言中的控制语句,决策表能将多个独立的条件和多个动作直接的联系清晰的表示出来决策树决策树 - 百度百科决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运_决策表 决策树

【论文解读】语义分割&医学图像分割论文合集-程序员宅基地

文章浏览阅读4k次,点赞18次,收藏171次。description: 整理自己看过和待看的一些主要关于图像分割包括其他领域的论文,不定时更新…综述篇Deep learning for cardiac image segmentation: A review [2019]Deep Semantic Segmentation of Natural and Medical Images: A Review [2019]Understanding Deep Learning Techniques for Image Segmentation

iOS 上通过 802.11k、802.11r 和 802.11v 实现 Wi-Fi 网络漫游-程序员宅基地

文章浏览阅读848次。在 iOS 上通过 802.11k、802.11r 和 802.11v 实现 Wi-Fi 网络漫游了解 iOS 如何使用 Wi-Fi 网络标准提升客户端漫游性能。iOS 支持在企业级 Wi-Fi 网络上对客户端漫游进行优化。802.11 工作组标准 k、r 和 v 可让客户端在同一网络内更加顺畅地从一个接入点 (AP) 漫游到另一个接入点。802.11k通过创建优..._11v漫游流程 csdn

随便推点

就这样我成了一名程序员_flyfish365-程序员宅基地

文章浏览阅读1.6k次。 6月份,毕业了,炎炎夏日加上几只属苍蝇的知了,我知道这些并不是自己心里烦躁的理由,长吸一口气,我的未来在哪里?问题找到了,但答案在哪里? 宿舍的几位同僚,义无反顾的整理好皮箱奔向大城市去打天下,送走他们,告别已经分手的女朋友,拿着自己的三证和一堆宝贝技术书籍,决定先回家。 家里永远都是最温暖的,回家后第一次见到我刚出生几个月的外甥,这小子可爱极了,老乐呵_flyfish365

【u-boot-2015.10源码分析】board_r.c_config_board_early_init_r-程序员宅基地

文章浏览阅读1.7k次。文章链接:https://blog.csdn.net/q_z_r_s机器感知一个专注于SLAM、机器视觉、Linux 等相关技术文章分享的公众号/* 最终进入命令解析模式 */DECLARE_GLOBAL_DATA_PTR;ulong monitor_flash_len;static int run_main_loop(void){#ifdef CON..._config_board_early_init_r

python计算机毕设【附源码】毕业设计选题管理系统(django+mysql+论文)-程序员宅基地

文章浏览阅读280次,点赞4次,收藏3次。通过使用前端技术HTML+CSS+JavaScript+Vue,后端技术Java+SSM,数据库MySQL5.7,以及开发工具Eclipse或IntelliJ IDEA,Tomcat7.0,JDK1.8,Maven3.3.9,可以实现一个功能完善、操作简便的毕业设计选题管理系统。在数据库管理工具的选择上,使用了Navicat 11,这是一个用户友好且功能强大的数据库管理软件,它支持多种数据库系统,包括MySQL,并提供了图形化界面,使得数据库的管理和维护工作更加便捷。

图解通信原理与案例分析-17:2G GPRS通用分组无线业务详解_2g slot-程序员宅基地

文章浏览阅读4.7k次。先占个空,以后再详细拆解主要关注与GSM的区别,特别是GRPS是如何通过增加信道和分组交换系统支持数据传输,如何通过新的调制解调技术,增加数据传输的速率的!1. GSM是全球移动通讯系统(Global System for Mobile Communications)的简称2. GPRS是通用分组无线业务(General Packet Radio Service)的简称3. GPRS是在GSM系统基础上发展起来的分组数据承载和传输业务。4. GPRS与GSM......_2g slot

【图像拼接】论文精读:Natural Image Stitching Using Depth Maps-程序员宅基地

文章浏览阅读10w+次。图像拼接系列相关论文精读Seam Carving for Content-Aware Image ResizingAs-Rigid-As-Possible Shape ManipulationAdaptive As-Natural-As-Possible Image StitchingShape-Preserving Half-Projective Warps for Image StitchingSeam-Driven Image StitchingParallax-tolerant Ima_natural image stitching using depth maps

【Java基础知识 11】java泛型方法的定义和使用-程序员宅基地

文章浏览阅读1.6w次,点赞131次,收藏52次。一、基本介绍Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。二、提出背景Java集合(Collection)中元素的类型是多种多样的。例如,有些集合中的元素是Byte类型的,而有些则可能是String类型的,等等。Java允许程序员构建一个元素类型为Object的Collection,其中的元素可以是任何类型在Java S._java基础

推荐文章

热门文章

相关标签