📜  反向Cuthill Mckee算法

📅  最后修改于: 2021-04-22 08:34:58             🧑  作者: Mango

Cuthill-Mckee算法用于对称方矩阵的重新排序。它基于图的广度优先搜索算法,其邻接矩阵是输入方阵的稀疏版本。
当要生成矩阵的行时,经常使用该顺序,该矩阵的行和列根据节点的编号进行编号。通过对节点进行适当的重新编号,通常可以产生带宽更小的矩阵。
矩阵的稀疏形式是其中大多数元素为零的矩阵。
反向Cuthill-Mckee算法与Cuthill-Mckee算法相同,唯一的区别在于,在Cut Cut-Mckee反向算法中,使用Cuthill-Mckee算法获得的最终指标被反向。
以下是反向Cuthill-Mckee算法的步骤:

  1. 为对象R的排列顺序实例化一个空队列Q和一个空数组。
  2. S1:我们首先找到具有最小度数的对象,该对象的索引尚未添加到R中。假设与第p行相对应的对象已被识别为最小度的对象。将p加到R。
  3. S2:将索引添加到R ,并在索引处添加相应对象的所有邻居,
    以递增的顺序,到Q。邻居是节点之间具有非零值的节点
    第p行中的非对角元素。
  4. S3:提取Q中的第一个节点,例如C。如果尚未将C插入R中,则将其添加到R中,然后按递增顺序将QC邻居添加到Q中。
  5. S4:如果Q不为空,则重复S3
  6. S5:如果Q为空,但是矩阵中有一些对象尚未包含在R中,请再次从S1开始。 (如果存在不相交的图,则可能会发生这种情况)
  7. S6:一旦所有对象都包含在R中,则终止该算法。
  8. S7:最后,反转R中的索引,即( swap(R [i],R [P-i + 1]) )。

程度:程度的定义不是恒定的,它会根据您使用的数据集而变化。对于下面给出的示例,节点的度数定义为相应行中非对角元素的总和。
节点“ A”的程度的广义定义是连接到“ A”的节点数。
例子:

Given a symetric matrix:  
| 0.0    0.78    0.79    0.8     0.23  |
| 0.9    0.0     0.43    0.771   0.752 |
| 0.82   0.0     0.0     0.79    0.34  |
| 0.8    0.8     0.8     0.0     0.8   |
| 0.54   0.97    0.12    0.78    0.0   | 

Degree here is defined as sum of non-diagonal 
elements in the corresponding row.
Sparsification for a matrix is defined as, 
if the element of the matrix at i, j has a value 
less than 0.75 its made to 0 otherwise its made to 1.

稀疏后的矩阵

Degree of node 0 = 2.6
Degree of node 1 = 2.803
Degree of node 2 = 2.55
Degree of node 3 = 3.2
Degree of node 4 = 2.41

Permutation order of objects (R) : 0 2 1 3 4

新的排列顺序只是节点的排序,即将节点’R [i]’转换为节点’i’。
因此,将节点’R [0] = 0’转换为0;节点“ R [1] = 2”,为1;节点“ R [2] = 1”,为2;节点“ R [3] = 3”,到3;和节点“ R [4] = 4”,为4;
让我们举一个更大的例子来了解重新排序的结果:

Give a adjacency matrix : 

| 0   1   0   0   0   0   1   0   1   0 |
| 1   0   0   0   1   0   1   0   0   1 |
| 0   0   0   0   1   0   1   0   0   0 |
| 0   0   0   0   1   1   0   0   1   0 |
| 0   1   1   1   0   1   0   0   0   1 | 
| 0   0   0   1   1   0   0   0   0   0 |
| 1   1   1   0   0   0   0   0   0   0 |
| 0   0   0   0   0   0   0   0   1   1 |
| 1   0   0   1   0   0   0   1   0   0 |
| 0   1   0   0   1   0   0   1   0   0 | 

Degree of node 'A' is defined as number of 
nodes connected to 'A'

Output : 
Permutation order of objects (R) : 
7 8 3 5 9 0 1 4 6 2 

现在将节点“ R [i]”转换为节点“ i”
因此,该图变为:

通过两个图的邻接矩阵可以看出回火的结果:

原始矩阵

原始矩阵

RCM重排矩阵

从这里我们可以清楚地看到,Cuthill-Mckee算法如何帮助将方阵重新排序为非分布式矩阵。
下面是上述算法的实现。取学位的一般定义。

CPP
// C++ program for Implementation of
// Reverse Cuthill Mckee Algorithm
 
