原始数据可以描述为固定数量的列(大约几千)和大(大约数十亿)和可变行数。每个细胞都有点。所需的查询类似于查找设置了位12,329,2912,3020的所有行。像
这样的东西for (i=0;i< max_ents;i++)
if (entry[i].data & mask == mask)
add_result(i);
在典型的情况下,在任何特定行中没有设置很多(例如5%)比特,但是不能保证,存在一定程度的可变性。
在更高级别上,数据描述条目的按位指纹,数据本身是一种搜索索引,因此需要最大速度。什么算法对这种搜索有好处?目前我正在考虑分别为每列提供单独的稀疏(压缩/压缩)位向量。我怀疑它是最佳的。
答案 0 :(得分:2)
这看起来类似于“文本搜索”,特别是相交的反向索引。让我通过最简单的算法来做到这一点。
首先,您应该创建每个位设置的有序数字列表。例如,对于数字表:
第1行 - &gt; 10110
第2行 - &gt; 00111
第3行 - &gt; 11110
第4行 - &gt; 00011
第5行 - &gt; 01010
第6行 - &gt; 10101
你可以创建一个反向索引:
位0设置在 - &gt; 2,4,6
位1设置为 - &gt; 1,2,3,4,5
位2设置在 - &gt; 1,2,3,6
等
现在,对于查询(比如位0和1&amp; 2),您只需使用合并排序(如算法)合并这些排序列表。要做到这一点,你可以先合并列表0,1,给你{2,4},然后将它与列表2合并,给你{2}。
可能有几种优化,包括但不限于压缩这些列表,因为连续项之间的差异通常很小,可以更有效地进行合并等。
但是,为了节省更多麻烦,为什么不重用其他人已经完成的工作呢? ;)...您可以随时使用(应该可以在不到1天的编码时间)任何开源文本搜索引擎(我建议Lucene)来执行此任务,它应该包含一些人们拥有的优化建立了很长时间;)。 (提示:您应该在文本搜索用语中将每一行视为“doc”,并将每一位视为“令牌”)。
编辑(根据问题作者的要求添加一些算法):
a)压缩:您可以做的最有效的事情之一是压缩帖子列表(与每个位置对应的排序列表)。大多数算法通常采用连续项的差异,然后根据某些编码(Gamma Coding,Varint Encoding)对其进行压缩,仅举几例。这会压缩反转列表,以便它消耗更少的文件空间(因此减少文件I / O),或者使用更少的内存来编码同一组数字。在您的情况下,我可以估计每个发布列表将包含~5%* 1e9 = 5e7元素。如果它们在0-1e9之间均匀分布,那么间隙应该在20左右,所以让我们说每个间隙的编码平均需要大约8b(这是一个很大的高估),加起来高达500MB。因此,对于1000个列表,您将需要500GB的空间,这肯定需要磁盘空间。这反过来意味着您应该尽可能地使用压缩算法,因为更好的压缩意味着更少的文件I / O并且您将受到I / O限制。
b)交叉顺序:您应该始终从最小的开始交叉列表,因为这可以保证创建最小的中间列表,这意味着稍后通过下面显示的技术进行较少的比较。
c)合并算法:由于您的索引几乎肯定会溢出到磁盘,因此在算法级别上可能无法做到。但是使用的一些想法是使用基于二进制搜索的过程来合并两个列表而不是简单的线性合并过程,以防其中一个列表比另一个小得多(这将导致O(N*log(M))
复杂性而不是O(N+M)
M >> N
)。但对于基于文件的索引,这几乎不是一个好主意,因为二进制搜索会进行许多随机访问,这会完全搞砸你的磁盘延迟,而线性合并过程则是严格顺序的。
d) Skip Lists :这是另一个很棒的数据结构,用于存储已排序的帖子列表,这些列表也可以支持之前提到的高效“二进制搜索”。这里的关键思想是跳过列表的上层可以保存在内存中,这可以大大加快交叉算法的最后阶段,当你只需搜索内存中的上层来获取磁盘时偏移,然后从那里进行磁盘访问。有一点,基于二元搜索+基于跳过列表的合并变得比线性合并更有效,并且可以通过实验找到。
e)缓存:不用脑子。如果您的某些术语经常出现,请将它们缓存在内存中,以便将来更有效地使用它们。注意,高速缓存也可以是,例如,一个更快的基于闪存的磁盘,它可以提供更好的吞吐量,并可能缓存大量更频繁的术语(32GB内存只能容纳约64个这些列表,而256GB闪存盘可以容纳~512)。 / p>