【问题标题】:Sparse matrix storage format designed for row/col manipulation?为行/列操作设计的稀疏矩阵存储格式?
【发布时间】:2016-06-21 11:40:30
【问题描述】:

我正在使用一个需要在稀疏矩阵中访问和存储数据的程序。大约 40-60% 的矩阵将是非零的,维度是从 14K 到 22K 元素的正方形。

这是我的问题 - 我将执行 很多 行和列操作。主要是添加、删除和交换。我查看了大多数现有的众所周知的稀疏矩阵格式(CRS、CCS、COO、块格式等),其中大多数似乎不太接受这些类型的操作。在您开始添加和删除整行或整列的那一刻,您不得不将所有元素更新到被操​​纵的行/列的任一侧,这是我想尽可能避免的事情(这发生在我身上您可能会以这样一种方式管理元素,即它们在矩阵中的坐标实际上存储为一对指向公共行或列索引的指针,并且通过简单地增加或减少该值来避免手动更新数千个元素)。

那里有类似的东西吗?

【问题讨论】:

  • 既然你有这么多的操作,而且你使用的是稀疏矩阵,你不应该考虑使用链表数组来代替吗?您将节省浪费的空间,并且您的操作应该更容易(特别是交换)
  • 你考虑过std::unordered_map<std::tuple<int, int>, double, tuplehasher>吗?
  • 一个典型的矩阵有多少行,多少列? “尺寸从 14K 到 22K 元素正方形” - 这是否意味着总共有 14K 到 22K 元素?或者一个矩阵是14000*14000到22000*22000?
  • 另外,你想要什么?你想节省时间吗?或者您想节省内存,并准备牺牲一些时间?或者是其他东西?同时,您提供的信息太少,无法给出有意义的答案。
  • 如果你有不频繁的读取和频繁的写入,你可以存储一个更改列表,并按需合成修改后的矩阵。

标签: c++ matrix


【解决方案1】:

我会考虑一个间接层,作为交换、插入和删除行和列的有效机制。

这类似于现代操作系统中虚拟内存的管理方式。这只是一个简短的说明:物理 RAM 地址由 CPU 的memory management unit 映射到线性寻址空间。 MMU 在主机 O/S 的帮助下,将实际的物理 RAM 地址映射到每个进程的虚拟地址空间。每个进程中指针和其他对象使用的地址不是真实的 RAM 地址,它们是虚拟的,由硬件 MMU 单元转换为实际的物理 RAM 地址。这就是为什么当系统空间不足时,主机操作系统可以将空闲进程分页到交换文件或分区中,然后稍后再加载回物理 RAM 的某个完全不同的区域,甚至不需要进程知道发生了什么。

无论如何,回到主题,这将是一种类似的方法。

考虑一个普通的花园品种二维std::map。这是你的稀疏矩阵:

std::map<size_t, std::map<size_t, value_t>>

第一个映射的维度或键是行,它为您提供第二个映射,其维度/键是列,最终包含您的值。

这很简单。这里没有什么惊天动地的。但正如您所理解的:移动、交换和插入行和列变得相当麻烦。

好的,让我们介绍您自己的个性化 MMU:您的地图管理单元。它几乎可以像硬件 MMU 一样工作:

std::map<size_t, size_t> rows;
std::map<size_t, size_t> columns;

因此,在您的示例中,要查找虚拟行 R、列 C 的值:

1) rows[R] 为您提供“物理”行号。

2) columns[C] 为您提供“物理”列号。

3) 现在,您获取“物理”行号和列号,然后转到您的二维std::map,并在给定物理行和列的情况下查找您的值。

那么,我们在这里得到什么?好吧,现在移动整行或整列涉及对地图管理单元的简单更新。从rowscolumns 映射中删除一个值,然后用不同的键、新的“逻辑”行或列将其放回原处。 “物理”二维地图中的值保持不变。

就是这样。移动、交换或插入行成为对 mmu 对象的简单操作。

还有两个细节需要注意:

A) 跟踪哪些物理行和列未被使用,并且可以在添加时分配给新的逻辑行和列。

B) 根据您的用例,可能还需要将物理行和列映射回虚拟行和列号。

这两项都是相当琐碎、直接的任务,可以作为你的家庭作业。

【讨论】:

    猜你喜欢
    • 2012-12-10
    • 2014-09-18
    • 2018-01-19
    • 1970-01-01
    • 2019-03-12
    • 2015-01-26
    • 2015-09-01
    • 2017-09-09
    • 2019-05-21
    相关资源
    最近更新 更多