MATLAB稀疏矩阵

7 稀疏矩阵

稀疏矩阵是一种特殊类型的矩阵,即矩阵中包括较多的零元素。对于稀疏矩阵的这种特性,在MATLAB中可以只保存矩阵中非零元素及非零元素在矩阵中的位置。在用稀疏矩阵进行计算时,通过消去零元素可以减少计算的时间。

7.1 稀疏矩阵的存储方式

对一般矩阵而言,MATLAB保存矩阵内的每一个元素,矩阵中的零元素与其他元素一样,需要占用同样大小的内存空间。但对于稀疏矩阵,MATLAB仅存储稀疏矩阵中的非零元素及其对应的位置,其他空余位置只是在访问时以默认的零元素来填充。对于一个含有大量零元素的大型矩阵,采用这种方法可以大大地减少数据占据的内存空间。

MATLAB采用3个内部数组来保存元素为实数的稀疏矩阵。

稀疏矩阵也可用于存储复数。当稀疏矩阵用于存储复数数据时,需用第4个内部数组保存非零复数的虚部。一个复数非零,是指其实部或虚部至少其中一个不为零。

【例2-16】  稀疏矩阵与一般矩阵的内存占用对比。

>> M_full = magic(1100);             % 创建一个1100´1100 矩阵

>> M_full(M_full > 50) = 0;          % 将>50的元素设置为0

>> M_sparse = sparse(M_full);    % 创建稀疏矩阵

>> whos

  Name             Size                Bytes  Class    Attributes

 M_full        1100x1100            9680000  double             

  M_sparse      1100x1100               9608  double   sparse 

本例中,M_full和 M_sparse两个变量存储的实际上是同一个矩阵,但是二者因为采用的存储形式分别为一般矩阵和稀疏矩阵,所以占用的内存量却相差了近1000倍。因为MATLAB版本不同,操作系统不同(例如32位和64位),内部存储格式也有些变化,但总体上来说占用的内存空间比一般矩阵小很多。

7.2 稀疏矩阵的创建

MATLAB决不会自动地创建一个稀疏矩阵,这需要用户来决定是否使用稀疏矩阵。在创建一个矩阵前,用户需要根据此矩阵中是否包含较多的零元素,采用稀疏矩阵技术是否有利,来决定是否采用稀疏矩阵的形式。把矩阵中非零元素的个数除以所有元素的个数,就叫做矩阵的密度,密度越小的矩阵采用稀疏矩阵的格式越有利。

要将一般矩阵转换为稀疏矩阵,可以使用函数sparse,如s=sparse (A),是指将矩阵A转换为稀疏矩阵。另外,使用函数full则可把稀疏矩阵转换为一般矩阵。

【例2-17】  一般矩阵与稀疏矩阵的转换示例。

>> A=[0 0 0 1;0 1 0 0;1 2 0 0;0 0 3 0]

A =

     0     0    0     1

     0     1    0     0

     1     2    0     0

     0     0    3     0

>> s=sparse(A)

s =

  (3,1)        1

  (2,2)        1

  (3,2)        2

  (4,3)        3

  (1,4)        1

>> B=full(s)

B =

     0     0    0     1

     0     1    0     0

     1     2    0     0

     0     0    3     0

从本例的结果中可以看出所有s的非零元素列表及其对应的行列序号。所有非零元素保存在一列中,反映了数据的内部结构。

稀疏矩阵的创建一般有以下几种方式。

1.直接创建稀疏矩阵

使用函数sparse,可以用一组非零元素直接创建一个稀疏矩阵。该函数调用格式为:

S=sparse(i,j,s,m,n)

其中i和j都为矢量,分别是指矩阵中非零元素的行号与列号,s是一个全部为非零元素矢量,元素在矩阵中排列的位置为(i,j)。m为输出的稀疏矩阵的行数,n为输出的稀疏矩阵的列数。

【例2-18】  稀疏矩阵的创建。

>> S=sparse([1 3 2 1 4],[3 1 4 1 4],[1 2 3 45],4,4)

S =

  (1,1)        4

  (3,1)        2

  (1,3)        1

  (2,4)        3

  (4,4)        5

>> full(S)

ans =

     4     0    1     0

     0     0    0     3

     2     0    0     0

     0     0    0     5

