稀疏矩阵算法RCM算法三之RCM算法

本章节是介绍 George and Liu 书中 RCM 算法脚本的第三篇,介绍RCM的完整算法。

程序结构

在这一章节中,我们会介绍三个子程序 DEGREE, RCM, GENRCM ,再加上之前提到的子程序,就构成了 RCM 算法的所有脚本。其输入为 ROOT, MASK, XADJ, ADJNCY 和 PERM 。不同子程序之间的关系见下图:

1

DEGREE

这个子程序会计算一个连通组分中所有节点的度数。其输入为 ROOT, MASK, XADJ, ADJNCY 。

这个子程序会从第一层 (只包含 ROOT ) 开始,同时计算每一层的所有节点的度数,然后产生下一层的所有节点,加入到 LS 数组中。当一个节点加入到 LS 数组中,其在 XADJ 数组中的符号会改变,从而确保这个节点只会被记录一次(这个功能之前在 ROOTLS 中是通过修改 MASK 数组实现的,但是这里 MASK 必须保持不变,以保证所有节点的度数可以正确计算)。

变量 CCSIZE 包含了当前在 LS 中的节点数目。当所有的节点均被发现之后,所有节点的度数也计算完毕,此时 LS 数组中的节点在 XADJ 中的元素会改回原来的符号。

输出的 LS 的结构是一个节点,后面接着这个节点的所有相邻节点,而不是按照上面的那种层次结构。

