基于十字链表与三元组表的稀疏矩阵压缩存储实例研究

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

基于十字链表与三元组表的稀疏矩阵压缩存储实例研究

作者:周张兰

来源:《软件导刊》2017年第11期

摘要:十字链表和带行链接信息的三元组表是稀疏矩阵的两种压缩存储方法。十字链表为链式存储结构,带行链接信息的三元组表为顺序存储结构。在MovieLens数据集上设计了分别采用十字链表和带行链接信息的三元组表对以用户为行、项目为列、用户评分为矩阵元的稀疏矩阵进行压缩存储,并在这两种存储结构上实现用户相似度计算算法。通过测试分析和比较了两种不同的压缩存储方法在创建及相似度计算上的执行效率,并探讨了各自的特点及适用条件。

关键词关键词:稀疏矩阵;十字链表;三元组表;压缩存储

DOIDOI:10.11907/rjdk.171845

中图分类号:TP302

文献标识码:A文章编号文章编号:16727800(2017)011002204

0引言

矩阵是科学与工程计算问题中研究的数学对象。在高阶矩阵中,可能存在很多相同值或零值的矩阵元,对这些矩阵元的存储造成存储空间的浪费。因此,可以对矩阵进行压缩存储,以节省存储空间,达到提高存储利用率的目的。在算法实现中,选择的存储结构不同,执行效率也将不同。对不同矩阵存储方法的特点进行分析和比较,有助于根据不同的实际应用,有针对性地选择更为合适的存储结构,以此提高矩阵运算及其它相关操作的运行效率。

1稀疏矩阵及存储

若一个m行n列矩阵中的零元素有t个,零元素个数t与矩阵元总数m×n的比值称为稀疏因子,一般认为若稀疏因子不大于0.05,则此矩阵为稀疏矩阵。设矩阵有10行10列,即总共100个元素,若其中零元素有95个,而非零元素仅有5个,则此矩阵为稀疏矩阵。在存储稀疏矩阵时,可以采用非压缩存储和压缩存储两种方式。非压缩存储使用二维数组,比如,设10行10列的稀疏矩阵M的矩阵元均为整数,则可以使用二维数组存储该矩阵M,数组的定义用C语言[1]描述如下:

int a[10][10];

其中,a[0][0]代表稀疏矩阵M第1行第1列元素,a[i][j]代表第i+1行第j+1列元素。由于数组是从下标0开始,因此使用二维数组时下标与逻辑上的行号和列号相差1。为了操作方便,也可以从a[1][1]开始存储第1行第1列元素,即定义为:

int a[11][11];

这种定义方式可以使逻辑上的行、列号与二维数组的下标保持一致,采用这种方式存储矩阵时所需存储空间要求更大。

从操作上看,二维数组的非压缩存储方式按行和列存取数据元素非常方便。然而,在稀疏矩阵中,大量零元素的存储不仅会浪费存储空间,而且与零元素相关的运算也会在一定程度上降低计算总体效率。因此,在实际应用中,对稀疏矩阵进行压缩存储很有必要。压缩存储只存储非零矩阵元,有顺序存储和非顺序存储两种。其中,顺序存储可使用带行链接信息的三元组表,而非顺序存储可采用十字链表。

2压缩存储

2.1三元组及表示

压缩存储只存储矩阵中的非零元。在矩阵中,除了存储非零元素的值,还要存储该元素所在的行号和列号。三元组表示法以(行号,列号,值)形式存储矩阵中的一个非零元素,比如,(1,1,5)表示第1行第1列元素值为5。设矩阵元素值的类型为ElemType,则存储一个非零元三元组的结构体类型定义用C语言描述如下:

struct Triple{

int i,j;

ElemType e;

};

三元组可存储稀疏矩阵中所有非零元的值及其行、列号。但是,从三元组中不能得到整个稀疏矩阵行数、列数及非零元个数等信息。因此,还要对存储整个稀疏矩阵的三元组表进行定义。带行链接信息的三元组表又称为行逻辑链接顺序表[2],其结构体类型定义用C语言描述如下:

struct TripleMatrix{

struct Triple *data;

int *rops;

int m,n,t;

};

其中,以三元组形式表示的非零元存储在数组data[]中,每行的第1个非零元在三元组表中的起始位置存放在数组rops[]中,两个数组的存储空间在初始化时分配。m、n、t分别代表稀疏矩阵的行数、列数和非零元个数。

2.2十字链表及表示

