给定一系列索引(标识符),我想将每个索引映射到一个布尔值,即:
// interface pseudocode
interface bitmap {
bool identifier_is_set(unsigned int id_idx) const;
void set_identifier(unsigned int id_idx, bool val) const;
};
这样我可以设置和查询每个ID(索引)是否已设置,您希望用什么来实现它?
我认为这称为位数组或位图或bitset,如果我错了,请纠正我。
假设最大标识符是预定的并且不大于1e6(1m),可能小得多(10k - 100k)。(这意味着sizeof(int)* maximum_id_idx使用的大小很容易适合进入记忆。)
到目前为止我看到的可能的解决方案:
std::set<size_t>
- 根据需要将标识符添加或删除。只要我们有稀疏位图,这将允许任意大的标识符。std::vector<bool>
- 调整为适当的最大值,为每个id_idx存储true或false。std::vector<char>
- 同样的事情,但没有遭遇奇怪的std::vector<bool>
问题。使用的内存少于vector<int>
。std::vector<int>
- 使用int
作为布尔标志,使用机器的自然字大小来容纳容器。 (如果可以产生影响,则不知道。)请根据上面引用的最大ID限制,特别是考虑查询位图的性能方面,回答您更喜欢的容器类型及其原因(插入性能无关紧要) )。
注意:vector
与set
的界面使用无关紧要,因为无论如何它都会隐藏在它的包装类后面。
答案 0 :(得分:3)
在不知道运行此代码的平台和您的访问模式的情况下,很难说vector<bool>
是否会快于vector<char>
(或vector<int>
)甚至{{1 }或set<int>
。
例如,如果您有一个非常稀疏的数组,那么仅包含索引集的unordered_set<int>
的线性搜索可能是最佳答案。 (See Mike Abrash's article on optimizing Pixomatic for x86.)
另一方面,您可能有一个有点稀疏的数组。有点稀疏,我的意思是设置元素的数量远大于L1或L2。在这种情况下,更多低级细节开始发挥作用,以及您的实际访问模式。
例如,在某些平台上,可变位移动非常昂贵。因此,如果您要查询一组随机标识符,则执行此操作的频率越高,vector<int>
或vector<char>
变得比vector<int>
或bitset<...>
更好。 (后两者使用位移位来查找位。)另一方面,如果按顺序迭代稀疏位向量并且只想设置位,则可以优化该迭代以消除变量移位的开销。
此时,您可能还想知道稀疏标识符的实际分布情况。如果它们聚集在一起,您需要知道最佳内存读取大小和一次读取字符之间的权衡。这将决定是否更频繁地访问缓存将抵消非原生大小数据的读取。
如果标识符分散,您可以通过使用哈希集(vector<bool>
)而不是位向量来获得显着的胜利。然而,这取决于负载。
答案 1 :(得分:2)
你检查过boost :: dynamic_bitset吗?
http://www.boost.org/doc/libs/1_36_0/libs/dynamic_bitset/dynamic_bitset.html
答案 2 :(得分:2)
假设最大标识符是预先确定的且不大于1e6(1m)
如果您有硬限制,请使用std::bitset
:
std::bitset<1000000> bits;
bits.set(1000);
答案 3 :(得分:1)
如果按性能表示最快查找的那个,那么std :: bitset可能足够快,因为它的查找是常量时间。将所有位设置为零有一个初始开销。矢量&lt;诠释&GT;可能会明显更快,并且设置比特会有更大的开销,因为在32位系统中它们的数量是32倍。
矢量&lt; BOOL&GT;就像你的实现中的bitset一样,如果你需要的话,它具有可调整大小的优点,虽然通常我会避免使用vector,如果我需要调整大小,可以使用boost的dynamic_bitset。
在查找和插入/删除中,std :: set将是O(log N),尽管它在内存使用中是最具可扩展性的,如果该集合不是特别满,则占用较少。 std :: set不受范围限制。
如果您的数据更稀疏,某种形式的散列也是一种选择,通常是O(1)设置和查找,尽管可能存在一些碰撞处理的开销。
答案 4 :(得分:0)
最快的似乎是使用位掩码。你应该构造一个std::vector<int>
,并使其大小足够(N除以sizeof(int)* 8,向上舍入)。
对于大型数据集,这似乎比std::vector<bool>
(或类似)更快。因为实际上使用的内存要少得多,因此缓存利用率更高
答案 5 :(得分:0)
你总是可以拥有一个std::vector<std::bitset<sizeof(size_t)> >
,然后你的查找就是简单的计算(虽然模运算相对较慢),但你有这个优势可以增长...我会冒这个空间明智的,以上可能也是最优的...