用三元组表示稀疏矩阵的乘法
三元组实现稀疏矩阵的相乘

三元组实现稀疏矩阵的相乘学院:班级:姓名:学号:一、实验题目:建立三元组,并以三元组为储存结构储存两个稀疏矩阵,并实现他们的相乘,输出结果。
二、需求分析:定义一个一维数组,它用来以行序为主序依次存放所有非零元构成的三元组,并定义变量分别记录矩阵的行数、列数非零元个数以及数组大小。
按照这种方式定义三元组的数据实现两个稀疏矩阵的乘法操作。
三、概要设计:建立一个三元组的结点:struct Node{int Row,Col; // 三元组Á的行列号?int Value; // 元素的值};用以储存稀疏矩阵,建立A和B两个稀疏矩阵用三元组表来储存非零元的行数、列数、值,再相乘输出结果。
四、详细设计:建立三元组:struct Node{int Row,Col; // 三元组Á的行列号?int Value; // 元素的值};建立三元组表:struct SparMatrix{int Rows,Cols; // 矩的行列数int Terms; // 矩阵的非零元个数struct Node arrayData[MAX_SIZE]; // 存放矩阵非零元素的三元组数组void PrintMatrix(); // 输出矩阵int GetElement(int m ,int n); // 获得矩阵对应的元素void PrintInit(); // 矩阵的输初始化void AddElement(int m,int n, int Value); // 增加非零元素};矩阵相乘:SparMatrix* MatrixMulti(SparMatrix* pM1,SparMatrix* pM2);void main(){SparMatrix matrix1;cout<<"The 1st matrix:"<<endl;matrix1.PrintInit();SparMatrix matrix2;cout<<"The 2nd matrix:"<<endl;matrix2.PrintInit();cout<<"Multiplication:"<<endl;matrix1.PrintMatrix();cout<<"*"<<endl;matrix2.PrintMatrix();cout<<"="<<endl;SparMatrix* pMatrixPro;pMatrixPro = MatrixMulti(&matrix1,&matrix2);if (pMatrixPro == NULL){cout<<"Error!"<<endl;}else{pMatrixPro->PrintMatrix();}if (pMatrixPro != NULL){delete pMatrixPro;pMatrixPro = NULL;}}五、程序使用说明:首先会看到“Please input the row and col num, using space to separate them:”的字样,这时候请输入你所要建立稀疏矩阵行数列数。
用三元组表示稀疏矩阵的乘法

图5.17给出了一个矩阵相乘的例子。当矩阵M、N是稀疏 矩阵时,我们可以采用三元组表的表示形式来实现矩阵的相 乘。
3 0 0 5
0 2
1 0
M 0 1 0 0 N
2 0 0 0
2 4
0 0
0 6
Q 1 0
0 4
图5.17 Q=M×N
图5.18 矩阵M、N、Q的三元组表
{
OLink * row_head, *col_head; /* 行、 列链表的头指针向量 */
int m, n, len; /* 稀疏矩阵的行数、 列数、 非零元素的个数 */
}CrossList;
CreateCrossList (CrossList * M) {/* 采用十字链表存储结构, 创建稀疏矩阵M */ scanf(&m, &n, &t); /* 输入M的行数, 列数和非零元素的个数 */ M->m=m; M->n=n; M->len=t; If(!(M->row_head=(OLink * )malloc((m+1)sizeof(OLink)))) exit(OVERFLOW); If(!(M->col_head=(OLink * )malloc((n+1)sizeof(OLink)))) exit(OVERFLOW); M->row_head[ ]=M->col_head[ ]=NULL;
}TriSparMatrix;
具体算法如下:
该算法的时间主要耗费在乘法运算及累加上,其时间复杂度为O (A.len×B.n)。当A.len 接近于A.m×A.n时,该算法时间复杂度接近于 经典算法的时间复杂度O(A.m×A.n×B.n)。
实验报告五:稀疏矩阵的三元组实现实验

实验报告
实验五稀疏矩阵的三元组实现实验(4学时)
实验目的:
掌握稀疏矩阵的三元组表示方法、算法实现。
实验内容:(类C算法的程序实现,任选其二)
(1) 基于三元组的稀疏矩阵表示与输入、输出方法(必做);
(2) 基于三元组的稀疏矩阵加法(选做);
(3) 基于三元组表示的稀疏矩阵转置(选做);
(4) 基于三元组表示的稀疏矩阵的乘法(选做)。
实验准备:
1) 计算机设备;2) 程序调试环境的准备,如TC环境;3) 实验内容的算法分析与代码设计与分析准备。
实验步骤:
1.录入程序代码并进行调试和算法分析;
2.编写实验报告。
实验结果:
试验的完整的C程序代码,以及程序实现与结果分析。
源程序:
1,三元组的顺序表存储表示
2,创建一个稀疏矩阵
3,表示这个矩阵
4,主函数
最终结果:。
三元组矩阵乘法

