稀疏矩阵(Sparse Matrix)及其存储格式详解

稀疏矩阵(Sparse Matrix)是线性代数和计算机科学中的一个重要概念,广泛应用于科学计算、工程模拟、图像处理、机器学习等多个领域。与稠密矩阵(Dense Matrix)相比,稀疏矩阵大部分元素为零,仅有少数非零元素。这一特性使得稀疏矩阵在存储和计算上具有显著的优势,尤其在处理大规模数据时更为高效。

一、稀疏矩阵的定义与性质

1. 定义

稀疏矩阵是指在一个矩阵中大多数元素为零,只有少数元素为非零值的矩阵。形式上,给定一个 m × n m \times n m×n 的矩阵 A A A,如果 A A A 中非零元素的数量远小于 m × n m \times n m×n,则称 A A A 为稀疏矩阵。

2. 稠密矩阵与稀疏矩阵的对比

  • 稠密矩阵(Dense Matrix)矩阵中大部分元素为非零值。例如,一个 1000 × 1000 1000 \times 1000 1000×1000 的稠密矩阵大约有 1 0 6 10^6 106 个非零元素。
  • 稀疏矩阵(Sparse Matrix):矩阵中大部分元素为零。例如,一个 1000 × 1000 1000 \times 1000 1000×1000 的稀疏矩阵可能只有 1 0 3 10^3 103 个非零元素。

3. 稀疏矩阵的性质

  • 非零元素稀少:稀疏矩阵中的非零元素数量远小于矩阵的总元素数量。
  • 结构特性:稀疏矩阵往往具有特定的结构特性,如对角线、带状、块状等。
  • 存储与计算效率:由于非零元素稀少,可以采用专门的存储格式和算法,提高存储和计算效率。

二、稀疏矩阵的存储格式

为了高效地存储和操作稀疏矩阵,研究人员设计了多种存储格式。这些格式主要通过仅存储非零元素及其位置信息,减少内存占用和提高访问速度。以下是几种常见的稀疏矩阵存储格式:

1. 压缩稀疏行(Compressed Sparse Row, CSR)

压缩稀疏行(Compressed Sparse Row, CSR)是一种存储稀疏矩阵的有效数据结构,常用于科学计算、机器学习、自然语言处理等领域,特别是处理大规模稀疏矩阵时。稀疏矩阵是指大多数元素为零的矩阵,使用常规的二维数组存储会浪费大量内存,而CSR格式可以显著降低内存的使用,并提高计算效率。

1. CSR格式的结构

CSR格式通过三种数组来存储一个稀疏矩阵的非零元素及其位置信息。假设矩阵有 m m m 行, n n n 列,总共有 z z z 个非零元素,CSR格式的存储结构由以下三个数组组成:

  • values:存储矩阵的所有非零元素。
  • column_indices:存储对应非零元素的列索引,和 values 数组中的元素一一对应。
  • row_ptr:存储每一行的起始位置索引,表示每一行开始的非零元素在 values 数组中的位置。
    在这里插入图片描述

2. CSR格式的存储结构详解

考虑一个 m × n m \times n m×n 的稀疏矩阵 A A A,其中 z z z 个元素非零,矩阵的内容如下所示:

01234
010002
100304
250006
2.1. values(非零元素值数组)

values 数组按行优先的顺序存储矩阵中的非零元素,忽略零元素。例如,矩阵中的非零元素为:1, 2, 3, 4, 5, 6,因此:

values = [1, 2, 3, 4, 5, 6]
2.2. column_indices(列索引数组)

column_indices 数组存储每个非零元素对应的列索引。注意,column_indices 中的索引与 values 数组中的元素一一对应。例如:

  • 第一个非零元素 1 在第0行的第0列,因此 column_indices[0] = 0

  • 第二个非零元素 2 在第0行的第4列,因此 column_indices[1] = 4

  • 第三个非零元素 3 在第1行的第1列,因此 column_indices[2] = 1

  • 以此类推,最终得到:

column_indices = [0, 4, 1, 3, 2, 4]
2.3. row_ptr(行指针数组)

row_ptr 数组表示每一行中第一个非零元素values 数组中的位置。它的长度是 m + 1 m + 1 m+1(矩阵行数加1),因为最后一个元素表示最后一行非零元素的结尾。row_ptr[i] 表示第 i i i 行的第一个非零元素在 values 数组中的索引。矩阵中每一行的非零元素分布如下:

  • 第0行的非零元素为 1 和 2,它们在 values 数组中的位置分别是索引 0 和 1。
  • 第1行的非零元素为 3 和 4,它们在 values 数组中的位置分别是索引 2 和 3。
  • 第2行的非零元素为 5 和 6,它们在 values 数组中的位置分别是索引 4 和 5。

因此,row_ptr 数组如下:

row_ptr = [0, 2, 4, 6]

3. CSR格式存储矩阵示例

将上述矩阵转换为CSR格式后,矩阵 A A A 变为:

values = [1, 2, 3, 4, 5, 6]
column_indices = [0, 4, 1, 3, 2, 4]
row_ptr = [0, 2, 4, 6]

这个表示法有效地将稀疏矩阵的非零元素及其位置信息存储在三个一维数组中。由于稀疏矩阵中大部分元素是零,因此这种存储方式大大节省了内存。

4.查找矩阵中特定元素

假设我们要查找矩阵中的元素 A [ i , j ] A[i, j] A[i,j](即第 i i i 行,第 j j j 列的元素)。

  1. 确定目标行:首先,根据目标行 i i i 查找该行中非零元素的位置。可以通过 row_ptr[i] 获取该行的第一个非零元素在 values 中的索引。row_ptr[i+1] 则表示该行非零元素的结束位置。

    例如:

    • 对于第 0 行,row_ptr[0] = 0row_ptr[1] = 2,表示第 0 行的非零元素在 values 数组中的位置范围是从索引 0 到 1(即 values[0]values[1])。
    • 对于第 1 行,row_ptr[1] = 2row_ptr[2] = 4,表示第 1 行的非零元素在 values 数组中的位置范围是从索引 2 到 3(即 values[2]values[3])。
  2. 查找列索引:通过 column_indices 数组,查找该行非零元素的列索引。例如,column_indices 数组中的值对应于非零元素在该行中的列位置。

  3. 查找元素是否存在:如果列索引数组中包含目标列 j j j,那么就可以找到该元素;否则,目标元素为零。

示例查找

假设我们要查找 A [ 1 , 3 ] A[1, 3] A[1,3],即第 1 行,第 3 列的元素。

  1. 确定第 1 行非零元素的索引范围row_ptr[1] = 2row_ptr[2] = 4,说明第 1 行的非零元素索引位于 values[2]values[3],即 values[2] = 3values[3] = 4

  2. 检查列索引:查看 column_indices[2] = 1column_indices[3] = 3。目标列索引 3 在 column_indices[3] 中找到。

  3. 返回对应的元素:在 values[3] 中找到对应的非零元素 4。

因此,get_element(1, 3) 将返回值 4。

