矩阵压缩(数组降维,对角矩阵,对称矩阵,稀疏矩阵)

矩阵压缩(降维,对角矩阵,对称矩阵,稀疏矩阵)

1. 二维数组降一维

问题描述:

将二维数组压缩成一维数组,可以节省空间或提高计算效率。常见的方式是按行或按列将二维数组展平为一维数组。

映射公式:
  • 按行优先展平(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 <iostream>
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;
}

2. 三维数组降二维

问题描述:

将三维数组压缩成二维数组,最常见的方法是将三维数据按特定顺序(如行优先)映射到二维数组的行或列。

映射公式:
  • 假设有一个三维数组

    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 是第三个维度索引,xyz 分别是三维数组的大小。
示例:三维数组压缩为二维
#include <iostream>
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;
}

3. 三维数组降一维

问题描述:

将三维数组压缩为一维数组,可以按行优先或列优先的方式展开。

映射公式:
  • 假设有一个三维数组 A[x][y][z],可以将其压缩为一维数组 B[x * y * z]。映射公式如下:
    k=i×y×z+j×z+k \mathbf{{\color{Red}k=i×y×z+j×z+k}} k=i×y×z+j×z+k
    其中 i, j, k 分别是三维数组的三个维度索引,x, y, z 是三维数组的大小,k 是在一维数组中的索引。
示例:三维数组压缩为一维
#include <iostream>
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;
}

4. 对角矩阵压缩

问题描述:

对角矩阵是一个除了主对角线以外,其他元素都是零的矩阵。我们可以只存储主对角线的元素,从而进行压缩。

映射公式:
  • 对于一个大小为 n x n 的对角矩阵 A,只需存储 A[i][i]i = 1, 2, ..., n,即压缩为一维数组 B,其大小为 n
    B[i]=A[i][i]fori=1,2,…,n \mathbf{{\color{Red}B[i]=A[i][i]fori=1,2,…,n}} B[i]=A[i][i]fori=1,2,,n
示例:对角矩阵压缩
#include <iostream>
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;
}

5、对称矩阵压缩

问题描述:

对称矩阵是指矩阵的元素关于主对角线对称,即对于一个大小为 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,我们可以只存储矩阵的上三角部分或下三角部分(包含主对角线)。因为上三角部分与下三角部分是相等的,存储上三角部分足以重构整个矩阵。
1. 上三角压缩(存储上三角部分,包括主对角线)

在对称矩阵的压缩存储中,常常选择存储上三角矩阵的元素,包括主对角线的元素。对称矩阵的上三角部分的元素个数为:
上三角元素个数=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,ij
    其中 i 和 j 为矩阵的行和列索引,k 是压缩后的数组的索引。

示例:

假设我们有一个 4×4 的对称矩阵:
A=(12342567368947910) A = \begin{pmatrix} 1 & 2 & 3 & 4 \\ 2 & 5 & 6 & 7 \\ 3 & 6 & 8 & 9 \\ 4 & 7 & 9 & 10 \end{pmatrix} A=12342567368947910
矩阵的上三角部分(包括主对角线)是:
(12345678910) \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]

2. 压缩存储算法

假设我们有一个大小为n×n的对称矩阵A,我们要将其压缩为一维数组 B[],存储上三角矩阵的元素。具体步骤如下:

  1. 对于矩阵 AAA,遍历所有元素A[i][j](其中 i≤j),将其存储到数组 B[] 中。
  2. 利用映射公式,将每个元素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 <iostream>
#include <vector>
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<int> 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
3. 如何重构原始矩阵

如果我们已经得到了压缩后的数组 B[],并且知道它是一个对称矩阵的上三角部分,我们可以根据 B[] 重构原始的对称矩阵 A

假设我们已经有了 B[],并且知道 n(矩阵的大小),我们可以通过以下步骤来重构原始矩阵 A

  1. 从数组 B[] 中取出每个元素,并将它们放回矩阵的对应位置。

  2. 对于 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 <iostream>