三元组矩阵乘法1. 任务背景矩阵是线性代数中一个非常重要的概念,广泛应用于各个领域,包括数学、物理、计算机科学等。
矩阵乘法是其中的一个基本运算,它在很多领域都有着重要的应用。
而三元组矩阵乘法则是矩阵乘法的一种特殊形式,它可以更加高效地表示和计算稀疏矩阵的乘法运算。
2. 任务介绍2.1 什么是三元组矩阵?三元组矩阵是一种特殊的矩阵表示方法,适用于稀疏矩阵(大部分元素为0)的存储和计算。
在三元组矩阵中,只存储非零元素的值及其对应的行列索引,而忽略了所有零元素。
这样可以大大减少存储空间和计算复杂度。
三元组矩阵的存储方式通常采用三个数组来表示,分别是:values、row_indices和col_indices。
其中,values数组存储非零元素的值,row_indices数组存储非零元素所在的行索引,col_indices数组存储非零元素所在的列索引。
2.2 三元组矩阵乘法的定义给定两个三元组矩阵A和B,它们的乘积C的定义如下:C(i, j) = sum(A(i, k) * B(k, j)) for k in range(0, n)其中,n表示矩阵的维度,A(i, k)表示矩阵A的第i行第k列的元素,B(k, j)表示矩阵B的第k行第j列的元素。
2.3 三元组矩阵乘法的算法三元组矩阵乘法的算法可以分为两个步骤:稀疏矩阵的转置和乘法计算。
2.3.1 稀疏矩阵的转置为了实现高效的三元组矩阵乘法,需要先将矩阵B进行转置。
转置后的矩阵B的非零元素行列索引互换,即原来的B(k, j)变为B(j, k)。
2.3.2 乘法计算在转置后的矩阵B上进行乘法计算。
对于矩阵A的每一行i,找到矩阵B中列索引等于i的所有非零元素,然后将这些元素与A(i, k)相乘,并将结果累加得到C(i, j)。
2.4 三元组矩阵乘法的优势相比于传统的矩阵乘法算法,三元组矩阵乘法具有以下优势:•节省存储空间:三元组矩阵只存储非零元素,大大减少了存储空间的占用。
稀疏矩阵基本操作 实验报告