import numpy as np
from scipy.sparse import csr_matrix# Step 1: 创建一个稀疏矩阵
dense_matrix = np.array([[1, 0, 0, 0, 2],[0, 3, 0, 4, 0],[0, 0, 5, 0, 6]])# 将稀疏矩阵转换为CSR格式
csr = csr_matrix(dense_matrix)# 提取CSR格式的三个数组
values = csr.data
column_indices = csr.indices
row_ptr = csr.indptr# 输出CSR表示
print("Values:", values)            # 非零元素的值
print("Column indices:", column_indices)  # 非零元素的列索引
print("Row pointer:", row_ptr)           # 每行第一个非零元素的索引# Step 2: 查找函数实现# 查找矩阵中特定的元素 A[i, j]
def get_element(i, j, row_ptr, column_indices, values):# 查找第i行的非零元素的索引范围start_idx = row_ptr[i]  # 第i行第一个非零元素的索引end_idx = row_ptr[i + 1]  # 第i行最后一个非零元素的索引 + 1# 在该范围内查找是否有列索引等于jfor idx in range(start_idx, end_idx):if column_indices[idx] == j:return values[idx]  # 返回对应的非零元素值# 如果没有找到,返回0,表示该位置的元素为零return 0# 示例:查找 A[1, 3] 元素
print(f"A[1, 3] = {get_element(1, 3, row_ptr, column_indices, values)}")# 查找所有非零元素及其位置
def get_all_non_zero_elements(row_ptr, column_indices, values):non_zero_elements = []for i in range(len(row_ptr) - 1):  # 遍历每一行start_idx = row_ptr[i]end_idx = row_ptr[i + 1]for idx in range(start_idx, end_idx):# 获取非零元素的行索引、列索引和值non_zero_elements.append((i, column_indices[idx], values[idx]))return non_zero_elements# 输出所有非零元素
print("All non-zero elements:", get_all_non_zero_elements(row_ptr, column_indices, values))# 查找第1行的所有非零元素
def get_row_non_zero_elements(i, row_ptr, column_indices, values):start_idx = row_ptr[i]end_idx = row_ptr[i + 1]row_elements = []for idx in range(start_idx, end_idx):row_elements.append((column_indices[idx], values[idx]))  # 存储列索引和非零值return row_elementsprint(f"Non-zero elements in row 1: {get_row_non_zero_elements(1, row_ptr, column_indices, values)}")# 查找第4列的所有非零元素
def get_column_non_zero_elements(j, row_ptr, column_indices, values):column_elements = []for i in range(len(row_ptr) - 1):  # 遍历每一行start_idx = row_ptr[i]end_idx = row_ptr[i + 1]for idx in range(start_idx, end_idx):if column_indices[idx] == j:column_elements.append((i, values[idx]))  # 存储行索引和非零值return column_elementsprint(f"Non-zero elements in column 4: {get_column_non_zero_elements(4, row_ptr, column_indices, values)}")

5. CSR格式的优点

  • 空间节省:对于稀疏矩阵,大部分元素为零。通过只存储非零元素及其位置信息,CSR格式能够显著减少内存消耗。

  • 高效的矩阵向量乘法:CSR格式非常适合用于矩阵和向量的乘法(即矩阵-向量乘法)。由于存储的是非零元素,可以快速地查找非零元素,并与向量对应位置的元素进行乘法计算。

  • 便于矩阵运算:对于稀疏矩阵的常见操作(如矩阵乘法、加法等),CSR格式提供了高效的实现方式,尤其在处理大型稀疏矩阵时,具有较好的性能。

6. CSR格式的缺点

  • 随机访问性能较差:CSR格式不适合用于需要频繁随机访问矩阵中任意位置的场景。虽然它提供了快速的矩阵-向量乘法,但是在矩阵的随机访问(尤其是按行访问)较为低效

  • 插入/删除操作复杂:对于稀疏矩阵中的动态变化(如插入新的非零元素或删除现有元素),CSR格式并不容易处理,需要重新组织和压缩数据。

2. 压缩稀疏列(Compressed Sparse Column, CSC)

压缩稀疏列(Compressed Sparse Column, CSC)是一种用于存储稀疏矩阵的紧凑格式,与压缩稀疏行(Compressed Sparse Row, CSR)格式相对。CSC格式特别适用于按列操作较多的场景,如求解线性系统、矩阵求导和矩阵分解等。它的存储方式和CSR格式类似,但它是按列而不是按行存储非零元素。

1. CSC格式的结构

CSC格式与CSR类似,但按列优先顺序存储非零元素:

  1. 值数组(values):存储所有非零元素的值,按列优先顺序排列。
  2. 行索引数组(row_index):存储对应于值数组中元素的行索引。例如,row_indices[i] 给出了 values[i] 在原矩阵中所在的行。
  3. 列指针数组(col_ptr):存储每一列非零元素在值数组和行索引数组中的起始位置。

在这里插入图片描述

我们以下稀疏矩阵为例:

A = [ 1 0 0 0 0 3 0 4 0 0 5 0 ] A = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 3 & 0 & 4 \\ 0 & 0 & 5 & 0 \end{bmatrix} A= 100030005040

这个矩阵包含非零元素:1、3、4、5。我们将用CSC格式表示这个矩阵。

对应的CSC表示:

  • values:存储所有非零元素,按列顺序:[1, 3, 5, 4]
  • row_indices:对应每个非零元素的行索引:[0, 1, 2, 1]
  • col_ptr:表示每列的非零元素在 valuesrow_indices 中的索引范围。对于第 i i i 列,col_ptr[i] 表示该列第一个非零元素的位置,col_ptr[i+1] 表示该列最后一个非零元素之后的位置。对于这个矩阵,col_ptr = [0, 1, 2, 3, 4]

通过上面的示例,CSC格式的三个数组分别为:

values = [1, 3, 5, 4]
row_indices = [0, 1, 2, 1]
col_ptr = [0, 1, 2, 3, 4]

2.查找矩阵中特定元素

