C++中的稀疏数组

Posted

技术标签:

【中文标题】C++中的稀疏数组【英文标题】:Sparse array in C++ 【发布时间】:2014-01-23 14:16:25 【问题描述】:

我需要一个类似向量的容器,具有整数索引,但省略了一些索引。那么在 C++ 中表示这种稀疏数组的常用方法是什么? 我有一种直觉,std::map 主要用于此类目的。但是对于通常不添加新项目的容器来说,它的速度相当慢。你有什么建议?

UPD:不是很“稀疏”。也许大约 5%。项目主要在初始化步骤期间添加(之后不经常添加)。但是访问很频繁(如果不是很重要,显然我不会开始这个话题)。

【问题讨论】:

尝试使用 boost 或 C++11 的无序映射。 “稀疏”有多稀疏?百分比?如果经常期望在两者之间存储附加值,则某些技术可能不合适。您是否需要按索引顺序频繁访问/迭代? 解释它有多慢。 boost flat map 是 map 的更快版本,除了插入速度非常慢 (O (n)) 所以你的使用模式必须是 fill map,use map,而不是:fill map,use,add,use , 添加, 删除, 使用... 这似乎是XY Problem。问题显然是性能要求,您假设实现这些要求的方法是使用稀疏数组。 【参考方案1】:

是的,地图通常是正确的方法。

我建议使用 C++11 unordered_map(基于哈希表)来获得闪电般快速的查找:这几乎是没有连续递增键的最佳选择。

【讨论】:

虽然可能一开始就提出了错误的问题,但我看不出unordered_map 将如何满足对 sparse 容器的假定需求。在被索引之前,不能保证unordered_map 已经分配了任意未分配的索引。 @JohnDibling:你为什么需要它?只需将unordered_mapsize_t 设置为所需的映射类型。有什么问题? @John Dibling 但这就是稀疏数组的全部意义:在为数组元素 i 赋值之前,没有为索引 i 分配任何东西。 @laune:不,这与稀疏数组相反。稀疏阵列就像一个只有 10% 已满的磁盘。 90% 的空间仍然被分配,从某种意义上说,磁盘实际上并没有增长,但它充满了未初始化的垃圾。 @John Dibling wikipedia, "sparse array": "一个简单的数组实现可能会为整个数组分配空间,但是在非默认值很少的情况下,这种实现是低效的。”具有 N 个元素的向量(如数学)始终具有 N 个元素,但稀疏数组的实现不应分配所有无用的存储空间。我不确定您所说的“稀疏容器”是什么意思,但是拥有一个包含大量未使用空间的 container 并不是实现稀疏向量的明智选择。【参考方案2】:

也许像

std::vector<boost::optional<your_type>> 

对你来说已经足够了。

【讨论】:

可能不是一个好主意。不过,可能是正确的方法。取决于某些事情。 如果对象很大,它应该可以正常工作 - 但如果对象很小并且索引很大,则不会。 这取决于其他一些事情。例如他是否需要快速知道是否填充了特定索引?【参考方案3】:

项目主要是在初始化步骤中添加的(之后并不经常添加)。但访问频繁

在这种情况下,boost::container::flat_map 对您来说是一个不错的选择。它基本上只是一个排序的向量。优点(从网站上偷来的):

比标准关联容器更快的查找 迭代速度比标准关联容器快得多 小对象的内存消耗更少(如果使用了 shrink_to_fit,对于大对象) 提高缓存性能(数据存储在连续内存中)

一个潜在的缺点:

最坏的线性时间插入和线性时间删除

即使最坏的情况发生在插入或删除过程中(移动底层向量的元素),它仍然没有那么糟糕,这要归功于(1)缓存的良好使用,(2)底层元素的重定位可能是矢量化(矢量指令)。鉴于您的使用模式,您必须在您的应用程序中尝试它以查看插入/删除是否存在问题。

如果flat_map不适合你,我会试试std::unordered_map

【讨论】:

以上是关于C++中的稀疏数组的主要内容,如果未能解决你的问题,请参考以下文章

对数组中的字符串进行排序,使其稀疏

如何使用 C++ 将 Eigen 稀疏矩阵转换为数组?

理解JS里的稀疏数组与密集数组

稀疏数组

Python中的稀疏3d矩阵/数组?

HDF5 中的稀疏数组支持