稀疏矩陣

努力的老周發表於2020-10-15

C++ 矩陣操作

假設我們需要操作一個 N*M 的矩陣。為了簡單,我們使用 int 型別進行說明。在實際編碼中,肯定是需要使用 STL template。

矩陣的儲存

靜態儲存

const int MAXN=1e5;    //定義矩陣最大行數
const int MAXM=1e5;    //定義矩陣最大列數
int array[MAXN][MAXM]; //定義矩陣

使用指標

使用指標包括兩部分程式碼。一部分是記憶體申請,另外一部分是記憶體釋放。

記憶體申請

int **array;//指向指標的指標,表示一個二維陣列
array=new int *[n];//申請行
for (int i=0; i<n; i++) {
    array[i]=new int[m];//申請列
}

記憶體釋放

for (int i=0; i<n; i++) {
    delete[] array[i];//釋放列
}
delete[] array;//釋放行

使用 STL 的 vector

vector<vector<int> > array(m); //這個m一定不能少

空間複雜度

從上面的 C++ 程式碼,我們可以非常明顯的知道,矩陣的空間複雜度為 O(N*M),也就是 O(N^2)。

矩陣的遍歷

我們知道一個 N*M 的矩陣,如果使用最基礎的 C++ 程式實現,就是一個兩重迴圈的遍歷。程式碼如下

for (int i=0; i<n; i++) {
    for (int j=0; j<m; j++) {
        cout<<array[i][j];
    }
}

時間複雜度

從上面的 C++ 程式碼,我們可以非常明顯的知道,矩陣的時間複雜度為 O(N*M),也就是 O(N^2)。

稀疏矩陣定義

在矩陣中,若數值為0的元素數目遠遠多於非0元素的數目,並且非0元素分佈沒有規律時,則稱該矩陣為稀疏矩陣。如下圖所示的 15*15 矩陣就是稀疏矩陣。

為什麼要研究稀疏矩陣

從上面的內容,我們可以看到,一個滿陣,它的時間複雜度和空間複雜度都是非常大的。尤其在數學研究的時候,維度 N 可能會趨向無窮大。

大型稀疏矩陣在計算科學以及工程應用中往往應用於求解 PDE(偏微分方程)。

因此,研究稀疏矩陣是數值計算,科學搬磚中必不可少的內容。其實這是一個造輪子的過程,只是為了加深對矩陣的理解。

稀疏矩陣儲存

由於稀疏矩陣的特性,我們不需要使用滿陣進行儲存。

Coordinate Format(COO)

COO 是一種座標形式的稀疏矩陣。採用三個陣列 row、col 和 data 儲存非零元素的資訊,這三個陣列的長度相同。row 陣列儲存元素的行,col 陣列儲存元素的列,data 陣列儲存元素的值。

儲存的主要優點是靈活、簡單,僅儲存非零元素以及每個非零元素的座標。但是 COO 不支援元素的存取和增刪,一旦建立之後,除了將之轉換成其它格式的矩陣,幾乎無法對其做任何操作和矩陣運算。

對應的 C++ 程式碼如下:

int row[] ={0, 0, 1, 1, 2, 2, 2, 3, 3};
int col[] ={0, 1, 1, 2, 0, 2, 3, 1, 3};
int data[]={1, 7, 2, 8, 5, 3, 9, 6, 4};

我們可以看到 COO 在儲存的列中有重複,還不是最節約的方式。

Compressed Sparse Row Format(CSR)

行壓縮,是按行儲存一個稀疏矩陣的方式。需要三類資料來表達:數值,列號,以及行偏移。CSR 不是三元組,而是整體的編碼方式。數值和列號與 COO 一致,表示一個元素以及其列號,行偏移表示某一行的第一個元素在 values 裡面的起始偏移位置。

如上圖中,第一行元素 1 是 0 偏移,第二行元素 2 是 2 偏移,第三行元素 5 是 4 偏移,第 4 行元素 6 是 7 偏移。在行偏移的最後補上矩陣總的元素個數,本例中是 9。

和 COO 相比,我們通過行壓縮,節約了儲存空間。

對應的 C++ 程式碼如下:

int row[] ={0, 2, 4, 7, 9};
int col[] ={0, 1, 1, 2, 0, 2, 3, 1, 3};
int data[]={1, 7, 2, 8, 5, 3, 9, 6, 4};

Compressed Sparse Column Format(CSC)

列壓縮,是按列儲存一個稀疏矩陣的方式。需要三類資料來表達:數值,行號,以及列偏移。CSC 不是三元組,而是整體的編碼方式。數值和行號與 COO 一致,表示一個元素以及其行號,列偏移表示某一列的第一個元素在 values 裡面的起始偏移位置。

如上圖中,第一列元素 1 是 0 偏移,第二列元素 7 是 2 偏移,第三列元素 8 是 5 偏移,第 4 列元素 9 是 7 偏移。在列偏移的最後補上矩陣總的元素個數,本例中是 9。

和 COO 相比,我們通過列壓縮,節約了儲存空間。

對應的 C++ 程式碼如下:

int row[] ={0, 0, 1, 1, 2, 2, 2, 3, 3};
int col[] ={0, 2, 5, 7, 9};
int data[]={1, 5, 7, 2, 6, 8, 3, 9, 4};

總結

COO、CSR 和 CSC 三種方法各有用途,我們使用 CSR 最多,因為在大部分科學計算中,基本都是執行 Ax=b 這個問題。

下一步工作

將稀疏矩陣儲存原理部分解釋完成後,下面將開始造輪子的過程。也就是自己寫一個稀疏矩陣的類,用於儲存。將來在這個基礎上,進一步擴充程式碼。逐步完成矩陣運算。

整個造輪子的過程,相關的程式碼都會儲存在 Github 中,https://github.com/zhouyium/SparseMatrix.git

相關文章