import numpy as np
from scipy.sparse import csc_matrix# 要查找矩阵中的指定元素(如A[i, j]),我们需要根据col_ptr来找到该列的非零元素范围,再根据row_indices找到目标行的非零元素。
def get_element(i, j, col_ptr, row_indices, values):# 获取第j列的非零元素范围start_idx = col_ptr[j]end_idx = col_ptr[j + 1]# 遍历该列的非零元素,查找是否存在行索引为i的元素for idx in range(start_idx, end_idx):if row_indices[idx] == i:return values[idx]  # 返回对应的非零元素值# 如果没有找到,返回0return 0# 要获取矩阵的指定列的所有非零元素,可以通过col_ptr数组找出该列的范围,然后用row_indices和values获取每个非零元素。
def get_column_non_zero_elements(j, col_ptr, row_indices, values):start_idx = col_ptr[j]end_idx = col_ptr[j + 1]# 获取该列的所有非零元素column_elements = []for idx in range(start_idx, end_idx):column_elements.append((row_indices[idx], values[idx]))  # 存储行索引和非零值return column_elements# 要获取矩阵中指定行的所有非零元素,可以遍历所有列,找到该行的非零元素。
def get_row_non_zero_elements(i, col_ptr, row_indices, values):row_elements = []# 遍历所有列,查找该行的非零元素for j in range(len(col_ptr) - 1):start_idx = col_ptr[j]end_idx = col_ptr[j + 1]for idx in range(start_idx, end_idx):if row_indices[idx] == i:row_elements.append((j, values[idx]))  # 存储列索引和非零值return row_elements# 创建一个稀疏矩阵
dense_matrix = np.array([[1, 0, 0, 0],[0, 3, 0, 4],[0, 0, 5, 0]])# 将稀疏矩阵转换为CSC格式
csc = csc_matrix(dense_matrix)# 提取CSC格式的三个数组
values = csc.data
row_indices = csc.indices
col_ptr = csc.indptr# 输出CSC表示
print("Values:", values)              # 非零元素的值
print("Row indices:", row_indices)    # 非零元素的行索引
print("Column pointer:", col_ptr)     # 每列第一个非零元素的索引# 查找A[1, 3]元素
print(f"A[1, 3] = {get_element(1, 3, col_ptr, row_indices, values)}")# 查找第2列的所有非零元素
print(f"Non-zero elements in column 2: {get_column_non_zero_elements(2, col_ptr, row_indices, values)}")# 查找第1行的所有非零元素
print(f"Non-zero elements in row 1: {get_row_non_zero_elements(1, col_ptr, row_indices, values)}")

3.优点

  1. 节省内存空间:只存储非零元素及其相关信息,适用于稀疏矩阵,避免了存储大量零值。
  2. 便于按列操作:CSC格式将矩阵按列组织,这使得按列进行的操作(如列向量乘法)非常高效。例如,在矩阵乘法中,矩阵的每一列都可以通过 valuesrow_indices 直接访问。
  3. 矩阵压缩:由于稀疏矩阵的非零元素数量相对较少,CSC格式通过压缩存储可以显著减少内存消耗。

4.缺点

  1. 按行操作较慢:由于CSC格式主要优化了按列操作,按行的操作(如矩阵的行向量乘法)需要额外的时间查找每行的非零元素,因此效率较低。
  2. 更新效率低:如果需要频繁地修改矩阵中的非零元素,CSC格式可能不是最好的选择,因为它对矩阵的插入和删除操作不太高效。

3. 坐标格式(Coordinate List, COO)

坐标格式(Coordinate List, COO) 是一种非常直观的稀疏矩阵存储格式,广泛用于表示稀疏矩阵。它通过将矩阵中的非零元素的行索引、列索引和值存储为一个三元组列表来表达矩阵数据。由于其简单性和直观性,COO格式在很多矩阵计算库中被用于稀疏矩阵的表示。

1.COO格式的结构

COO格式的三元组表示

每个稀疏矩阵中的非零元素都由一个三元组 (row_index, col_index, value) 表示。所有这些三元组会被存储在三个数组中:

  1. row_indices:存储所有非零元素的行索引。
  2. col_indices:存储所有非零元素的列索引。
  3. values:存储所有非零元素的值。

在这里插入图片描述

示例:二维稀疏矩阵

假设我们有以下稀疏矩阵:

A = [ 1 0 0 0 0 3 0 4 0 0 5 0 ] A = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 3 & 0 & 4 \\ 0 & 0 & 5 & 0 \end{bmatrix} A= 100030005040

这个矩阵的非零元素是 1、3、5、4,它们的行索引和列索引分别如下所示:

  • 1 位于第0行第0列。
  • 3 位于第1行第1列。
  • 5 位于第2行第2列。
  • 4 位于第1行第3列。

2.COO格式存储

对于上面的矩阵,COO格式将该矩阵表示为以下三个数组:

  • row_indices = [0, 1, 2, 1]:表示非零元素的行索引。
  • col_indices = [0, 1, 2, 3]:表示非零元素的列索引。
  • values = [1, 3, 5, 4]:表示非零元素的值。

因此,矩阵 A A A 的COO格式表示如下:

Row IndexColumn IndexValue
001
113
225
134

3.查找矩阵中特定元素

虽然COO格式非常直观,但对于许多矩阵操作,它的性能不如CSR或CSC格式。因此,COO格式通常用于矩阵的构建或转换,而对于实际的矩阵运算,通常会将其转换为CSR或CSC格式。

import numpy as np
from scipy.sparse import coo_matrix# 要查找矩阵中的指定元素(如A[i, j]),我们可以直接遍历行索引和列索引,查找目标位置。
def get_element(i, j, row_indices, col_indices, values):# 遍历所有非零元素,查找是否匹配(i, j)for idx in range(len(row_indices)):if row_indices[idx] == i and col_indices[idx] == j:return values[idx]  # 返回对应的非零元素值# 如果没有找到,返回0return 0# 要获取矩阵的指定列的所有非零元素,可以通过col_indices数组遍历找到指定列的所有元素。
def get_column_non_zero_elements(j, row_indices, col_indices, values):column_elements = []# 遍历所有非零元素,查找列索引为j的元素for idx in range(len(col_indices)):if col_indices[idx] == j:column_elements.append((row_indices[idx], values[idx]))  # 存储行索引和非零值return column_elements# 要获取矩阵中指定行的所有非零元素,可以通过row_indices数组遍历找到指定行的所有元素。
def get_row_non_zero_elements(i, row_indices, col_indices, values):row_elements = []# 遍历所有非零元素,查找行索引为i的元素for idx in range(len(row_indices)):if row_indices[idx] == i:row_elements.append((col_indices[idx], values[idx]))  # 存储列索引和非零值return row_elements# 创建一个稀疏矩阵
dense_matrix = np.array([[1, 0, 0, 0],[0, 3, 0, 4],[0, 0, 5, 0]])# 将稀疏矩阵转换为COO格式
coo = coo_matrix(dense_matrix)# 提取COO格式的三个数组
values = coo.data
row_indices = coo.row
col_indices = coo.col# 输出COO表示
print("Values:", values)              # 非零元素的值
print("Row indices:", row_indices)    # 非零元素的行索引
print("Column indices:", col_indices) # 非零元素的列索引# 查找A[1, 3]元素
print(f"A[1, 3] = {get_element(1, 3, row_indices, col_indices, values)}")# 查找第2列的所有非零元素
print(f"Non-zero elements in column 2: {get_column_non_zero_elements(2, row_indices, col_indices, values)}")# 查找第1行的所有非零元素
print(f"Non-zero elements in row 1: {get_row_non_zero_elements(1, row_indices, col_indices, values)}")

4.优点

  1. 直观简洁:COO格式是非常简单和直观的存储方式。它通过列出非零元素的索引和对应的值来表示矩阵,因此非常适合用于构建稀疏矩阵。

  2. 易于修改:COO格式很容易进行元素的插入或删除,只需在相应的行和列索引中增加或删除元素即可。

  3. 适用于构建稀疏矩阵:由于COO格式直接记录了矩阵中的非零元素,因此在构建稀疏矩阵时非常有用,特别是在数据从其他稀疏格式转换过来的时候。