稀疏矩阵基本操作实验报告一、实验内容稀疏矩阵的压缩储存结构,以及稀疏矩阵的三元组表表示方法下的转置、相加、相乘等算法二、实验目的1.熟悉数组、矩阵的定义和基本操作2.熟悉稀疏矩阵的储存方式和基本运算3.理解稀疏矩阵的三元组表类型定义,掌握稀疏矩阵的输入、输出和转置算法三、实验原理1.使用三元组储存矩阵中的非零元素(三元组分别储存非零元素的行下标,列下标和元素值)。
除了三元组表本身,储存一个稀疏矩阵还需要额外的三个变量,分别储存矩阵的非零元个数,矩阵的行数和矩阵的列数。
2.稀疏矩阵的创建算法:第一步:根据矩阵创建一个二维数组,表示原始矩阵第二步:取出二维数组中的元素(从第一个元素开始取),判断取出元素是否为非零元素,如果为非零元素,把该非零元素的数值以及行下标和列下表储存到三元数组表里,否则取出下一个元素,重复该步骤。
第三步:重复第二步,知道二维数组中所有的元素已经取出。
3.稀疏矩阵倒置算法:第一步:判断进行倒置的矩阵是否为空矩阵,如果是,则直接返回错误信息。
第二步:计算要倒置的矩阵每列非零元素的数量,存入到num数组(其中num[i] 代表矩阵中第i列非零元素的个数)。
以及倒置后矩阵每行首非零元的位置,存入cpot 数组中(其中cpot表示倒置后矩阵每行非零元的位置,对应表示原矩阵每列中第一个非零元的位置)。
第三步:确定倒置后矩阵的行数和列数。
第四步:取出表示要导致矩阵中三元组表元素{e, I, j}(第一次取出第一个,依次取出下一个元素),从第二步cpot数组中确定该元素倒置后存放的位置(cpot[j]),把该元素的行下标和列下标倒置以后放入新表的指定位置中。
cpot[j] 变量加一。
第五步:重复第四步,直到三元组表中所有的元素都完成倒置。
第六步:把完成倒置运算的三元组表输出。
4.稀疏矩阵加法算法:第一步:检查相加两个矩阵的行数和列数是否相同,如果相同,则进入第二步,否则输出错误信息。
第二步:定义变量i和j,用于控制三元组表的遍历。
三元组实现稀疏矩阵加减乘

#include<stdio.h>#include<stdlib.h> #define MAXSIZE 12500 // 三元组结构typedef structinti,j;int e; }Triple;{//矩阵行下标和列下标//值typedef struct {Triple data[MAXSIZE+1];int rpos[MAXSIZE+1]; // 这是存放各行第一非零元在矩阵中的位置int mu,nu,tu; // 矩阵的行数、列数、非零元个数}Matrix;void Init(Matrix* M);void Add(Matrix* M,Matrix* T,Matrix* G);void Jian(Matrix* M,Matrix* T,Matrix* G);void Cheng(Matrix* M,Matrix* T,Matrix* G);void Cheng(Matrix* M,Matrix* T,Matrix* G); void PrintMatrix(Matrix* M);//2、初始化矩阵void Init(Matrix* M){int i;if(M->mu < 1 || M->nu < 1 || M->tu > M->mu*M->nu){printf(" 出错!\n"); //如果矩阵的行数、列数不符合要求,打印出错} for(i=1;i<=M->tu;i++) //data[0] 不用{printf(“第%d个非零元的行号:“,i); //以下为数据初始化scanf("%d",&M->data[i].i);printf(“第%d个非零元的列号:",i); scanf("%d",&M->data[i].j); printf(”第%d个非零元的元素值:",i); scanf("%d",&M->data[i].e);}printf("\n");printf(" 您创建的矩阵如下:\n"); PrintMatrix(M);} //3、矩阵相加void Add(Matrix* M,Matrix* T,Matrix* G) {G->mu = M->mu;的行、列数。
稀疏矩阵的存储和乘法操作