#include 
using namespace std;
 
vector globalDegree;
 
int findIndex(vector > a, int x)
{
    for (int i = 0; i < a.size(); i++)
        if (a[i].first == x)
            return i;
    return -1;
}
 
bool compareDegree(int i, int j)
{
    return ::globalDegree[i] < ::globalDegree[j];
}
 
template 
ostream& operator<<(ostream& out, vector const& v)
{
    for (int i = 0; i < v.size(); i++)
        out << v[i] << ' ';
    return out;
}
 
class ReorderingSSM {
private:
    vector > _matrix;
 
public:
    // Constructor and Destructor
    ReorderingSSM(vector > m)
    {
        _matrix = m;
    }
 
    ReorderingSSM() {}
    ~ReorderingSSM() {}
 
    // class methods
 
    // Function to generate degree of all the nodes
    vector degreeGenerator()
    {
 
        vector degrees;
 
        for (int i = 0; i < _matrix.size(); i++) {
            double count = 0;
            for (int j = 0; j < _matrix[0].size(); j++) {
                count += _matrix[i][j];
            }
 
            degrees.push_back(count);
        }
 
        return degrees;
    }
 
    // Implementation of Cuthill-Mckee algorithm
    vector CuthillMckee()
    {
        vector degrees = degreeGenerator();
 
        ::globalDegree = degrees;
 
        queue Q;
        vector R;
        vector > notVisited;
 
        for (int i = 0; i < degrees.size(); i++)
            notVisited.push_back(make_pair(i, degrees[i]));
 
        // Vector notVisited helps in running BFS
        // even when there are dijoind graphs
        while (notVisited.size()) {
 
            int minNodeIndex = 0;
 
            for (int i = 0; i < notVisited.size(); i++)
                if (notVisited[i].second < notVisited[minNodeIndex].second)
                    minNodeIndex = i;
 
            Q.push(notVisited[minNodeIndex].first);
 
            notVisited.erase(notVisited.begin()
                                    + findIndex(notVisited,
                                                notVisited[Q.front()].first));
 
            // Simple BFS
            while (!Q.empty()) {
 
                vector toSort;
 
                for (int i = 0; i < _matrix[0].size(); i++) {
                    if (i != Q.front() && _matrix[Q.front()][i] == 1
                        && findIndex(notVisited, i) != -1) {
                        toSort.push_back(i);
                        notVisited.erase(notVisited.begin()
                                           + findIndex(notVisited, i));
                    }
                }
 
                sort(toSort.begin(), toSort.end(), compareDegree);
 
                for (int i = 0; i < toSort.size(); i++)
                    Q.push(toSort[i]);
 
                R.push_back(Q.front());
                Q.pop();
            }
        }
 
        return R;
    }
 
    // Implementation of reverse Cuthill-Mckee algorithm
    vector ReverseCuthillMckee()
    {
 
        vector cuthill = CuthillMckee();
 
        int n = cuthill.size();
 
        if (n % 2 == 0)
            n -= 1;
 
        n = n / 2;
 
        for (int i = 0; i <= n; i++) {
            int j = cuthill[cuthill.size() - 1 - i];
            cuthill[cuthill.size() - 1 - i] = cuthill[i];
            cuthill[i] = j;
        }
 
        return cuthill;
    }
};
 
// Driver Code
int main()
{
    int num_rows = 10;
 
    vector > matrix;
 
    for (int i = 0; i < num_rows; i++) {
        vector datai;
 
        for (int j = 0; j < num_rows; j++)
            datai.push_back(0.0);
 
        matrix.push_back(datai);
    }
 
    // This is the test graph,
    // check out the above graph photo
    matrix[0] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 };
    matrix[1] = { 1, 0, 0, 0, 1, 0, 1, 0, 0, 1 };
    matrix[2] = { 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 };
    matrix[3] = { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 };
    matrix[4] = { 0, 1, 1, 1, 0, 1, 0, 0, 0, 1 };
    matrix[5] = { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 };
    matrix[6] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
    matrix[7] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
    matrix[8] = { 1, 0, 0, 1, 0, 0, 0, 1, 0, 0 };
    matrix[9] = { 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 };
 
    ReorderingSSM m(matrix);
 
    vector r = m.ReverseCuthillMckee();
 
    cout << "Permutation order of objects: " << r << endl;
 
    return 0;
}


输出
Permutation order of objects: 7 8 9 3 5 1 0 4 6 2 

参考:https://en.wikipedia.org/wiki/Cuthill%E2%80%93McKee_algorithm