5.缺点

  1. 不适合矩阵操作:COO格式对于许多常见的矩阵操作(如矩阵乘法、转置等)效率较低,因为每次需要遍历整个三元组数组。

  2. 内存效率差:相对于CSR或CSC等压缩格式,COO格式存储了更多的元数据(行索引、列索引和元素值),因此内存效率较低,尤其是当矩阵非常稀疏时。

  3. 不支持高效按行或按列访问:COO格式无法直接按行或按列高效访问数据,因为它只存储了非零元素的位置和数值,而没有提供有效的索引结构来支持按行或按列操作。

总结
坐标格式(COO) 是一种简单且直观的稀疏矩阵存储格式,适用于存储和构建稀疏矩阵,特别是在矩阵元素的插入和删除操作中表现出色。然而,由于其在矩阵操作(如矩阵乘法和转置等)中的效率较低,它通常作为中间格式使用,而将矩阵转换为更高效的格式(如CSR或CSC)来进行实际计算。

4. 对角线格式(Diagonal, DIA)

对角线格式(Diagonal, DIA) 是稀疏矩阵存储的另一种有效方式,尤其适用于具有显著对角结构的矩阵,例如对角矩阵、带状矩阵等。与稀疏矩阵的其他存储格式(如CSR、CSC和COO)不同,对角线格式将矩阵中的非零元素存储在其对角线位置上,这种格式特别适用于那些非零元素主要分布在矩阵的对角线附近的情况。

对角线格式通过存储矩阵的每条对角线的元素来压缩稀疏矩阵。具体而言,矩阵中的每一条对角线都被存储为一个数组,所有这些数组被打包在一个大的二维数组中,从而避免了为每个元素存储完整的行和列索引。通过这种方式,DIA格式能够有效压缩包含带状或对角结构的矩阵。

1.DIA格式的结构

对于一个矩阵,对角线是指矩阵中行索引与列索引之间的差是常数的元素所在的集合。例如,主对角线上的元素行列索引差为0,副对角线上元素行列索引差为1,其他对角线依此类推。

在DIA格式中,矩阵中的非零元素按照其对角线进行分组,并且每条对角线的元素会被存储在一个数组中。DIA格式包含两个主要部分:

  1. 对角线偏移量:存储每条对角线的相对位置(即,行索引与列索引之间的差)。
  2. 对角线值数组:对于每条对角线,存储该对角线上的非零元素值。
    在这里插入图片描述

2.DIA格式存储

考虑一个带状矩阵(即只有主对角线及其附近几条对角线具有非零元素):

A = [ 1 2 0 0 0 3 4 5 0 0 0 6 7 8 0 0 0 9 10 11 0 0 0 12 13 ] A = \begin{bmatrix} 1 & 2 & 0 & 0 & 0 \\ 3 & 4 & 5 & 0 & 0 \\ 0 & 6 & 7 & 8 & 0 \\ 0 & 0 & 9 & 10 & 11 \\ 0 & 0 & 0 & 12 & 13 \end{bmatrix} A= 13000246000579000810120001113

这个矩阵的非零元素分布在以下几个对角线上:

  • 主对角线(对角线偏移:0):1, 4, 7, 10, 13
  • 上对角线(对角线偏移:1):2, 5, 8, 11
  • 下对角线(对角线偏移:-1):3, 6, 9, 12

对角线偏移量(diagonal offset)

对于上述矩阵,可以定义三个对角线偏移量:

  • 对角线0(主对角线):偏移量为0。
  • 对角线1(上对角线):偏移量为1。
  • 对角线-1(下对角线):偏移量为-1。

对角线值数组(diagonal values)

  • 对角线0的值:[1, 4, 7, 10, 13]
  • 对角线1的值:[2, 5, 8, 11]
  • 对角线-1的值:[3, 6, 9, 12]

因此,DIA格式表示该矩阵时,主要存储三个信息:

  1. 偏移量数组[0, 1, -1]
  2. 对角线值数组
    • 对角线0的值:[1, 4, 7, 10, 13]
    • 对角线1的值:[2, 5, 8, 11]
    • 对角线-1的值:[3, 6, 9, 12]

3.查找矩阵中特定元素

对角线格式(Diagonal, DIA) 中,矩阵的非零元素被组织为对角线上的块,每个对角线被单独存储。每个对角线存储的是该对角线上的所有非零元素。要查找元素,我们首先需要确定该元素所在的对角线,然后在对应的对角线上查找。

import numpy as np
from scipy.sparse import dia_matrix# 要查找矩阵中的指定元素(如A[i, j]),我们需要根据对角线偏移量找到该元素是否存在。
def get_element(i, j, offsets, data):# 计算元素 (i, j) 所在的对角线索引diag_index = j - i# 检查是否存在该对角线if diag_index in offsets:# 获取该对角线的数据diag_data = data[offsets.index(diag_index)]# 计算该对角线上元素的索引diag_offset = i - offsets[offsets.index(diag_index)]# 检查偏移是否在该对角线的范围内if 0 <= diag_offset < len(diag_data):return diag_data[diag_offset]  # 返回对应的非零元素值# 如果没有找到,返回0return 0# 要获取矩阵的指定列的所有非零元素,可以遍历对角线,检查每个对角线是否包含该列。
def get_column_non_zero_elements(j, offsets, data, shape):column_elements = []# 遍历每个对角线,检查其是否包含指定的列for diag_index, diag_data in zip(offsets, data):# 计算对角线的起始列start_col = max(0, -diag_index)if start_col <= j < start_col + len(diag_data):row_index = j - diag_index  # 计算该列在对角线中的行索引column_elements.append((row_index, diag_data[j - start_col]))  # 存储行索引和非零值return column_elements# 要获取矩阵中指定行的所有非零元素,可以遍历对角线,检查每个对角线是否包含该行。
def get_row_non_zero_elements(i, offsets, data):row_elements = []# 遍历每个对角线,检查其是否包含指定的行for diag_index, diag_data in zip(offsets, data):# 计算对角线的起始行start_row = max(0, diag_index)if start_row <= i < start_row + len(diag_data):col_index = i - diag_index  # 计算该行在对角线中的列索引row_elements.append((col_index, diag_data[i - start_row]))  # 存储列索引和非零值return row_elements# 创建一个稀疏矩阵
dense_matrix = np.array([[1, 0, 0, 0],[0, 3, 0, 4],[0, 0, 5, 0]])# 将稀疏矩阵转换为DIA格式
dia = dia_matrix(dense_matrix)# 提取DIA格式的三个数组
data = dia.data
offsets = dia.offsets
shape = dia.shape# 输出DIA表示
print("Data (Diagonal elements):", data)        # 非零元素值
print("Offsets (Diagonal offsets):", offsets)   # 对角线偏移量
print("Shape:", shape)                           # 矩阵形状# 查找A[1, 3]元素
print(f"A[1, 3] = {get_element(1, 3, offsets, data)}")# 查找第2列的所有非零元素
print(f"Non-zero elements in column 2: {get_column_non_zero_elements(2, offsets, data, shape)}")# 查找第1行的所有非零元素
print(f"Non-zero elements in row 1: {get_row_non_zero_elements(1, offsets, data)}")