稀疏矩阵的存储和乘法操作⼀稀疏矩阵的存储1.三元组顺序表三元组表⽰法就是在存储⾮零元的同时,存储该元素所对应的⾏下标和列下标。
稀疏矩阵中的每⼀个⾮零元素由⼀个三元组(i,j,a ij)唯⼀确定。
矩阵中所有⾮零元素存放在由三元组组成的顺序表中(通常⽤数组)。
所以三元组的逻辑结构如下://————稀疏矩阵的三元组表⽰法————//#define MAX_SIZE 1500 //表⽰稀疏矩阵的⾮零元素的最⼤个数class Triple{int i,j;//表⽰⾮零元素的⾏下表和列下标int val;//⾮零元素的值,此处以int类型为例};class TSMatrix{Triple data[MAX_SIZE];int row_num,col_num,cnt;//稀疏矩阵的⾏数、列数以及⾮零元素的个数};注意,此处的⾮零元素的三元组是以⾏序为主序顺序排列的。
2.⾏逻辑链接顺序表⾏逻辑链接顺序表的实质就是在三元组顺序表的基础上加了⼀个数组,这个数组⽤于存储稀疏矩阵中每⾏的第⼀个⾮零元素的在三元组顺序表中的位置(此处⼀定要理解对,是在三元组顺序表中的位置)。
所以其逻辑结构如下://————稀疏矩阵的⾏逻辑链接表⽰法————//#define MAX_SIZE 1500 //表⽰稀疏矩阵的⾮零元素的最⼤个数#define MAX_ROW 1500 //表⽰稀疏矩阵的⾏数的最⼤个数class Triple{int i,j;//表⽰⾮零元素的⾏下表和列下标int val;//⾮零元素的值,此处以int类型为例};class RLSMatrix{Triple data[MAX_SIZE]; //⾮零元三元组表int rpos[MAX_ROW];//每⾏第⼀个⾮零元素的位置int row_num,col_num,cnt;//稀疏矩阵的⾏数、列数以及⾮零元素的个数};3.⼗字链表当稀疏矩阵的⾮零元个数和位置在操作过程中变化较⼤时,就不易采⽤顺序存储结构来表⽰三元组的线性表。
数据结构三元组稀疏矩阵乘法代码

