将二维数组压缩成一维数组,可以节省空间或提高计算效率。常见的方式是按行或按列将二维数组展平为一维数组。
按行优先展平(Row-major order):二维数组 A[m][n]
展开成一维数组 B[m * n]
,映射公式为:
B [ i × n + j ] = A [ i ] [ j ] \mathbf{{\color{Red}B[i×n+j]=A[i][j]}} B[i×n+j]=A[i][j]
i
是行索引,j
是列索引,m
是行数,n
是列数。按列优先展平(Column-major order):二维数组 A[m][n]
展开成一维数组 B[m * n]
,映射公式为:
B [ j × m + i ] = A [ i ] [ j ] \mathbf{{\color{Red}B[j×m+i]=A[i][j]}} B[j×m+i]=A[i][j]
i
是行索引,j
是列索引,m
是行数,n
是列数。#include
using namespace std;
int main() {
int A[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int B[3 * 4]; // 一维数组
// 将二维数组 A 转换为一维数组 B
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
B[i * 4 + j] = A[i][j];
}
}
// 输出一维数组
for (int i = 0; i < 12; ++i) {
cout << B[i] << " ";
}
cout << endl;
return 0;
}
将三维数组压缩成二维数组,最常见的方法是将三维数据按特定顺序(如行优先)映射到二维数组的行或列。
假设有一个三维数组
A[x][y][z]
我们可以将其压缩为一个二维数组 。
B[x * y][z]
B [ i × y + j ] [ k ] = A [ i ] [ j ] [ k ] \mathbf{{\color{Red}B[i×y+j][k]=A[i][j][k]}} B[i×y+j][k]=A[i][j][k]
i
是三维数组的第一个维度索引,j
是第二个维度索引,k
是第三个维度索引,x
、y
、z
分别是三维数组的大小。#include
using namespace std;
int main() {
int A[2][3][4] = {
{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}},
{{13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24}}
};
int B[6][4]; // 二维数组
// 将三维数组 A 压缩为二维数组 B
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 4; ++k) {
B[i * 3 + j][k] = A[i][j][k];
}
}
}
// 输出二维数组 B
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 4; ++j) {
cout << B[i][j] << " ";
}
cout << endl;
}
return 0;
}
将三维数组压缩为一维数组,可以按行优先或列优先的方式展开。
A[x][y][z]
,可以将其压缩为一维数组 B[x * y * z]
。映射公式如下:i
, j
, k
分别是三维数组的三个维度索引,x
, y
, z
是三维数组的大小,k
是在一维数组中的索引。#include
using namespace std;
int main() {
int A[2][3][4] = {
{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}},
{{13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24}}
};
int B[2 * 3 * 4]; // 一维数组
// 将三维数组 A 压缩为一维数组 B
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 4; ++k) {
int index = i * 3 * 4 + j * 4 + k;
B[index] = A[i][j][k];
}
}
}
// 输出一维数组 B
for (int i = 0; i < 24; ++i) {
cout << B[i] << " ";
}
cout << endl;
return 0;
}
对角矩阵是一个除了主对角线以外,其他元素都是零的矩阵。我们可以只存储主对角线的元素,从而进行压缩。
n x n
的对角矩阵 A
,只需存储 A[i][i]
,i = 1, 2, ..., n
,即压缩为一维数组 B
,其大小为 n
。#include
using namespace std;
int main() {
int A[4][4] = {
{1, 0, 0, 0},
{0, 2, 0, 0},
{0, 0, 3, 0},
{0, 0, 0, 4}
};
int B[4]; // 压缩后的对角矩阵
// 将对角矩阵 A 压缩为一维数组 B
for (int i = 0; i < 4; ++i) {
B[i] = A[i][i];
}
// 输出压缩后的数组 B
for (int i = 0; i < 4; ++i) {
cout << B[i] << " ";
}
cout << endl;
return 0;
}
对称矩阵是指矩阵的元素关于主对角线对称,即对于一个大小为 n×n
的矩阵 A
,如果
A [ i ] [ j ] = A [ j ] [ i ] \mathbf{{\color{Red}A[i][j]=A[j][i]}} A[i][j]=A[j][i]
,则矩阵 A
是对称矩阵。因此,在存储对称矩阵时,存储对称部分的元素即可,避免存储冗余的元素。
A
,我们可以只存储矩阵的上三角部分或下三角部分(包含主对角线)。因为上三角部分与下三角部分是相等的,存储上三角部分足以重构整个矩阵。在对称矩阵的压缩存储中,常常选择存储上三角矩阵的元素,包括主对角线的元素。对称矩阵的上三角部分的元素个数为:
上三角元素个数 = n ( n + 1 ) ÷ 2 \mathbf{{\color{Red}上三角元素个数=n(n+1){\div} 2 } } 上三角元素个数=n(n+1)÷2
这个公式表示了存储上三角部分所需的空间,其中 n
是矩阵的维度。
对于一个 n×n
的对称矩阵 A
,我们可以用一维数组 B[]
来存储上三角部分的元素。
位置映射公式为:
B [ k ] = A [ i ] [ j ] , 其中 k = i ( i + 1 ) 2 + j , 且 i ≤ j \mathbf{{\color{Red}B[k]=A[i][j],其中k=i(i+1)2+j,且i≤j}} B[k]=A[i][j],其中k=i(i+1)2+j,且i≤j
其中 i 和 j 为矩阵的行和列索引,k 是压缩后的数组的索引。
假设我们有一个 4×4 的对称矩阵:
A = ( 1 2 3 4 2 5 6 7 3 6 8 9 4 7 9 10 ) A = \begin{pmatrix} 1 & 2 & 3 & 4 \\ 2 & 5 & 6 & 7 \\ 3 & 6 & 8 & 9 \\ 4 & 7 & 9 & 10 \end{pmatrix} A= 12342567368947910
矩阵的上三角部分(包括主对角线)是:
( 1 2 3 4 5 6 7 8 9 10 ) \begin{pmatrix} 1 & 2 & 3 & 4 \\ & 5 & 6 & 7 \\ & & 8 & 9 \\ & & & 10 \end{pmatrix} 12536847910
我们可以将其压缩成一个一维数组:
B = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] B=[1,2,3,4,5,6,7,8,9,10] B=[1,2,3,4,5,6,7,8,9,10]
假设我们有一个大小为n×n
的对称矩阵A
,我们要将其压缩为一维数组 B[]
,存储上三角矩阵的元素。具体步骤如下:
A[i][j]
(其中 i≤j),将其存储到数组 B[]
中。A[i][j]
映射到数组B[]
的位置假设数组 B[]
存储了上三角矩阵的元素,则:
k = i ( i + 1 ) 2 + j \mathbf{{\color{Red}k=\frac{i(i+1)}{2}+j}} k=2i(i+1)+j
是元素 A[i][j]
在压缩后的数组 B[]
中的索引。
其中 i 是行索引,j 是列索引,i≤j
#include
#include
using namespace std;
int main() {
// 对称矩阵 A
int A[4][4] = {
{1, 2, 3, 4},
{2, 5, 6, 7},
{3, 6, 8, 9},
{4, 7, 9, 10}
};
// 计算上三角部分的元素个数
int n = 4;
int num_elements = n * (n + 1) / 2;
// 创建压缩后的数组 B
vector B(num_elements);
// 将对称矩阵的上三角部分存储到 B 数组中
int k = 0; // B 数组的索引
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
B[k++] = A[i][j];
}
}
// 输出压缩后的对称矩阵
cout << "Compressed Array (B): ";
for (int i = 0; i < num_elements; ++i) {
cout << B[i] << " ";
}
cout << endl;
return 0;
}
输出:
Compressed Array (B): 1 2 3 4 5 6 7 8 9 10
如果我们已经得到了压缩后的数组 B[]
,并且知道它是一个对称矩阵的上三角部分,我们可以根据 B[]
重构原始的对称矩阵 A
。
假设我们已经有了 B[]
,并且知道 n(矩阵的大小),我们可以通过以下步骤来重构原始矩阵 A
:
从数组 B[]
中取出每个元素,并将它们放回矩阵的对应位置。
对于 B[k]
,它对应的矩阵位置是 A[i][j]
,其中
k = i ( i + 1 ) 2 + j \mathbf{{\color{Red}k = \frac{i(i+1)}{2} + j}} k=2i(i+1)+j
#include
#include
using namespace std;
int main() {
// 假设 B 数组已经存储了压缩后的上三角矩阵元素
vector B = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int n = 4; // 矩阵的大小
int k = 0; // B 数组的索引
// 创建一个 n x n 的矩阵 A
int A[4][4] = {0};
// 根据 B 数组重构原始矩阵 A
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
A[i][j] = B[k++];
A[j][i] = A[i][j]; // 对称性
}
}
// 输出重构后的矩阵 A
cout << "Reconstructed Matrix A:" << endl;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cout << A[i][j] << " ";
}
cout << endl;
}
return 0;
}
输出:
Reconstructed Matrix A:
1 2 3 4
2 5 6 7
3 6 8 9
4 7 9 10
稀疏矩阵是指大部分元素为零的矩阵。存储稀疏矩阵时,如果我们直接存储矩阵所有元素,会浪费大量内存。为了节省空间,我们可以使用 压缩存储 的方式,只存储非零元素的值以及它们的位置(索引)。
稀疏矩阵的压缩方法通常有以下几种:
CSR(Compressed Sparse Row) 是一种常见的稀疏矩阵压缩存储格式。它将矩阵按行压缩,并存储每一行的非零元素及其列索引。
假设稀疏矩阵
A[m][n]
,其中只有部分非零元素。CSR将矩阵表示为三个数组:
values[]
:存储所有非零元素。columns[]
:存储每个非零元素对应的列索引。row_ptr[]
:存储每行的非零元素在 values[]
数组中的起始位置。具体地,row_ptr[i]
表示矩阵第 i
行的第一个非零元素在 values[]
中的索引位置,而 row_ptr[i+1]
表示该行的第一个非零元素的下一个位置。
对于稀疏矩阵:
A = ( 0 0 3 0 4 0 0 0 0 5 0 0 0 0 0 6 ) A = \begin{pmatrix} 0 & 0 & 3 & 0 \\ 4 & 0 & 0 & 0 \\ 0 & 5 & 0 & 0 \\ 0 & 0 & 0 & 6 \end{pmatrix} A= 0400005030000006
我们可以使用 CSR 格式压缩它:
values[]
:存储所有非零元素 [3, 4, 5, 6]
columns[]
:存储每个非零元素的列索引 [2, 0, 1, 3]
row_ptr[]
:表示每行非零元素的起始位置 [0, 1, 2, 3, 4]
所以,最终的 CSR 表示为:
values = [3, 4, 5, 6]
columns = [2, 0, 1, 3]
row_ptr = [0, 1, 2, 3, 4]
示例代码(CSR):
#include
#include
using namespace std;
int main() {
// 稀疏矩阵 A
int m = 4, n = 4;
int A[4][4] = {
{0, 0, 3, 0},
{4, 0, 0, 0},
{0, 5, 0, 0},
{0, 0, 0, 6}
};
// CSR存储格式
vector values; // 存储非零值
vector columns; // 存储列索引
vector row_ptr(m + 1, 0); // 存储每行非零值的起始位置
// 扫描稀疏矩阵,将非零值压缩
int count = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (A[i][j] != 0) {
values.push_back(A[i][j]);
columns.push_back(j);
++count;
}
}
row_ptr[i + 1] = count; // 更新当前行的结束位置
}
// 输出压缩后的稀疏矩阵(CSR格式)
cout << "Values: ";
for (int i : values) {
cout << i << " ";
}
cout << endl;
cout << "Columns: ";
for (int i : columns) {
cout << i << " ";
}
cout << endl;
cout << "Row pointers: ";
for (int i : row_ptr) {
cout << i << " ";
}
cout << endl;
return 0;
}
CCS(Compressed Column Storage) 是对稀疏矩阵的一种列优先存储方式,类似于 CSR 存储,但它将矩阵按列进行压缩。它使用以下三个数组来表示稀疏矩阵:
values[]
:存储非零元素。rows[]
:存储每个非零元素对应的行索引。col_ptr[]
:存储每列的非零元素在 values[]
数组中的起始位置。对于相同的稀疏矩阵:
A = ( 0 0 3 0 4 0 0 0 0 5 0 0 0 0 0 6 ) A = \begin{pmatrix} 0 & 0 & 3 & 0 \\ 4 & 0 & 0 & 0 \\ 0 & 5 & 0 & 0 \\ 0 & 0 & 0 & 6 \end{pmatrix} A= 0400005030000006
我们可以使用 CCS 格式压缩它:
values[]
:存储所有非零元素 [3, 4, 5, 6]
rows[]
:存储每个非零元素的行索引 [0, 1, 2, 3]
col_ptr[]
:表示每列非零元素的起始位置 [0, 1, 2, 3, 4]
所以,最终的 CCS 表示为:
values = [3, 4, 5, 6]
rows = [0, 1, 2, 3]
col_ptr = [0, 1, 2, 3, 4]
CCS 示例代码:
#include
#include
using namespace std;
int main() {
// 稀疏矩阵 A
int A[4][4] = {
{0, 0, 3, 0},
{4, 0, 0, 0},
{0, 5, 0, 0},
{0, 0, 0, 6}
};
// 矩阵的行数和列数
int m = 4, n = 4;
// 创建 CCS 存储格式的数组
vector values; // 存储非零值
vector rows; // 存储行索引
vector col_ptr(n + 1, 0); // 存储每列非零值的起始位置
// 扫描稀疏矩阵,将非零值压缩到 CCS 格式
int count = 0;
for (int j = 0; j < n; ++j) { // 遍历每一列
for (int i = 0; i < m; ++i) { // 遍历每一行
if (A[i][j] != 0) {
values.push_back(A[i][j]); // 存储非零值
rows.push_back(i); // 存储行索引
count++;
}
}
col_ptr[j + 1] = count; // 更新当前列的结束位置
}
// 输出 CCS 格式的稀疏矩阵
cout << "Values: ";
for (int i : values) {
cout << i << " ";
}
cout << endl;
cout << "Rows: ";
for (int i : rows) {
cout << i << " ";
}
cout << endl;
cout << "Column Pointers: ";
for (int i : col_ptr) {
cout << i << " ";
}
cout << endl;
// 重构矩阵 A(根据 CCS 格式)
int A_reconstructed[4][4] = {0};
for (int j = 0; j < n; ++j) {
for (int k = col_ptr[j]; k < col_ptr[j + 1]; ++k) {
int i = rows[k];
A_reconstructed[i][j] = values[k];
}
}
// 输出重构后的矩阵 A
cout << "Reconstructed Matrix A:" << endl;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cout << A_reconstructed[i][j] << " ";
}
cout << endl;
}
return 0;
}
三元组表示法(Triplet Representation)是另一种压缩稀疏矩阵的方式,通常用于存储稀疏矩阵的所有非零元素和它们的位置。每个非零元素都被表示为一个三元组 (i, j, value)
,其中 i
是行索引,j
是列索引,value
是非零元素的值。
(i, j, value)
,然后将这些三元组存储在一个数组中。对于稀疏矩阵:
A = ( 0 0 3 0 4 0 0 0 0 5 0 0 0 0 0 6 ) A = \begin{pmatrix} 0 & 0 & 3 & 0 \\ 4 & 0 & 0 & 0 \\ 0 & 5 & 0 & 0 \\ 0 & 0 & 0 & 6 \end{pmatrix} A= 0400005030000006
其三元组表示为:
(0, 2, 3)
(1, 0, 4)
(2, 1, 5)
(3, 3, 6)
示例代码(三元组表示法):
#include
#include
using namespace std;
struct Triplet {
int row, col, value;
};
int main() {
int A[4][4] = {
{0, 0, 3, 0},
{4, 0, 0, 0},
{0, 5, 0, 0},
{0, 0, 0, 6}
};
vector triplets;
// 扫描矩阵,存储非零元素
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
if (A[i][j] != 0) {
triplets.push_back({i, j, A[i][j]});
}
}
}
// 输出三元组表示
for (const auto& triplet : triplets) {
cout << "(" << triplet.row << ", " << triplet.col << ", " << triplet.value << ")\n";
}
return 0;
}