4.优点

  1. 适用于带状矩阵和对角矩阵:DIA格式特别适合存储带状矩阵、对角矩阵或任何非零元素主要集中在几个对角线附近的矩阵。对于这些矩阵,DIA格式可以显著节省存储空间。

  2. 紧凑存储:DIA格式通过仅存储非零元素及其对角线信息,大大减少了内存使用。对于对角线稀疏矩阵,DIA格式比CSR或CSC格式更为高效。

  3. 高效的操作:对于矩阵中的许多操作(如矩阵-向量乘法),DIA格式提供了相对高效的实现方式。

5.缺点

  1. 不适合稀疏度极高的矩阵:当稀疏矩阵的非零元素分布较为分散时,DIA格式的存储效率较低,因为它依然为每一条对角线分配一个存储数组,导致存储空间浪费。

  2. 无法处理非对角矩阵的稀疏矩阵:对于那些几乎只有少量非零元素、且非零元素位置没有明显对角线结构的稀疏矩阵,DIA格式并不适用。在这种情况下,其他格式(如CSR或COO)更为高效。

  3. 限制矩阵结构:DIA格式只能有效地支持带状或对角状的稀疏矩阵,对于结构复杂的稀疏矩阵,其他优势不明显。

总结
对角线格式(DIA) 是一种非常适用于带状矩阵、对角矩阵或其他对角结构明显的稀疏矩阵的存储格式。它通过将矩阵的每条对角线存储为一个数组来压缩非零元素,具有较高的存储效率和计算效率。然而,DIA格式并不适用于稀疏性较高、非零元素分布不规则的矩阵。对于这种类型的矩阵,其他稀疏矩阵格式(如CSR或COO)会更为合适。

5. 列块压缩(Block Compressed Sparse Row, BCSR)

列块压缩(Block Compressed Sparse Row, BCSR) 是一种在稀疏矩阵的基础上进行优化的存储格式。它是传统压缩稀疏行(CSR)格式的扩展,旨在通过将矩阵分割为块来提高存储和计算效率,特别是在矩阵的块结构明显时。

BCSR格式通过将矩阵按小块(block)进行划分,使得每个块可以被视为一个较大的元素,并按传统的CSR格式压缩存储。这种方式通常用于矩阵具有相同结构、块状或带状的情况,从而进一步减少存储空间并加速矩阵运算。BCSR格式在许多科学计算、工程问题中,尤其是大规模矩阵计算(如有限元分析、图像处理等领域)中被广泛应用。

1.BCSR格式的结构

与传统的CSR格式一样,BCSR格式也将矩阵的非零元素存储为压缩格式,但不同的是,BCSR会首先将矩阵划分成固定大小的小子矩阵(或称为块),然后对这些块进行压缩

假设原始稀疏矩阵 A A A 的大小为 m × n m \times n m×n,在BCSR格式中,它首先被分割成多个大小为 b × b b \times b b×b(例如,2×2或4×4等)的块矩阵,然后对这些块进行压缩存储。每个块本身是一个矩阵,只有非零元素会被存储。BCSR格式的压缩主要基于以下几个步骤:

  1. 矩阵分块:将稀疏矩阵分割成多个固定大小的块。每个块大小通常是 b × b b \times b b×b,块的数量依赖于矩阵的维度和块大小。

  2. 存储块信息:对于每个块,存储其在原始矩阵中的行和列索引,和该块的非零元素。BCSR格式只存储非零元素及相关的索引。

  3. 压缩存储:存储所有非零块的数据,每个块的数据按压缩稀疏矩阵存储(例如,只有非零元素会被存储)。块的的信息通过行和列的偏移量进行组织。

2.BCSR格式存储

BCSR格式通常包括三个主要部分:

  • block_values:存储矩阵中所有非零块的值。每个块可以是一个大小为 b × b b \times b b×b 的子矩阵,block_values 是一个一维数组,其中每个元素表示一个矩阵块的值。每个块内的元素按行优先顺序存储。

  • row_ptr:行指针数组,存储每一行的第一个块在 block_values 中的索引位置。row_ptr[i] 表示第 i i i 行开始的第一个块的索引,row_ptr[i+1] 表示第 i i i 行最后一个块的结束索引。

  • col_idx:存储每个块所在的列索引。在 block_values 中的每个块都与一个特定的列相关联。col_idx[i] 存储了第 i i i 个块所在的列索引。

  • block_shape:块的形状,即每个块的大小。对于大多数应用,块的大小通常是 b × b b \times b b×b,其中 b b b 是一个小的整数(例如 2 或 4),表示块的维度。

BCSR格式的存储示例

假设我们有一个如下的稀疏矩阵:

A = [ 1 0 2 0 3 4 0 0 5 0 6 0 0 7 0 8 10 11 0 0 0 0 12 0 ] A = \begin{bmatrix} 1 & 0 & 2 & 0 \\ 3 & 4 & 0 & 0 \\ 5 & 0 & 6 & 0 \\ 0 & 7 & 0 & 8 \\ 10 & 11 & 0 & 0 \\ 0 & 0 & 12 & 0 \end{bmatrix} A= 135010004071102060012000800

如果我们将其按 2 × 2 2 \times 2 2×2 块进行划分(即每个块是2×2大小),则矩阵将被划分为如下几个块:

  • B 1 = [ 1 0 0 3 ] B_1 = \begin{bmatrix} 1 & 0 \\ 0 & 3 \end{bmatrix} B1=[1003],
  • B 2 = [ 2 0 4 0 ] B_2 = \begin{bmatrix} 2 & 0 \\ 4 & 0 \end{bmatrix} B2=[2400],
  • B 3 = [ 5 0 0 8 ] B_3 = \begin{bmatrix} 5 & 0 \\ 0 & 8 \end{bmatrix} B3=[5008],
  • B 4 = [ 6 0 9 0 ] B_4 = \begin{bmatrix} 6 & 0 \\ 9 & 0 \end{bmatrix} B4=[6900],
  • B 5 = [ 10 0 0 11 ] B_5 = \begin{bmatrix} 10 & 0 \\ 0 & 11 \end{bmatrix} B5=[100011],
  • B 6 = [ 12 0 0 0 ] B_6 = \begin{bmatrix} 12 & 0 \\ 0 & 0 \end{bmatrix} B6=[12000].

然后,这些块将被压缩存储在BCSR格式中。

行指针数组

每一行的第一个块位置:

  • Row Pointer Array: [0, 2, 4, 6, 8],表示第0行有两个块,第1行有两个块,以此类推。

列索引数组