十字链表是稀疏矩阵的链式存储结构。十字链表对矩阵的每一行建立一个链表,行链表的头指针存放在其对应行的一维数组中。同样,为矩阵的每一列建立一个链表,列链表的头指针也存放在其对应列的一维数组中。为了实现十字链表,首先将需要存储的非零元素封装成一个结点。其中,结点包含5个域,除了行号、列号和值3个数据域外,还有两个指针域,分别指向该元素所在行及所在列的下一个非零元素。设稀疏矩阵的数据元素类型为ElemType,则十字链表中存储非零元素结点的结构体类型定义如下:

struct CrossLNode{

int i,j;

ElemType e;

struct CrossLNode *rownext, *colnext;

};

其中,rownext和colnext指针分别指向该元素所在行和所在列的下一个非零元素。对整个稀疏矩阵而言,除了存储非零元,还要记录整个矩阵的行数、列数和非零元个数以及两个分别存储所有行链表和列链表头指针的一维数组。因此,十字链表的结构体类型定义如下:

struct CrossLinkList{

struct CrossLNode *rowhead, *columnhead;

int m, n, t;

};

在十字链表定义中,m、n、t分别代表稀疏矩阵的行数、列数和非零元素个数。rowhead 和columnhead指针分别指向存储所有行链表头指针和所有列链表头指针的一维数组的首地址。

3实例测试

3.1数据集

协同过滤推荐[36]是一种广泛使用的推荐方法,传统的协同过滤推荐算法不依赖于推荐内容本身,而根据用户对项目的评分产生推荐。其中,用户对项目的评分可以看作是一个以用户为行、项目为列的矩阵,矩阵元aij表示用户i对项目j的评分。在实际应用中,用户有兴趣并且产生评分的项目相对于所有项目而言极其有限。因此,用户项目评分矩阵往往是一个稀疏矩阵。借助协同过滤推荐中常用的MovieLens[7]数据集作为数据对象,通过计算用户相似度,分析、对比分别采用带行链接信息的三元组表和十字链表对稀疏矩阵进行压缩存储时各自的执行效率。

本文使用的MovieLens数据集中,用户数943个、项目数1 682个。用户评分记录包括:用户号、项目号、评分和时间戳。其中,训练集有u1.base、u2.base、u3.base、u4.base和

u5.base 5个数据文件,每个文件中评分记录为80 000条,每一条评分记录以(user_id,

item_id,rating,timestamp)的形式存储。分析此数据可以得出,以用户对项目评分为矩阵元的矩阵稀疏因子为80 000/(943×1 682)≈0.05,即此矩阵可认为是一个稀疏矩阵。

3.2算法设计

在基于用户的协同过滤推荐中,首先利用评分矩阵计算用户与用户之间的相似度,从而产生目标用户的近邻,再利用这些用户近邻对目标用户未评分项目进行评分预测,最后根据预测评分的高低向目标用户推荐。用户相似度计算是基于用户的协同过滤推荐算法的重要步骤,本文采用余弦相似度计算用户之间的相似度值。为了比较带行链接信息的三元组表和十字链表压缩存储效率,分别在这两种存储结构上设计和实现用户相似度计算算法并进行了测试。

(1)带行链接信息的三元组表测试算法。采用带行链接信息的三元组表压缩存储评分矩阵并计算用户相似度的算法步骤如下:首先,初始化TripleMatrix类型的带行链接信息的三元组表M;然后,创建带行链接信息的三元组表M。读入评分记录文件u1.base,将其中每一条评分记录的行号、列号和值依次存储到M.data中;再计算用户之间的相似度,根据余弦相似度计算公式利用行链接信息M.rpos在三元组表中读取所需用户评分,并计算有共同评分的用户之间的相似度值;最后,重复以上步骤分别测试u2.base、u3.base、u4.base和u5.base 4个数据文件。

(2)十字链表测试算法。采用十字链表存储评分矩阵并计算用户相似度的算法步骤与带行链接信息的三元组表类似,但由于该方法采用链式存储结构,因此在算法实现上主要是指针操作。首先,初始化并创建CrossLinkList类型的十字链表T;然后,读入评分记录文件

u1.base,每读取一条记录,用malloc动态申请一个CrossLNode类型的节点,并存储相应的行、列和值,同时将该结点挂到其行所在的链表及其列所在的链表中;接着,计算用户之间的相似度,根据余弦相似度计算公式在创建的十字链表中读取所需节点的用户评分,并计算有共

相关文档
最新文档