脚本如下(**IABS()**是 fortran 内置的对整数求绝对值的函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
C****************************************************************          1.
C**************************************************************** 2.
C********** DEGREE ..... DEGREE IN MASKED COMPONENT *** 3.
C**************************************************************** 4.
C**************************************************************** 5.
C 6.
C PURPOSE - THIS ROUTINE COMPUTES THE DEGREES OF THE NODE 7.
C IN THE CONNECTED COMPONENT SPECIFIED BY MASK AND ROOT. 8.
C NODES FOR WHICH MASK IS ZERO ARE IGNORED. 9.
C 10.
C INPUT PARAMETERS - 11.
C ROOT - IS THE INPUT NODE THAT DEFINES THE COMPONENT 12.
C (XADJ, ADJNCY) - ADJACENCY STRUCTURE PAIR. 13.
C MASK - SPECIFIES A SECTION SUBGRAPH. 14.
C 15.
C OUTPUT PARAMETERS - 16.
C DEG - ARRAY CONTAINING THE DEGREES OF THE NODES IN 17.
C THE COMPONENT 18.
C CCSIZE - SIZE OF THE COMPONENT SPECIFIED BY MASK AND ROOT 19.
C 20.
C WORKING PARAMETER - 21.
C LS - A TEMPORARY VECTOR USED TO STORE THE NODES OF THE 22.
C COMPONENT LEVEL BY LEVEL 23.
C 24.
C**************************************************************** 25.
C 26.
SUBROUTINE DEGREE ( ROOT, XADJ, ADJNCY, MASK, 27.
1 DEG, CCSIZE, LS ) 28.
C 29.
C**************************************************************** 30.
C 31.
INTEGER ADJNCY(1), DEG(1), LS(1), MASK(1) 32.
INTEGER XADJ(1), CCSIZE, I, IDEG, J, JSTOP, JSTRT, 33.
1 LBEGIN, LVLEND, LVSIZE, NBR, NODE, ROOT 34.
C 35.
C**************************************************************** 36.
C 37.
C ----------------------------------------------------- 38.
C INITIALIZATION ... 39.
C THE ARRAY XADJ IS USED AS A TEMPORARY MARKER TO 40.
C INDICATE WHICH NODES HAVE BEEN CONSIDERED SO FAR. 41.
C ----------------------------------------------------- 42.
LS(1) = ROOT 43.
XADJ(ROOT) = -XADJ(ROOT) 44.
LVLEND = 0 45.
CCSIZE = 1 46.
C ----------------------------------------------------- 47.
C LBEGIN IS THE POINTER OT THE BEGINNING OF THE CURRENT 48.
C LEVEL. AND LVLEND POINTS TO THE END OF THIS LEVEL. 49.
C ----------------------------------------------------- 50.
100 LBEGIN = LVLEND + 1 51.
LVLEND = CCSIZE 52.
C ----------------------------------------------------- 53.
C FIND THE DEGREES OF NODES IN THE CURRENT LEVEL, 54.
C AND AT THE SAME TIME, GENERATE THE NEXT LEVEL. 55.
C ----------------------------------------------------- 56.
DO 400 I = LBEGIN, LVLEND 57.
NODE = LS(I) 58.
JSTRT = -XADJ(NODE) 59.
JSTOP = IABS(XADJ(NODE+1)) - 1 60.
IDEG = 0 61.
IF ( JSTOP .LT. JSTRT ) GO TO 300 62.
DO 200 J = JSTRT, JSTOP 63.
NBR = ADJNCY(J) 64.
IF ( MASK(NBR) .EQ. 0 ) GO TO 200 65.
IDEG = IDEG + 1 66.
IF ( XADJ(NBR) .LT. 0 ) GO TO 200 67.
XADJ(NBR) = -XADJ(NBR) 68.
CCSIZE = CCSIZE + 1 69.
LS(CCSIZE) = NBR 70.
200 CONTINUE 71.
300 DEG(NODE) = IDEG 72.
400 CONTINUE 73.
C ----------------------------------------------------- 74.
C COMPUTE THE CURRENT LEVEL WIDTH. 75.
C IF IT IS NONZERO, GENERATE ANOTHER LEVEL. 76.
C ----------------------------------------------------- 77.
LVSIZE = CCSIZE - LVLEND 78.
IF ( LVSIZE .GT. 0 ) GO TO 100 79.
C ----------------------------------------------------- 80.
C RESET XADJ TO ITS CORRECT SIGN AND RETURN. 81.
C ----------------------------------------------------- 82.
DO 500 I = 1, CCSIZE 83.
NODE = LS(I) 84.
XADJ(NODE) = -XADJ(NODE) 85.
500 CONTINUE 86.
RETURN 87.
END 88.

下面我们开始逐行分析脚本。

首先是初始化过程,从 ROOT 节点开始,将其加入到 LS 向量中,并将其 XADJ 中的值改为负数。

凡是加入到 LS 向量中的节点,其 XADJ 中的值均要改为相应的负数,防止重复添加到 LS 向量中。

1
2
3
4
5
6
7
8
9
C        -----------------------------------------------------            38.
C INITIALIZATION ... 39.
C THE ARRAY XADJ IS USED AS A TEMPORARY MARKER TO 40.
C INDICATE WHICH NODES HAVE BEEN CONSIDERED SO FAR. 41.
C ----------------------------------------------------- 42.
LS(1) = ROOT 43.
XADJ(ROOT) = -XADJ(ROOT) 44.
LVLEND = 0 45.
CCSIZE = 1 46.

开始进入循环,其中 LBEGIN 是当前层的起点,LVLEND 是当前层的终点

1
2
3
4
5
6
C        -----------------------------------------------------            47.
C LBEGIN IS THE POINTER OT THE BEGINNING OF THE CURRENT 48.
C LEVEL. AND LVLEND POINTS TO THE END OF THIS LEVEL. 49.
C ----------------------------------------------------- 50.
100 LBEGIN = LVLEND + 1 51.
LVLEND = CCSIZE 52.

查找当前层的所有节点的度数,注意这里已经是加入到 LS 向量中的节点,XADJ(NODE) 本身就是负数,而 XADJ(NODE + 1) 不确定是否是负数,因此采用 IABS() 函数取绝对值。

IDEG 记录度数,其中的节点必须满足 MASK 值不为0的条件;如果这些节点进一步满足其 XADJ 值不为负数,则加入到 LS 向量中,LS 向量长度 CCSIZE 加1,其 XADJ 值改为负数,这些节点作为下一层次的节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C        -----------------------------------------------------            53.
C FIND THE DEGREES OF NODES IN THE CURRENT LEVEL, 54.
C AND AT THE SAME TIME, GENERATE THE NEXT LEVEL. 55.
C ----------------------------------------------------- 56.
DO 400 I = LBEGIN, LVLEND 57.
NODE = LS(I) 58.
JSTRT = -XADJ(NODE) 59.
JSTOP = IABS(XADJ(NODE+1)) - 1 60.
IDEG = 0 61.
IF ( JSTOP .LT. JSTRT ) GO TO 300 62.
DO 200 J = JSTRT, JSTOP 63.
NBR = ADJNCY(J) 64.
IF ( MASK(NBR) .EQ. 0 ) GO TO 200 65.
IDEG = IDEG + 1 66.
IF ( XADJ(NBR) .LT. 0 ) GO TO 200 67.
XADJ(NBR) = -XADJ(NBR) 68.
CCSIZE = CCSIZE + 1 69.
LS(CCSIZE) = NBR 70.
200 CONTINUE 71.
300 DEG(NODE) = IDEG 72.
400 CONTINUE 73.

我们计算当前层次的节点数目,如果不为0,则继续循环。

1
2
3
4
5
6
C        -----------------------------------------------------            74.
C COMPUTE THE CURRENT LEVEL WIDTH. 75.
C IF IT IS NONZERO, GENERATE ANOTHER LEVEL. 76.
C ----------------------------------------------------- 77.
LVSIZE = CCSIZE - LVLEND 78.
IF ( LVSIZE .GT. 0 ) GO TO 100 79.

最后我们将 XADJ 向量“复原”

1
2
3
4
5
6
7
8
9
C        -----------------------------------------------------            80.
C RESET XADJ TO ITS CORRECT SIGN AND RETURN. 81.
C ----------------------------------------------------- 82.
DO 500 I = 1, CCSIZE 83.
NODE = LS(I) 84.
XADJ(NODE) = -XADJ(NODE) 85.
500 CONTINUE 86.
RETURN 87.
END 88.

RCM

Reverse Cuthill-McKee, 这个子程序就是对一个连通组分执行 RCM 算法,其输入为 ROOT, MASK, XADJ, ADJNCY 。

因为这个算法要求知道连通组分中所有节点的度数,因此其第一步就是通过调用 DEGREE 子程序计算所有节点的度数,所有节点会按照一层接着一层的顺序排列在 LS 数组中(这里是 PERM 数组),但是每一层的节点之间没有按照度数从低到高排序;因此我们通过 DO 600 I = ... 对每一层的节点按照度数排序或者说编码,生成一个新的顺序的层;通过循环 DO 200 I = ... 查找某个节点的所有未编码的相邻节点,按照度数从低到高的顺序排序。最终新的排序会记录在 PERM 数组中,最后再翻转这个顺序,得到我们最终想要的顺序。

注意,这里就像 ROOTLS 子程序,当节点 编码了之后,MASK(i) 会设为0;但是不像 ROOTLS 子程序, RCM 子程序不会将 MASK 数组恢复如初,其编码后的节点始终为 0 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
C****************************************************************          1.
C**************************************************************** 2.
C********** RCM ..... REVERSE CUTHILL-MCKEE ORDERING *** 3.
C**************************************************************** 4.
C**************************************************************** 5.
C 6.
C PURPOSE - RCM NUMBERS A CONNECTED COMPONENT SPECIFIED BY 7.
C MASK AND ROOT, USING THE RCM ALGORITHM. 8.
C THE NUMBERING IS TO BE STARTED AT THE NODE ROOT. 9.
C 10.
C INPUT PARAMETERS - 11.
C ROOT - IS THE NODE THAT DEFINES THE CONNECTED 12.
C COMPONENT AND IT IS USED AS THE STARTING 13.
C NODE FOR THE RCM ORDERING 14.
C (XADJ, ADJNCY) - ADJACENCY STRUCTURE PAIR FOR 15.
C THE GRAPH. 16.
C 17.
C UPDATED PARAMETERS - 18.
C MASK - ONLY THOSE NODES WITH NONZERO INPUT MASK 19.
C VALUES ARE CONSIDERED BY THE ROUTINE. THE 20.
C NODES NUMBERED BY RCM WILL HAVE THEIR 21.
C MASK VALUES SET TO ZERO. 22.
C 23.
C OUTPUT PARAMETERS - 24.
C PERM - WILL CONTAIN THE RCM ORDERING 25.
C CCSIZE - IS THE SIZE OF THE CONNECTED COMPONENT 26.
C THAT HAS BEEN NUMBERED BY RCM. 27.
C 28.
C WORKING PARAMETER - 29.
C DEG - IS A TEMPORARY VECTOR USED TO HOLD THE DEGREE 30.
C OF THE NODES IN THE SECTION GRAPH SPECIFIED 31.
C BY MASK AND ROOT 32.
C 33.
C PROGRAM SUBROUTINES - 34.
C DEGREE. 35.
C 36.
C**************************************************************** 37.
C 38.
SUBROUTINE RCM ( ROOT, XADJ, ADJNCY, MASK, 39.
1 PERM, CCSIZE, DEG ) 40.
C 41.
C**************************************************************** 42.
C 43.
INTEGER ADJNCY(1), DEG(1), MASK(1), PERM(1) 44.
INTEGER XADJ(1), CCSIZE, FNBR, I, J, JSTOP, 45.
1 JSTRT, K, L, LBEGIN, LNBR, LPERM, 46.
1 LVLEND, NBR, NODE, ROOT 47.
C 48.
C**************************************************************** 49.
C 50.
C ----------------------------------------------------- 51.
C FIND THE DEGREES OF THE NODES IN THE 52.
C COMPONENT SPECIFIED BY MASK ADN ROOT. 53.
C ----------------------------------------------------- 54.
CALL DEGREE( ROOT, XADJ, ADJNCY, MASK, DEG, 55.
1 CCSIZE, PERM ) 56.
MASK(ROOT) = 0 57.
IF ( CCSIZE .LE. 1 ) RETURN 58.
LVLEND = 0 59.
LNBR = 1 60.
C ----------------------------------------------------- 61.
C LBEGIN AND LVLEND POINT OT THE BEGINNING AND 62.
C THE END OF THE CURRENT LEVEL RESPECTIVELY. 63.
C ----------------------------------------------------- 64.
100 LBEGIN = LVLEND + 1 65.
LVLEND = LNBR 66.
DO 600 I = LBEGIN, LVLEND 67.
C -------------------------------------------------- 68.
C FOR EACH NODE IN CURRENT LEVEL ... 69.
C -------------------------------------------------- 70.
NODE = PERM(I) 71.
JSTRT = XADJ(NODE) 72.
JSTOP = XADJ(NODE+1) - 1 73.
C -------------------------------------------------- 74.
C FIND THE UNNUMBERED NEIGHBORS OF NODE. 75.
C FNBR AND LNBR POINT TO THE FIRST AND LAST 76.
C UNNUMBERED NEIGHBORS RESPECTIVELY OF THE CURRENT 77.
C NODE IN PERM. 78.
C -------------------------------------------------- 79.
FNBR = LNBR + 1 80.
DO 200 J = JSTRT, JSTOP 81.
NBR = ADJNCY(J) 82.
IF ( MASK(NBR) .EQ. 0 ) GO TO 200 83.
LNBR = LNBR + 1 84.
MASK(NBR) = 0 85.
PERM(LNBR) = NBR 86.
200 CONTINUE 87.
IF ( FNBR .GE. LNBR ) GO TO 600 88.
C ----------------------------------------------- 89.
C SORT THE NEIGHBORS OF NODE IN INCREASING 90.
C ORDER BY DEGREE. LINEAR INSERTION IS USED. 91.
C ----------------------------------------------- 92.
K = FNBR 93.
300 L = K 94.
K = K + 1 95.
NBR = PERM(K) 96.
400 IF ( L .LT. FNBR ) GO TO 500 97.
LPERM = PERM(L) 98.
IF ( DEG(LPERM) .LE. DEG(NBR) ) GO TO 500 99.
PERM(L+1) = LPERM 100.
L = L -1 101.
GO TO 400 102.
500 PERM(L+1) = NBR 103.
IF ( K .LT. LNBR ) GO TO 300 104.
600 CONTINUE 105.
IF (LNBR .GT. LVLEND) GO TO 100 106.
C ----------------------------------------------------- 107.
C WE NOW HAVE THE CUTHILL MCKEE ORDERING. 108.
C REVERSE IT BELOW ... 109.
C ----------------------------------------------------- 110.
K = CCSIZE/2 111.
L = CCSIZE 112.
DO 700 I = 1, K 113.
LPERM = PERM(L) 114.
PERM(L) = PERM(I) 115.
PERM(I) = LPERM 116.
L = L - 1 117.
700 CONTINUE 118.
RETURN 119.
END 120.

下面我们开始逐行查看这个脚本

首先我们调用 DEGREE 子程序,找到连通组分中所有节点的度数,注意这里的 LS 向量替换为 PERM 向量

1
2
3
4
5
6
C        -----------------------------------------------------            51.
C FIND THE DEGREES OF THE NODES IN THE 52.
C COMPONENT SPECIFIED BY MASK ADN ROOT. 53.
C ----------------------------------------------------- 54.
CALL DEGREE( ROOT, XADJ, ADJNCY, MASK, DEG, 55.
1 CCSIZE, PERM ) 56.

然后我们从 ROOT 出发,将其 MASK 值设为 0

1
2
3
4
MASK(ROOT) = 0                                                   57.
IF ( CCSIZE .LE. 1 ) RETURN 58.
LVLEND = 0 59.
LNBR = 1 60.

LBEGIN 和 LVLEND 指向当前层次的初始和结束位置,进入循环。

FNBR 和 LNBR 指向这个节点的所有未排序的相邻节点的初始和结束位置(由于是连通子图,每个节点至少有一个相邻节点;但是考虑到 MASK 值为0的情况,因此可能有0个相邻节点)。并且将相应的节点重新赋值给 PERM 向量(这些节点之间还没有排序)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
C        -----------------------------------------------------            61.
C LBEGIN AND LVLEND POINT OT THE BEGINNING AND 62.
C THE END OF THE CURRENT LEVEL RESPECTIVELY. 63.
C ----------------------------------------------------- 64.
100 LBEGIN = LVLEND + 1 65.
LVLEND = LNBR 66.
DO 600 I = LBEGIN, LVLEND 67.
C -------------------------------------------------- 68.
C FOR EACH NODE IN CURRENT LEVEL ... 69.
C -------------------------------------------------- 70.
NODE = PERM(I) 71.
JSTRT = XADJ(NODE) 72.
JSTOP = XADJ(NODE+1) - 1 73.
C -------------------------------------------------- 74.
C FIND THE UNNUMBERED NEIGHBORS OF NODE. 75.
C FNBR AND LNBR POINT TO THE FIRST AND LAST 76.
C UNNUMBERED NEIGHBORS RESPECTIVELY OF THE CURRENT 77.
C NODE IN PERM. 78.
C -------------------------------------------------- 79.
FNBR = LNBR + 1 80.
DO 200 J = JSTRT, JSTOP 81.
NBR = ADJNCY(J) 82.
IF ( MASK(NBR) .EQ. 0 ) GO TO 200 83.
LNBR = LNBR + 1 84.
MASK(NBR) = 0 85.
PERM(LNBR) = NBR 86.
200 CONTINUE 87.

如果没有MASK 不为0的相邻节点,则 FNBR = LNBR + 1;如果只有一个MASK 值不为0的相邻节点,此时 FNBR = LNBR。这两种情况下,相邻节点不需要排序,GO TO 600,查找下一个节点。

1
IF ( FNBR .GE. LNBR ) GO TO 600                               88.

正常情况下,我们要对刚刚加入到 PERM 向量中的向量节点进行排序(注意这里还在 DO 600 的循环内部)。

下面这段脚本的可读性太差了。看了半天大概看懂了,第 K 个位置和节点 NBR 相当于新增的节点,而之前的 FNBR 到 K-1 默认是已经排好序的节点。首先将第 K 个位置的节点与 K-1 位置的节点比较,如果左边大于等于右边则不用重排;否则将 K-1 位置的节点放到 K 位置,将 NBR 再和 K-2 位置的节点比较,依次比下去,直到找到 NBR 节点合适的插入位置。

其实就是看新增的 NBR 节点适合插入哪个位置,其实就是插入排序算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
C              -----------------------------------------------            89.
C SORT THE NEIGHBORS OF NODE IN INCREASING 90.
C ORDER BY DEGREE. LINEAR INSERTION IS USED. 91.
C ----------------------------------------------- 92.
K = FNBR 93.
300 L = K 94.
K = K + 1 95.
NBR = PERM(K) 96.
400 IF ( L .LT. FNBR ) GO TO 500 97.
LPERM = PERM(L) 98.
IF ( DEG(LPERM) .LE. DEG(NBR) ) GO TO 500 99.
PERM(L+1) = LPERM 100.
L = L -1 101.
GO TO 400 102.
500 PERM(L+1) = NBR 103.
IF ( K .LT. LNBR ) GO TO 300 104.
600 CONTINUE 105.

对这一层的所有节点的相邻节点进行排序,加入到 PERM 中后,判断是否新增了节点,即 LNBR 是否大于 LVLEND ,LNBR 是此时 PERM 的长度,LVLEND 是之前 PERM 的长度。如果新增了节点,则继续循环

1
IF (LNBR .GT. LVLEND) GO TO 100                                 106.

最后我们对 PERM 向量进行翻转,K 是总数除以2取整,然后第一个数和最后一个数互换,第二个数和倒数第二个数互换,以此类推。

举个例子,假设 CCSIZE = 5,那么 K = 5/2=2,那么就是位置1和位置5的数互换,位置2和位置4的数互换,位置3不动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C        -----------------------------------------------------           107.
C WE NOW HAVE THE CUTHILL MCKEE ORDERING. 108.
C REVERSE IT BELOW ... 109.
C ----------------------------------------------------- 110.
K = CCSIZE/2 111.
L = CCSIZE 112.
DO 700 I = 1, K 113.
LPERM = PERM(L) 114.
PERM(L) = PERM(I) 115.
PERM(I) = LPERM 116.
L = L - 1 117.
700 CONTINUE 118.
RETURN 119.
END 120.

GENRCM

(GENeral RCM) ,这个子程序用于找到一个不连通的图的 RCM 顺序,这个子程序会对每一个连通组分运行子程序 RCM 。这个子程序的输入为 NEQNS (the number of nodes or equations),(XADJ, ADJNCY)。中间数据为 MASKXLS ,作为中间子程序 FNROOT 和 RCM 的输入。

这个子程序一开始将 MASK 中的所有值均设为 1,之后对 MASK 进行循环知道其发现一个节点 ,其 MASK(i) = 1; 节点 MASK, (XADJ, ADJNCY) 便可以指定一个连通子图,之后调用子程序 FNROOTRCM 来确定这个子图中所有节点的顺序(注意运行完 RCM 之后,所有排序号的节点其 MASK 值会设为 0)。

注意 NUM 指向数组 PERM 中的第一个自由的位置 (first free position) ,每次运行完 RCM 子程序后就会更新。GENRCM 程序中运行 RCM 中关于 PERM 的实际参数为 PERM(NUM) ,也就是说在 RCM 中的 PERM(NUM) 参数是 GENRCM 中 PERM 数据的最后 NEQNS - NUM + 1 个元素。

最后当一个连通组分排好序后,GENRCM 程序会查找下一个 的节点,对下一个连通组分进行排序,直到终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
C****************************************************************          1.
C**************************************************************** 2.
C********** GENRCM ..... GENERAL REVERSE CUTHILL MCKEE *** 3.
C**************************************************************** 4.
C**************************************************************** 5.
C 6.
C PURPOSE - GENRCM FINDS THE REVERSE CUTHILL-MCKEE 7.
C ORDERING FOR A GENERAL GRAPH. FOR EACH CONNECTED 8.
C COMPONENT IN THE GRAPH, GENRCM OBTAINS THE ORDERING 9.
C BY CALLING THE SUBROUTINE RCM. 10.
C 11.
C INPUT PARAMETERS - 12.
C NEQNS - NUMBER OF EQUATIONS 13.
C (XADJ, ADJNCY) - ARRAY PAIR CONTAINING THE ADJACENCY 14.
C STRUCTURE OF THE GRAPH OF THE MATRIX 15.
C 16.
C OUTPUT PARAMETER - 17.
C PERM - VECTOR THAT CONTAINS THE RCM ORDERING. 18.
C 19.
C WORKING PARAMETERS - 20.
C MASK - IS USED TO MARK VARIABLES THAT HAVE BEEN 21.
C NUMBERED DURING THE ORDERING PROCESS. IT IS 22.
C INITIALIZED TO 1, AND SET TO ZERO AS EACH NODE 23.
C IS NUMBERED. 24.
C XLS - THE INDEX VECTOR FOR A LEVEL STRUCTURE. THE 25.
C LEVEL STRUCTURE IS STORED IN THE CURRENTLY 26.
C UNUSED SPACES IN THE PERMUTATION VECTOR PERM. 27.
C 28.
C PROGRAM SUBROUTINES - 29.
C FNROOT, RCM. 30.
C 31.
C**************************************************************** 32.
C 33.
SUBROUTINE GENRCM ( NEQNS, XADJ, ADJNCY, PERM, MASK, XLS ) 34.
C 35.
C**************************************************************** 36.
C 37.
INTEGER ADJNCY(1), MASK(1), PERM(1), XLS(1) 38.
INTEGER XADJ(1), CCSIZE, I, NEQNS, NLVL 39.
1 NUM, ROOT 40.
C 41.
C**************************************************************** 42.
C 43.
DO 100 I = 1, NEQNS 44.
MASK(I) = 1 45.
100 CONTINUE 46.
NUM = 1 47.
DO 200 I = 1, NEQNS 48.
C ----------------------------------------------------- 49.
C FOR EACH MASKED CONNECTED COMPONENT ... 50.
C ----------------------------------------------------- 51.
IF (MASK(I) .EQ. 0) GO TO 200 52.
ROOT = I 53.
C -------------------------------------------------- 54.
C FIRST FIND A PSEUDO-PERIPHERAL NODE ROOT. 55.
C NOTE THAT THE LEVEL STRUCTURE FOUND BY 56.
C FNROOT IS STORED STARTING AT PERM(NUM). 57.
C THEN RCM IS CALLED TO ORDER THE COMPONENT 58.
C USING ROOT AS THE STARTING NODE. 59.
C -------------------------------------------------- 60.
CALL FNROOT ( ROOT, XADJ, ADJNCY, MASK, 61.
1 NLVL, XLS, PERM(NUM) ) 62.
CALL RCM ( ROOT, XADJ, ADJNCY, MASK, 63.
1 PERM(NUM), CCSIZE, XLS ) 64.
NUM = NUM + CCSIZE 65.
IF (NUM .GT. NEQNS) RETURN 66.
200 CONTINUE 67.
RETURN 68.
END 69.

下面我们开始逐行解析脚本

首先将 MASK 向量的元素均初始化为 1,NUM 初始设为 1

1
2
3
4
       DO 100 I = 1, NEQNS                                              44.
MASK(I) = 1 45.
100 CONTINUE 46.
NUM = 1 47.

开始循环,找到第一个 MASK 值不为 0 个节点,设为 ROOT

然后调用 FNROOT 查找一个合适的初始节点,然后调用 RCM 算法查找合适的顺序,其中 NUM 表示 PERM 第一额可用的位置。

FNROOT 子程序中的 LS 向量用 PERM(NUM) 替代,RCM 子程序中的 DEG 向量用 XLS 替代,应该是出于节省内存的作用。这两个都只是中间向量,没有必要再创建两个数组。

调用完这两个子程序之后,已经排序的节点的 MASK 值已经设为 0 了。

之后对 NUM 进行更新,如果 NUM 大于 NEQNS ,说明所有节点已经排序,退出程序 (RETURN) ;否则继续对下一个连通组分进行排序,直至所有节点均被排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
         DO 200 I = 1, NEQNS                                              48.
C ----------------------------------------------------- 49.
C FOR EACH MASKED CONNECTED COMPONENT ... 50.
C ----------------------------------------------------- 51.
IF (MASK(I) .EQ. 0) GO TO 200 52.
ROOT = I 53.
C -------------------------------------------------- 54.
C FIRST FIND A PSEUDO-PERIPHERAL NODE ROOT. 55.
C NOTE THAT THE LEVEL STRUCTURE FOUND BY 56.
C FNROOT IS STORED STARTING AT PERM(NUM). 57.
C THEN RCM IS CALLED TO ORDER THE COMPONENT 58.
C USING ROOT AS THE STARTING NODE. 59.
C -------------------------------------------------- 60.
CALL FNROOT ( ROOT, XADJ, ADJNCY, MASK, 61.
1 NLVL, XLS, PERM(NUM) ) 62.
CALL RCM ( ROOT, XADJ, ADJNCY, MASK, 63.
1 PERM(NUM), CCSIZE, XLS ) 64.
NUM = NUM + CCSIZE 65.
IF (NUM .GT. NEQNS) RETURN 66.
200 CONTINUE 67.
RETURN 68.
END 69.

参考文献

  1. George A, Liu J, Ng E. Computer solution of sparse linear systems[J]. Oak Ridge National Laboratory, 1994.
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2024 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信