数据结构三元组稀疏矩阵乘法代码代码:```pythonclass SparseMatrix:def __init__(self, rows, columns):self.rows = rowsself.columns = columnsself.elements = []def insert(self, row, column, value):if row >= 0 and row < self.rows and column >= 0 and column < self.columns:self.elements.append((row, column, value))else:raise IndexError("Invalid index")def multiply(self, other):if self.columns != other.rows:raise ValueError("Invalid matrix dimensions")result_matrix = SparseMatrix(self.rows, other.columns)for i in range(self.rows):for j in range(other.columns):result = 0for k in range(len(self.elements)):if self.elements[k][0] == i:for l in range(len(other.elements)):if other.elements[l][1] == j:if self.elements[k][1] == other.elements[l][0]:result += self.elements[k][2] * other.elements[l][2] if result != 0:result_matrix.insert(i, j, result)return result_matrix# 创建稀疏矩阵1sparse_matrix1 = SparseMatrix(3, 3)sparse_matrix1.insert(0, 0, 1)sparse_matrix1.insert(0, 2, 2)sparse_matrix1.insert(1, 1, 3)sparse_matrix1.insert(2, 0, 4)# 创建稀疏矩阵2sparse_matrix2 = SparseMatrix(3, 3)sparse_matrix2.insert(0, 1, 5)sparse_matrix2.insert(1, 0, 6)sparse_matrix2.insert(1, 2, 7)sparse_matrix2.insert(2, 2, 8)# 稀疏矩阵乘法result = sparse_matrix1.multiply(sparse_matrix2)# 输出结果for i in range(result.rows):for j in range(result.columns):print(result.elements[i * result.columns + j][2], end=" ")print()```该代码实现了三元组表示的稀疏矩阵的乘法运算。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
储 所 以 可 以 采 用 固 定 三 元 组 表 a 中 的 元 素 ( i , k , Mik )
(1≤i≤m1,1≤k≤n1),在三元组表b中找所有行号为k的的对 应元素(k,j, Nkj)(1≤k≤m2,1≤j≤n2)进行相乘、 累加, 从而得到Q[i][j],即以三元组表a中的元素为基准, 依次 求出其与三元组表b的有效乘积。
0 Q 1 0
6 0 4
图5.17 Q=M×N
第十二讲
图5.18 矩阵M、N、Q的三元组表
第十二讲
经典算法中,不论M[i][k]、N[k][j]是否为零,
都要进行一次乘法运算,而实际上,这是没有必要的。采用三
元组表的方法来实现时,因为三元组只对矩阵的非零元素做存
· B=(A, A, D) 长度为3的广义表, 其前两个元素 为表A, 第三个元素为空表D。
· C=(a,C) 长度为2递归定义的广义表,C相当于无 穷表C=(a,(a,(a,(…))))。 #其中,A、B、C、D是广义表的名字。 下面以广义表A为例, 说明求表头、 表尾的操作: head(A)=a 表A的表头是a。 表A的表尾是((b, c))。
第十二讲
#define MAXSIZE 1000 /*非零元素的个数最多为1000*/ #define MAXROW 1000 /*矩阵最大行数为1000*/ typedef struct { int row, col; /*该非零元素的行下标和列下标*/ ElementType e; /*该非零元素的值*/ }Triple; typedef struct { Triple data[MAXSIZE+1]; /* 非零元素的三元组表,data[0]未用*/ int first[MAXROW+1]; /* 三元组表中各行第一个非零元素所在的 位置 */ int m, n, len;/*矩阵的行数、 列数和非零元素的个数*/ }TriSparMatrix;
第十二讲
else { /* 寻找行表中的插入位置 */ for(q=M->row_head[i]; q->right&&q->right->col<j; q=q->right) } if(M->col_head[j]==NULL) M->col_head[j]=p; else { /*寻找列表中的插入位置*/ p->right=q->right; q->right=p; /* 完成插入 */
0 1 Q 0 0 0
0 6 21 3 0 0 0 0 0 8 0 1 0 0 0 0 0 0 6 0
图5.19 Q=M×N
第十二讲
Row Num[row]
1 2
2 1
3 1
4 1
(5)
First[row]
1
3
4
5
6
图5.20 图5.19中矩阵N对应的向量num[row], first[row]
第十二讲
算法中附设两个向量num[ ]、first[ ],其中num
[row]表示三元组表b中第row行非零元素个数(1≤row≤m2), first[row]表示三元组表b中第row行第一个非零元素所在的 位置。显然,first[row+1]-1指向三元组表b中第row行最后一 个非零元素的位置。 first[1]=1; first[row]=first[row-1]+num[row-1], 2≤row≤m2+1。 这里,first[m2+1]-1表示最后一行最后一个非零元素 的存储位置。当三元组表a中第i行非零元素的列号等于三元
OLink * row_head, *col_head; /* 行、 列链表的头指针向量 */
int m, n, len; /* 稀疏矩阵的行数、 列数、 非零元素的个数 */ }CrossList;
第十二讲
CreateCrossList (CrossList * M) {/* 采用十字链表存储结构, 创建稀疏矩阵M */ scanf(&m, &n, &t); /* 输入M的行数, 列数和非零元素的个数 */
第十二讲
用三元组表实现稀疏矩阵的乘法运算
第十二讲
两个矩阵相乘也是矩阵的一种常用的运算。设矩阵M是
m1×n1矩阵,N是m2×n2矩阵;若可以相乘,则必须满足矩
阵M的列数n1与矩阵N的行数m2相等,才能得到结果矩阵 Q=M×N(一个m1×n2的矩阵)。
数学中矩阵Q中的元素的计算方法如下:
Q[i ][ j ] M [i ][k ] N [k ][ j ]
是一个广义表,则称di是广义表GL的子表。在广义表GL中,d1
是广义表GL的表头,而广义表GL其余部分组成的表(d2 , d3,…,dn)称为广义表的表尾。由此可见广义表的定义是递归
定义的,因为在定义广义表时又使用了广义表的概念。
第十二讲
· D=()空表;其长度为零。 · A=(a, (b, c)) 表长度为2的广义表,其中第一个 元素是单个数据a,第二个元素是一个子表(b,c)。
k 1
n1
其中: 1≤i≤m1, 1≤j≤n2。
第十二讲
根据数学上矩阵相乘的原理, 我们可以得到矩阵相乘的 经典算法:
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]=Q[i][j]+M[i][k]*N[k][j];
tail(A)=((b, c)) 广义表的表尾一定是一个表。
第十二讲
从上面的例子可以看出: (1) 广义表的元素可以是子表,而子表还可以是子表…… 由此可见,广义表是一个多层的结构。 (2) 广义表可以被其它广义表共享,如广义表B就共享 表A。 在表B中不必列出表A的内容,只要通过子表的名称就 可以引用该表。
第十二讲
typedef enum {ATOM, LIST} ElemTag; /* ATOM=0,表示原子;LIST=1, 表示子表 */ typedef struct GLNode
{
ElemTag tag; union /* 标志位tag用来区别原子结点和表结点 */
{
AtomType
矩阵不仅节约了空间,而且使得矩阵某些运算的运算时间比经
典算法还少。但是在进行矩阵加法、减法和乘法等运算时,有 时矩阵中的非零元素的位置和个数会发生很大的变化。如
A=A+B, 将矩阵B加到矩阵A上,此时若还用三元组表表示法,
势必会为了保持三元组表“以行序为主序”而大量移动元素。
第十二讲
在十字链表中,矩阵的每一个非零元素用一个结点表示,
(3) 广义表具有递归性, 如广义表C。
第十二讲
由于广义表GL=(d1,d2,d3,…,dn)中的数据元素既可 以是单个元素,也可以是子表,因此对于广义表来说,我们
难以用顺序存储结构来表示它,通常我们用链式存储结构来
表示。表中的每个元素可用一个结点来表示。广义表中有两 类结点:一类是单个元素结点;另一类是子表结点。 任何一 个非空的广义表都可以分解成表头和表尾两部分,反之,一 对确定的表头和表尾可以唯一地确定一个广义表。由此,一 个表结点可由三个域构成:标志域、指向表头的指针域和指 向表尾的指针域。而元素结点只需要两个域:标志域和值域。
M->row_head[ ]=M->col_head[ ]=NULL;
/* 初始化行、 列头指针向量, 各行、 列链表为空的链表 */ for(scanf(&i, &j, &e); i!=0; scanf(&i, &j, &e)) { if(!(p=(OLNode *) malloc(sizeof(OLNode)))) exit(OVERFLOW); p->row=i; p->col=j; p->value=e; /* 生成结点 */ if(M->row_head[i]==NULL) M->row_head[i]=p;
第十二讲
具体算法如下:
该算法的时间主要耗费在乘法运算及累加上,其时间复杂度为O (A.len×B.n)。当A.len 接近于A.m×A.n时,该算法时间复杂度接近于 经典算法的时间复杂度O(A.m×A.n×B.n)。
第十二讲
稀疏矩阵的链式存储结构: 十字链表
与用二维数组存储稀疏矩阵比较,用三元组表表示的稀疏
atom; /* 原子结点的值域atom */
struct { struct GLNode * hp, *tp; } htp;
/* 表结点的指针域htp, 包括表头指针域hp和表尾指针域tp */
} atom_htp; } *GList; /* atom_htp 是原子结点的值域atom和表结点的指针域htp的联合体域 */
LISP语言中,广义表是一种最基本的数据结构,就连LISP 语 言的程序也表示为一系列的广义表。
第十二讲
在第2章中, 线性表被定义为一个有限的序列(a1 ,a2 ,
a3,…,an), 其中ai被限定为是单个数据元素。广义表也是n个
数据元素(d1,d2,d3,…,dn )的有限序列,但不同的是,广 义表中的di既可以是单个元素,还可以是一个广义表,通常 记作: GL=(d1,d2,d3, …,dn)。GL是广义表的名字,通 常广义表的名字用大写字母表示。n是广义表的长度。若其中di
该结点除了(row,col,value)以外,还要有以下两个链域:
right: 用于链接同一行中的下一个非零元素; down: 用于链接同一列中的下一个非零元素。