本例中通过sparse函数直接创建了稀疏矩阵S。sparse函数中的前两个输入变量[1 3 2 1 4]和[3 1 4 1 4]就是元素在矩阵中排列的位置,第3个输入变量[1 2 3 4 5]就是稀疏矩阵前面两个输入变量中的位置所对应的元素的值,而最后的两个输入变量4是指输出的稀疏矩阵的行数是4,输出的稀疏矩阵的列数同样也为4。通过full函数把稀疏矩阵转换为一般矩阵,这样就可以清楚地看出sparse函数输入和输出之间的关系。

需要指出的是:函数sparse还有一个变化形式,可以设置最大数目的非零元素。如有必要,可以在函数sparse中添加第6个输入参数,设置稀疏矩阵中非零元素的最大数目,这样以后要在矩阵中添加非零元素,就无需再修改矩阵的结构。具体的使用方法请查阅help文档。

2.从对角线元素中创建稀疏矩阵

要将一个矩阵的对角线元素保存在一个稀疏矩阵中,可以使用函数spdiags。其调用格式为:

S=spdiags(B,d,m,n)

函数spdiags用于创建一个尺寸为m行n列的稀疏矩阵S,其非零元素来自矩阵B中的元素且按对角线排列,参数d指定矩阵B中用于生成稀疏矩阵B的对角线位置。矩阵的主对角线可以认为是第0条对角线,每向右上移动一条对角线编号加1,向左下移动一条对角线编号减1,也就是说B中的j列填充矢量d元素,j指定对角线。

【例2-19】  稀疏矩阵的创建。

>> B=[1 2 3;4 5 6;7 8 9;10 11 12]

B =

     1     2    3

     4     5    6

     7     8    9

    10    11   12

>> d=[-3 0 2]

d =

    -3     0    2

>> A=spdiags(B,d,7,4)  

A =

  (1,1)        2

  (4,1)        1

  (2,2)        5

  (5,2)        4

  (1,3)        9

   (3,3)       8

  (6,3)        7

  (2,4)       12

  (4,4)       11

  (7,4)       10

>> full(A)

ans =

     2     0    9     0

     0     5    0    12

     0     0    8     0

     1     0    0    11

     0     4    0     0

     0     0    7     0

     0     0    0    10

本例生成了一个7行4列的稀疏矩阵A。B的第1列元素排列在主对角线以下的第3条对角线上,第2列元素排列在主对角线上,第3列中的非零元素排列在主对角线上方的第2条对角线上。这里需要注意B中的第三列并没有全部分布在第2条对角线上,而是最后两个元素9和12排列在该对角线上。

3.从外部文件中导入稀疏矩阵

用外部文件创建的文本文件,如果该文件中的数据按3或者4列排列,则可将这个文本文件载入内存,用于创建一个稀疏矩阵。

【例2-20】  稀疏矩阵的创建。

假如有这样一个文件uphill.dat(用户可以通过记事本打开、编辑其内容),文件内含有以下数据:

1    1    1.000000000000000

1    2    0.500000000000000

2    2    0.333333333333333

1    3    0.333333333333333

2    3    0.250000000000000

3    3    0.200000000000000

1    4    0.250000000000000

2    4    0.200000000000000

3    4    0.166666666666667

4    4    0.142857142857143

4    4    0.000000000000000

那么通过使用load命令可以将此数据文件载入MATLAB,然后对其进行操作。实际中用户可以在命令窗口输入:

>> load uphill.dat  % 用load命令将数据的文本文件uphill.dat载入工作空间

>> H = spconvert(uphill)

H =

  (1,1)       1.0000

  (1,2)       0.5000

  (2,2)       0.3333

  (1,3)       0.3333

  (2,3)       0.2500

  (3,3)       0.2000

  (1,4)       0.2500

  (2,4)       0.2000

  (3,4)       0.1667

  (4,4)       0.1429

>> full(H)


ans =


   1.0000    0.5000    0.3333   0.2500

        0    0.3333    0.2500   0.2000

        0         0    0.2000   0.1667

        0         0         0   0.1429