每个块所在的列:

  • Column Index Array: [0, 2, 0, 3, 0, 4],表示第0行第一个块位于第0列,第二个块位于第2列;第1行第一个块位于第0列,第二个块位于第3列,依此类推。

非零值数组

每个块中的非零元素:

  • Non-zero Value Array: [1, 3, 2, 4, 5, 8, 6, 9, 10, 11, 12]

3.查找矩阵中特定元素

import numpy as np
from scipy.sparse import bcsr_matrix# 要查找矩阵中的指定元素(如A[i, j]),我们需要根据块的索引来查找目标元素。
def get_element(i, j, row_ptr, col_idx, block_values, block_size):# 计算矩阵元素 (i, j) 所在的块block_row = i // block_sizeblock_col = j // block_size# 获取该块在行中对应的块索引block_index = Nonefor idx in range(row_ptr[block_row], row_ptr[block_row + 1]):if col_idx[idx] == block_col:block_index = idxbreak# 如果该块不存在,则返回0if block_index is None:return 0# 计算块内部的行和列索引row_within_block = i % block_sizecol_within_block = j % block_size# 返回块内的值return block_values[block_index][row_within_block, col_within_block]# 获取矩阵指定列的所有非零元素
def get_column_non_zero_elements(j, row_ptr, col_idx, block_values, block_size):column_elements = []# 遍历所有块,检查它们是否属于指定列for block_row in range(len(row_ptr) - 1):for idx in range(row_ptr[block_row], row_ptr[block_row + 1]):if col_idx[idx] == j:# 获取块内的所有非零元素for row_within_block in range(block_size):for col_within_block in range(block_size):if block_values[idx][row_within_block, col_within_block] != 0:column_elements.append((block_row * block_size + row_within_block,j * block_size + col_within_block,block_values[idx][row_within_block, col_within_block]))return column_elements# 获取矩阵指定行的所有非零元素
def get_row_non_zero_elements(i, row_ptr, col_idx, block_values, block_size):row_elements = []# 确定该行所属的块行block_row = i // block_sizefor idx in range(row_ptr[block_row], row_ptr[block_row + 1]):# 获取该块内所有非零元素for row_within_block in range(block_size):if i % block_size == row_within_block:for col_within_block in range(block_size):if block_values[idx][row_within_block, col_within_block] != 0:row_elements.append((block_row * block_size + row_within_block,col_idx[idx] * block_size + col_within_block,block_values[idx][row_within_block, col_within_block]))return row_elements# 创建一个稀疏矩阵
dense_matrix = np.array([[1, 0, 0, 0],[0, 3, 0, 4],[0, 0, 5, 0]])# 假设每个块的大小是 2x2
block_size = 2# 将稀疏矩阵转换为BCSR格式
bcsr = bcsr_matrix(dense_matrix)# 提取BCSR格式的数组
block_values = bcsr.data
row_ptr = bcsr.indptr
col_idx = bcsr.indices# 输出BCSR表示
print("Block Values:", block_values)     # 非零块的值
print("Row Pointer:", row_ptr)            # 每行第一个块的索引
print("Column Indices:", col_idx)        # 每个块的列索引# 查找A[1, 3]元素
print(f"A[1, 3] = {get_element(1, 3, row_ptr, col_idx, block_values, block_size)}")# 查找第2列的所有非零元素
print(f"Non-zero elements in column 2: {get_column_non_zero_elements(2, row_ptr, col_idx, block_values, block_size)}")# 查找第1行的所有非零元素
print(f"Non-zero elements in row 1: {get_row_non_zero_elements(1, row_ptr, col_idx, block_values, block_size)}")

4.优点

  1. 适用于块结构矩阵:BCSR格式特别适合存储具有明显块结构的稀疏矩阵,如有限元分析中的矩阵。它能够有效利用矩阵的块特性,提升存储和计算效率。

  2. 更好的缓存性能:BCSR格式以块矩阵数据按块存储,这有助于提高现代CPU缓存的命中率,特别是在处理较大的矩阵时。

  3. 并行计算优化:块状结构可以更容易地实现并行计算,尤其是在每个块可以独立处理的情况下。

5.缺点

  1. 仅适用于块结构矩阵:BCSR格式并不适用于所有类型的稀疏矩阵,特别是那些没有明显块结构的矩阵。在这些情况下,其他稀疏矩阵格式(如CSR或CSC)会更有效。

  2. 块大小选择依赖于矩阵结构:选择合适的块大小是关键,如果块大小选择不当,可能会导致存储浪费或计算效率下降。

  3. 复杂的块划分:在实际应用中,如何将矩阵划分为合适的块并保持计算的高效率是一个挑战。如果矩阵的非零元素分布为分散或不规则,块划分可能会导致存储和计算的低效。

总结
列块压缩(BCSR) 格式是一种通过将稀疏矩阵划分为块并压缩存储每个块的格式,适用于具有明显块结构的稀疏矩阵。BCSR格式能够显著提高带状矩阵和块结构矩阵的存储效率和计算效率,特别是在需要进行大规模矩阵运算时。尽管它对于某些特定类型的矩阵非常有效,但并不适用于所有类型的稀疏矩阵。在选择BCSR格式时,需要根据矩阵的结构特性来决定块的大小和划分策略。

三、稀疏矩阵的基本操作

稀疏矩阵的操作与稠密矩阵类似,但需要考虑其特有的存储格式和优化策略。以下是几种常见的稀疏矩阵操作:

1. 矩阵-向量乘法(Matrix-Vector Multiplication)

矩阵-向量乘法是稀疏矩阵中最常见的操作之一。以CSR格式为例,其计算过程如下:

  1. 遍历每一行。
  2. 对于每一行,遍历非零元素。
  3. 将非零元素乘以对应的向量元素,并累加到结果向量中。

复杂度:(O(NZ)),其中 (NZ) 为非零元素数量。

2. 矩阵-矩阵乘法(Matrix-Matrix Multiplication)

矩阵-矩阵乘法较为复杂,尤其是两矩阵都为稀疏矩阵时。常用的方法包括:

  1. 乘法算法的优化:利用稀疏矩阵的存储格式,避免不必要的零乘法。
  2. 利用块结构:对于块稀疏矩阵,利用块乘法提高效率。
  3. 并行计算:利用多线程或GPU进行并行乘法计算。

3. 矩阵转置(Transpose)

矩阵转置操作将稀疏矩阵的行列互换。在CSR和CSC格式中,这一操作可以通过转换存储格式来高效实现:

  1. 将CSR转换为CSC,或反之。
  2. 重新排列非零元素的存储顺序。

4. 矩阵分解(Matrix Decomposition)

稀疏矩阵分解在科学计算和工程模拟中非常重要,常见的分解方法包括:

  1. LU分解:将矩阵分解为下三角矩阵 ( L ) 和上三角矩阵 ( U )。
  2. Cholesky分解:适用于对称正定矩阵,将矩阵分解为 ( LL^T )。
  3. QR分解:将矩阵分解为正交矩阵 ( Q ) 和上三角矩阵 ( R )。