#include <vector>
using namespace std;

int main() {
    // 假设 B 数组已经存储了压缩后的上三角矩阵元素
    vector<int> 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 

6. 稀疏矩阵压缩

问题描述:

稀疏矩阵是指大部分元素为零的矩阵。存储稀疏矩阵时,如果我们直接存储矩阵所有元素,会浪费大量内存。为了节省空间,我们可以使用 压缩存储 的方式,只存储非零元素的值以及它们的位置(索引)。

稀疏矩阵的压缩方法通常有以下几种:

  1. 压缩行存储(Compressed Row Storage, CRS 或 CSR)
  2. 压缩列存储(Compressed Column Storage, CCS 或 CSC)
  3. 三元组表示法(Triplet Representation)
1. 压缩行存储(CSR)

CSR(Compressed Sparse Row) 是一种常见的稀疏矩阵压缩存储格式。它将矩阵按行压缩,并存储每一行的非零元素及其列索引。

映射公式(CSR)
  • 假设稀疏矩阵

    A[m][n]
    

    ,其中只有部分非零元素。CSR将矩阵表示为三个数组:

    • values[]:存储所有非零元素。
    • columns[]:存储每个非零元素对应的列索引。
    • row_ptr[]:存储每行的非零元素在 values[] 数组中的起始位置。

具体地,row_ptr[i] 表示矩阵第 i 行的第一个非零元素在 values[] 中的索引位置,而 row_ptr[i+1] 表示该行的第一个非零元素的下一个位置。

示例

对于稀疏矩阵:
A=(0030400005000006) A = \begin{pmatrix} 0 & 0 & 3 & 0 \\ 4 & 0 & 0 & 0 \\ 0 & 5 & 0 & 0 \\ 0 & 0 & 0 & 6 \end{pmatrix} A=0400005030000006
我们可以使用 CSR 格式压缩它:

  1. values[]:存储所有非零元素 [3, 4, 5, 6]
  2. columns[]:存储每个非零元素的列索引 [2, 0, 1, 3]
  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 <iostream>
#include <vector>
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<int> values;    // 存储非零值
    vector<int> columns;   // 存储列索引
    vector<int> 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;
}
2. 压缩列存储(CCS)

CCS(Compressed Column Storage) 是对稀疏矩阵的一种列优先存储方式,类似于 CSR 存储,但它将矩阵按列进行压缩。它使用以下三个数组来表示稀疏矩阵:

  • values[]:存储非零元素。
  • rows[]:存储每个非零元素对应的行索引。
  • col_ptr[]:存储每列的非零元素在 values[] 数组中的起始位置。
示例

对于相同的稀疏矩阵:
A=(0030400005000006) A = \begin{pmatrix} 0 & 0 & 3 & 0 \\ 4 & 0 & 0 & 0 \\ 0 & 5 & 0 & 0 \\ 0 & 0 & 0 & 6 \end{pmatrix} A=0400005030000006
我们可以使用 CCS 格式压缩它:

  1. values[]:存储所有非零元素 [3, 4, 5, 6]
  2. rows[]:存储每个非零元素的行索引 [0, 1, 2, 3]
  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 <iostream>
#include <vector>
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<int> values;   // 存储非零值
    vector<int> rows;     // 存储行索引
    vector<int> 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;
}
3. 三元组表示法

三元组表示法(Triplet Representation)是另一种压缩稀疏矩阵的方式,通常用于存储稀疏矩阵的所有非零元素和它们的位置。每个非零元素都被表示为一个三元组 (i, j, value),其中 i 是行索引,j 是列索引,value 是非零元素的值。

映射公式:
  • 将每个非零元素存储为一个三元组 (i, j, value),然后将这些三元组存储在一个数组中。
示例

对于稀疏矩阵:
A=(0030400005000006) 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 <iostream>
#include <vector>
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<Triplet> 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值