本例首先使用load函数导入了一个3列数据的文本文件uphill.dat,用户可以通过在命令行中输入变量名uphill 可以查看数据uphill 中的具体内容来验证数据读取是否正确,然后调用spconvert将uphill 转换为相应的稀疏矩阵H。通过调用full函数可以直观地看出得到的稀疏矩阵。

MATLAB使用load函数来导入外部数据文件,具体的用法可以参阅第10章。

7.3 稀疏矩阵的运算

多数MATLAB自带的数学函数都可用于处理稀疏矩阵,此时可以将稀疏矩阵当做一般矩阵看待。另外MATLAB也提供有一些专门针对稀疏矩阵的函数。处理稀疏矩阵时,计算的复杂程度与稀疏矩阵中非零元素的数目成正比,也与矩阵的行列尺寸有关。比如稀疏矩阵的乘法、乘方、包含一定次数的线性方程组等,都是比较复杂的运算。

用函数处理稀疏矩阵时,计算结果要遵循以下一些原则。

(1)MATLAB函数处理一个矩阵时,不管这个矩阵是一般矩阵还是稀疏矩阵,其返回值为一个数值或矩阵。返回值都按一般矩阵方式进行保存,并不会根据接受的参数是稀疏矩阵,而将结果保存为稀疏矩阵。

(2)函数处理一个数值或矢量返回一个矩阵时,如果矩阵为零矩阵、元素全为1的矩阵、随机矩阵或单位矩阵,这些矩阵全为一般矩阵形式。对于零矩阵,有一种类似稀疏矩阵的存储方法,因为零矩阵中没有非零元素,所以不能将一个零矩阵转换为一个稀疏矩阵,但指令zeros(m,n)和sparse(m,n)是可用的。对于单位矩阵和随机矩阵,可以使用类似稀疏矩阵的操作指令,即speye和sprand。对于元素全为1的矩阵,则没有类似的操作指令。

(3)以矩阵为参数返回矩阵或矢量的一元函数,返回值的存储类型与参数的存储类型相同。例如矩阵S的cholesky分解,如果S为一般矩阵,结果也为一般矩阵;如果S为稀疏矩阵,结果也为稀疏矩阵。对于列向处理矩阵的函数,如求各列最大值的函数max,求各列之和的函数sum等,也都返回与参数相同的存储类型。如果参数是稀疏矩阵,即使返回的矩阵或矢量全为非零元素,也用稀疏方式表示。例外情况只有函数sparse和full,因为它们用于一般矩阵和稀疏矩阵之间的转换。

(4)对于有两个输入参数为矩阵的情况,如果输入的两个矩阵都为稀疏矩阵,则输出仍为稀疏矩阵;都为一般矩阵,结果也为一般矩阵。如果输入参数一个为稀疏矩阵,一个为一般矩阵,结果通常为一般矩阵,但在能够保证矩阵稀疏性不变的运算中,结果则为稀疏矩阵。

(5)使用方括号对矩阵进行组合时,如果组合的矩阵中有稀疏矩阵,结果则为稀疏矩阵。

(6)子矩阵在右边的赋值操作,返回值为右边子矩阵的储存类型,子矩阵在左边赋值不改变其储存类型。

【例2-21】  稀疏矩阵的组合。

>> A=[1 0 0;0 0 1;1 2 0]

A =

     1     0    0

     0     0    1

     1     2    0

>> B=sparse(A)

B =

  (1,1)        1

  (3,1)        1

   (3,2)        2

  (2,3)        1

>> C=[A(:,1),B(:,2)]

C =

  (1,1)        1

  (3,1)        1

  (3,2)        2

本例将矩阵A的第1列和矩阵B的第2列组成了新的矩阵C,从结果可知,C为稀疏矩阵。

【例2-22】  稀疏矩阵子矩阵的赋值。

>> A=[1 0 0;0 0 1;1 2 0];

>> B=sparse(A);

>> C=sparse(cat(1,full(B),A))

C =

  (1,1)        1

  (3,1)        1

  (4,1)        1

  (6,1)        1

  (3,2)        2

  (6,2)        2

  (2,3)        1

  (5,3)        1

>> i=[1 2 3];

>> j=[1 2 3];

>> T=C(i,j)

T =

  (1,1)        1

  (3,1)        1

   (3,2)       2

  (2,3)        1