这些分解方法需要针对稀疏矩阵进行优化,以保持稀疏性并提高计算效率。

5. 求逆与解线性方程组(Matrix Inversion and Solving Linear Systems)

稀疏矩阵求逆和解线性方程组在许多应用中非常关键,常用的方法包括:

  1. 迭代方法:如共轭梯度法(Conjugate Gradient)、GMRES等,适用于大规模稀疏矩阵。
  2. 直接方法:稀疏LU分解,适用于较小规模或结构良好的稀疏矩阵。

四、稀疏矩阵的应用领域

稀疏矩阵由于其高效的存储和计算特性,被广泛应用于多个领域,以下是几个主要应用:

1. 科学计算与工程模拟

在科学计算和工程模拟中,许多问题可以表示为稀疏矩阵,如有限元方法(FEM)、有限差分方法(FDM)等。稀疏矩阵在这些方法中用于表示系统的刚度矩阵、质量矩阵等。

2. 机器学习与数据挖掘

在机器学习和数据挖掘中,稀疏矩阵用于表示高维数据、特征矩阵和图结构。例如,文本数据中的词袋模型(Bag-of-Words)常用稀疏矩阵表示,图神经网络中的邻接矩阵也是稀疏矩阵。

3. 图像处理与计算机视觉

在图像处理和计算机视觉中,稀疏矩阵用于表示图像的特征、滤波器和变换矩阵。例如,稀疏编码和稀疏表示在图像去噪、图像恢复和超分辨率重建中发挥重要作用。

4. 网络科学

在网络科学中,图的邻接矩阵和拉普拉斯矩阵通常为稀疏矩阵,用于表示大型网络的连接关系和结构特性。这些矩阵在社区检测、网络分析和传播模拟中广泛使用。

5. 优化与运筹学

在优化和运筹学中,线性规划、整数规划和其他优化问题常常涉及稀疏矩阵。稀疏矩阵在这些问题的约束条件和目标函数中发挥关键作用。

五、稀疏矩阵相关算法

处理稀疏矩阵需要专门设计的算法,以充分利用其稀疏性,提高计算效率。以下是一些常见的稀疏矩阵相关算法:

1. 稀疏矩阵乘法

稀疏矩阵乘法是稀疏矩阵运算中最基本的操作之一,通常采用行优先或列优先的方法,结合适当的存储格式进行优化。

2. 稀疏矩阵分解

稀疏矩阵分解包括LU分解、Cholesky分解和QR分解等,这些分解方法需要保持矩阵的稀疏性,避免引入过多的填充元素(fill-in)。

3. 稀疏线性方程组求解

稀疏线性方程组求解是许多应用中的核心任务,常用的方法包括:

  • 直接方法:如稀疏LU分解、稀疏Cholesky分解。
  • 迭代方法:如共轭梯度法(Conjugate Gradient)、最小残差法(MINRES)、广义最小残量法(GMRES)等。

4. 稀疏矩阵压缩与重排序

为了减少存储空间和提高计算效率,稀疏矩阵常常需要进行压缩和重排序。重排序算法如Cuthill-McKee算法和Reverse Cuthill-McKee算法用于减少稀疏矩阵的带宽和填充系数。

5. 稀疏矩阵向量运算优化

稀疏矩阵向量运算(Sparse Matrix-Vector Multiplication, SpMV)是许多算法的基础,优化SpMV的性能对于整体计算效率至关重要。常用的优化策略包括:

  • 数据对齐与内存访问优化:确保数据在内存中的连续性,减少缓存未命中。
  • 向量化与并行化:利用SIMD指令和多线程并行计算,加速运算过程。

六、 稀疏矩阵的优化策略

为了进一步提高稀疏矩阵运算的效率,研究人员提出了多种优化策略:

1. 缓存优化

利用缓存友好的数据布局和访问模式,减少内存访问的延迟,提高运算速度。例如,按行或按列的顺序存储数据,匹配缓存行的大小。

2. 并行与分布式计算

利用多核CPU、GPU和分布式计算框架,实现稀疏矩阵运算的并行化处理,显著提高计算效率。GPU特别适合进行大规模稀疏矩阵运算,具备强大的并行计算能力。

3. 向量化与SIMD优化

通过利用现代CPU的向量化指令集(如AVX、SSE),实现稀疏矩阵运算的SIMD优化,提升单核性能。

4. 数据压缩与存储格式转换

选择最适合特定操作的存储格式,减少数据冗余和内存占用。例如,使用CSR格式进行高效的行访问,使用COO格式进行矩阵构建。

5. 算法级优化

设计专门针对稀疏矩阵特点的算法,减少不必要的计算和存储。例如,利用图结构的节点进行高效的块匹配和路径搜索。

七、稀疏矩阵的挑战与未来发展

尽管稀疏矩阵在存储和计算上具有显著优势,但在实际应用中仍面临诸多挑战:

1. 填充问题 (Fill-in)

在稀疏矩阵分解过程中,原本稀疏的矩阵可能产生大量的非零元素(填充),增加存储和计算开销。为减少填充,可以采用压缩重排和专门的分解算法。

2. 动态稀疏矩阵

在某些应用中,稀疏矩阵的结构会动态变化(如图的动态演化),需要支持高效的稀疏矩阵构建和修改操作,这对存储格式和算法提出了更高要求。

3. 高效稀疏矩阵乘法

稀疏矩阵乘法(SpMM)是许多算法的核心操作,设计高效的SpMM算法,特别是在并行和分布式环境中,仍然是一个活跃的研究领域。

4. 稀疏深度学习

在深度学习中,稀疏表示和稀疏矩阵技能显著减少模型参数和计算量,提升模型的效率。然而,如何设计高效的稀疏深度学习算法和框架支持,仍然面临挑战。

5. 新型存储格式

随着硬件的发展和应用需求的变化,新的稀疏矩阵存储格式不断涌现,旨在进一步减少存储开销,提高访问效率以适应并行计算需求。

6. 硬件加速

利用专用硬件(如GPU、FPGA、TPU等)加速稀疏矩阵运算,是提高大规模稀疏矩阵计算效率的重要方向。研究如何优化稀疏矩阵在这些硬件上的存储和计算,具有重要意义。

总结
稀疏矩阵作为一种高效表示和处理稀疏数据的工具,在多个领域中发挥着关键作用。通过专门的存储格式和优化算法,稀疏矩阵不仅能够显著减少内存占用,还能提高计算效率,特别是在处理大规模数据时。随着科学计算和机器学习的发展,稀疏矩阵的应用将更加广泛,相关算法和工具也将不断优化和创新。


参考文献:
稀疏矩阵(Sparse Matrix)

结~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/15920.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

操作系统:页表中的页表项

操作系统&#xff1a;页表中的页表项 页表是操作系统用于跟踪进程使用的虚拟地址与系统内存中相应物理地址之间映射的数据结构。 页表项&#xff08;Page Table Entry&#xff0c;PTE&#xff09;是页表中的一个条目&#xff0c;用于存储有关特定内存页的信息。每个页表项包含…