>> C(j,i)=full(T)      % 将一般矩阵赋值给一稀疏矩阵,仍返回稀疏矩阵

C =

  (1,1)        1

  (3,1)        1

  (4,1)        1

  (6,1)        1

  (3,2)        2

  (6,2)        2

  (2,3)        1

  (5,3)        1

7.4 稀疏矩阵的交换与重新排序

稀疏矩阵S的行交换与列交换可以用以下两种方法表示。

(1)对于交换矩阵P,对稀疏矩阵S进行行交换可表示为P*S,进行列交换可表示为P*S’。

(2)对于一个交换矢量p,p为一般矢量包含1到n个自然数的一个排列。对稀疏矩阵进行行交换,可以表示为S(p,:)。S(:,p)为列交换形式。对于矩阵S的某一列进行行交换,可以表示为S(p,n),如S(p,1)为对第1列进行交换。

【例2-23】  稀疏矩阵S的交换。

>> p=[1 3 2 4];

>> S=eye(4,4)

S =

     1     0    0     0

     0     1    0     0

     0     0    1     0

     0     0    0     1

>> P=S(p,:)

P =

     1     0    0     0

     0     0    1     0

     0     1    0     0

     0     0    0     1

>> V=S(p,2)

V =

     0

     0

     1

     0

矩阵P的第1行为S的第1行,第2行为S的第3行,等等。即对矩阵S的行,按照矢量p指定的顺序进行调整。

>> S1=speye(4,4)

S1 =

  (1,1)        1

  (2,2)        1

  (3,3)        1

  (4,4)        1

>> P1=S1(p,:)     % 对于稀疏矩阵行列的交换,返回的形式仍为稀疏矩阵

P1 =

  (1,1)        1

  (3,2)        1

  (2,3)        1

   (4,4)        1

对于稀疏矩阵S1进行行列的交换,返回的P1仍为稀疏矩阵。对稀疏矩阵的列重新排序,有时可以使矩阵分解的速度更快,最简单的矩阵排序是根据矩阵中非零元素的个数进行的,这种方法对于元素极不规则的矩阵很有效,特别适用于非零元素在行或列中数目变化较大的矩阵。MATLAB提供有一个非常简单的函数colperm,可以实现这种排序方法。此函数的M文件仅有以下几行:

function p=colperm(S)

if ~ismatrix(S)                     % 判断输入变量是否是一个矩阵

error(message('MATLAB:colperm:invalidInput'));   % 不满足条件的话返回错误信息

end

[~,p] = sort(full(sum(S ~= 0, 1)));

程序的第5行,实现了以下4个功能。

(1)调用S ~= 0判断矩阵中各元素是否为0,若不为0则返回逻辑值1。

(2)函数sum求上一步创建的矩阵各列的和,也即为各列中非零元素的个数。

(3)函数full将上一步创建的矢量转换为一般矢量的格式。

(4)使用函数sort对上一步操作创建的矢量元素进行升序排序,函数sort的第2个输出参数p,即为对矩阵S中各列中非零元素的个数进行重新排序的交换矢量。

【例2-24】  对下面的矩阵A,先用函数colperm获取一个交换矢量p,然后根据矢量p对矩阵A的列,按照非零元素的个数升序排序。

>> A=[0 1 2 3;3 2 1 0;0 0 2 0;1 0 0 2]

A =

     0     1    2     3

     3     2    1     0

     0     0    2     0

     1    0     0     2

>> p=colperm(A)

p =

     1     2    4     3

>> B=A(:,p)

B =

     0     1    3     2

     3     2    0     1

     0     0    0     2

     1     0    2     0

结果显示,矩阵B就是将A的各列按照非零元素的个数升序排序的结果。

7.5 稀疏矩阵视图

MATLAB提供有spy函数,用于观察稀疏矩阵非零元素的分布视图。本小节举例来说明spy函数的用法。

【例2-25】  稀疏矩阵视图示例。本例采用spy函数绘制Buckminster Fuller网格球顶的60×60邻接矩阵视图。这个矩阵还可用来表示碳60模型和足球。

>>B = bucky;

>>spy(B)

得到的结果如图2-2所示。图中显示了稀疏矩阵B的非零元素分布视图。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容