Docker部署Kafka SASL_SSL认证,并集成到Spring Boot

1&#xff0c;创建证书和密钥 需要openssl环境&#xff0c;如果是Window下&#xff0c;下载openssl Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 还需要keytool环境&#xff0c;此环境是在jdk环境下 本案例所使用的账号密码均为&#xff1a; ka…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《基于改进容积卡尔曼滤波的含光伏配电网动态状态估计》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

jenkins使用cli发行uni-app到h5

官网文档HBuilderX 文档 首先确定是否存在环境变量 正常情况cmd中执行cli 如果提示 cli 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。请先配置环境变量 Freestyle Project项目在Build Steps中增加Execute Windows batch command命令如下 d: cd D:\devsof…

FMEA 在新兴技术领域(如量子计算、人工智能芯片等)的应用挑战与机遇

【大家好&#xff0c;我是唐Sun&#xff0c;唐Sun的唐&#xff0c;唐Sun的Sun。】 摘要&#xff1a; 本文深入探讨了 FMEA&#xff08;失效模式及后果分析&#xff09;在如量子计算、人工智能芯片等新兴技术领域的应用所面临的挑战与机遇。随着科技的飞速进步&#xff0c;新兴技…

websocket身份验证

websocket身份验证 前言 上一集我们就完成了websocket初始化的任务&#xff0c;那么我们完成这个内容之后就应该完成一个任务&#xff0c;当客户端与服务端连接成功之后&#xff0c;客户端应该主动发起一个身份认证的消息。 身份认证proto 我们看一眼proto文件的内容。 我…

Spire.PDF for .NET【页面设置】演示:复制 PDF 文档中的页面

在某些情况下&#xff0c;我们需要创建 PDF 文档中现有页面的副本&#xff0c;而不是复制整个文件&#xff0c;特别是如果我们必须创建某个页面的数百份副本&#xff0c;那么逐个复制页面可能会很繁琐。本文演示了如何使用 Spire.PDF 复制 PDF 文档中的页面并一次创建多个副本的…

Vue-组件三大组成组件通信

一、学习目标 1.组件的三大组成部分&#xff08;结构/样式/逻辑&#xff09; scoped解决样式冲突/data是一个函数 2.组件通信 组件通信语法 父传子 子传父 非父子通信&#xff08;扩展&#xff09; 3.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xff09; 拆…

2024CVPR点云-1-点云分类CausalPC

文章摘要&#xff1a;深度神经网络在点云分类中表现出了显著的性能。然而&#xff0c;以前的工作表明它们容易受到对抗性扰动的影响&#xff0c;这些扰动可以操纵它们的预测。鉴于点云的独特模态&#xff0c;出现了各种攻击策略&#xff0c;这对现有的防御提出了挑战&#xff0…

ssm114基于SSM框架的网上拍卖系统的设计与实现+vue(论文+源码)_kaic

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;商品拍卖当然也不能排除在外&#xff0c;随着商品拍卖管理的不断成熟&#xff0c;它彻底改变了过去传统的经营管理方式&#xff0c;不仅使商品…

【Qt聊天室客户端】消息功能--发布程序

1. 获取文件内容 主要目标是实现获取内容二进制数据的接口&#xff0c;主要是为后面的消息功能提供服务 具体实现 客户端发送请求 服务端处理请求&#xff0c;同时支持三种数据类型 客户端处理服务端的响应 2. 发送图片消息 客户端与服务端的通信约定 客户端从服务器中获取图片…

【CSS】opacity 影响 z-index 不生效

准备知识 一般来说&#xff0c;z-index 不生效的原因有&#xff1a; 父元素的 position 属性&#xff1a; z-index 只对 position 属性为 relative、absolute 或 fixed 的元素有效。 其他元素的 z-index&#xff1a; 如果页面中有其他元素也设置了较高的 z-index&#xff0c;…

Elasticsearch基本概念及使用

Elasticsearch 是一个开源的、分布式的全文搜索和分析引擎&#xff0c;基于 Apache Lucene 构建。它提供了快速的搜索能力&#xff0c;支持大规模的数据分析&#xff0c;广泛应用于日志分析、全文搜索、监控系统和商业智能等领域。ES操作指令是基于restAPI构建&#xff0c;也就…

C语言入门到精通(第六版)——第十六章

16、网络套接字编程 16.1、计算机网络基础 计算机网络技术是计算机技术和通信技术相结合的产物&#xff0c;代表计算机的一个重要发展方向。了解计算机的网络结构&#xff0c;有助于用户开发网络应用程序。 16.1.1、IP地址 为了使网络上的计算机能够彼此识别对方&#xff0c;…

Electron教程1-初学入门

玩转Electron Electron 是什么注意事项环境安装安装 vscode安装 git 第一个实例第二个实例第二个实例解读 总结问题解答 Electron 是什么 Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个…

柠乐音乐 1.3.87 | 界面优美支持无损音乐下载的音乐播放器

柠乐音乐app提供丰富的音乐资源&#xff0c;涵盖流行、摇滚、古典等多种类型音乐&#xff0c;并且全部免费。支持FLAC无损音质音乐免费高速下载。内置独特推荐算法&#xff0c;可根据用户喜好智能推荐音乐。还包括电台播放资源、歌单同步&#xff08;支持网易云音乐和QQ音乐&am…

【资料】网络安全风险评估报告,风险管理报告,网络安全风险管理计划,网络安全网络安全能力验证报(Word原件)

一、概述 1.1工作方法 1.2评估依据 1.3评估范围 1.4评估方法 1.5基本信息 二、资产分析 2.1 信息资产识别概述 2.2 信息资产识别 三、评估说明 3.1无线网络安全检查项目评估 3.2无线网络与系统安全评估 3.3 ip管理与补丁管理 3.4防火墙 四、威胁细类分析 4.1威胁…

change buffer:到底应该选择普通索引还是唯一索引

文章目录 引言第一章&#xff1a;普通索引和唯一索引在查询逻辑与效率上的对比1.1 查询逻辑分析1.2 查询效率对比 第二章&#xff1a;普通索引和唯一索引在更新逻辑与效率上的对比2.1 更新逻辑分析2.2 更新效率对比 第三章&#xff1a;底层原理详解 - 普通索引和唯一索引的区别…

软件工程师简历(精选篇)

【#软件工程师简历#】 一份专业而精准的软件工程师简历&#xff0c;不仅能够全面展示技术实力和项目经验&#xff0c;更是赢得理想工作机会的重要敲门砖。那么&#xff0c;如何撰写一份令人印象深刻的软件工程师简历呢&#xff1f;以下是幻主简历整理的软件工程师简历&#xf…

深度学习推荐系统的工程实现

参考自《深度学习推荐系统》——王喆&#xff0c;用于学习和记录。 介绍 之前章节主要从理论和算法层面介绍了推荐系统的关键思想。但算法和模型终究只是“好酒”&#xff0c;还需要用合适的“容器”盛载才能呈现出最好的味道&#xff0c;这里的“容器”指的就是实现推荐系统…