我有一组uint32
个整数,集合中可能有数百万个项目。其中50-70%是连续的,但在输入流中它们以不可预测的顺序出现。
我需要:
将此集合压缩到范围内以实现节省空间的表示。已经使用普通算法实现了这一点,因为只计算一次速度的范围在这里并不重要。在此转换之后,结果范围的数量通常在5 000-10 000之间,当然,其中许多是单项。
测试某个整数的成员资格,不需要有关该集合中特定范围的信息。这个必须非常快 - O(1)。正在考虑minimal perfect hash functions,但它们与范围不一致。 Bitsets空间效率很低。其他结构,如二叉树,具有O(log n)的复杂性,最糟糕的是它们实现了许多条件跳转,而处理器无法很好地预测它们,从而导致性能不佳。
是否有专门用于整数范围的数据结构或算法来解决此任务?
答案 0 :(得分:10)
关于第二个问题:
你可以查看Bloom Filters。 Bloom过滤器专门用于回答O(1)中的成员资格问题,尽管响应是no
或maybe
(不像是/否那样明确:p)。
当然,在maybe
的情况下,您需要进一步处理以实际回答问题(除非您的情况下概率答案已足够),但即使如此,布隆过滤器也可以充当守门员,并且完全拒绝大多数查询。
此外,您可能希望在不同结构中保留实际范围和退化范围(单个元素)。
这减少了排序数组中存储的元素数量,从而减少了在那里执行的二进制搜索的复杂性。既然你声明许多范围是退化的,我认为你只有500-1000个范围(即,一个数量级减少),而log(1000)~10
因此,我建议采取以下步骤:
首先执行排序数组测试,因为如果包含一个数字,你给出的数字(数千个范围内合并的数百万个数字),很可能它会在一个范围而不是单个:)
最后一个注意事项:提防O(1),虽然它看起来很吸引人,但你不是在渐近的情况下。只有5000-10000的范围很少,因为log(10000)就像13那样。所以不要通过获得具有如此高的常数因子的O(1)解决方案来使你的实现失望,它实际上比O(log N)运行得慢。 )解决方案:)
答案 1 :(得分:6)
如果您事先知道范围是什么,那么您可以使用下面概述的策略检查给定整数是否存在于O(lg n)中的一个范围内。它不是O(1),但在实践中它仍然很快。
这种方法背后的想法是,如果你将所有范围合并在一起,你就会在数字线上有一系列不相交的范围。从那里,您可以通过说区间[a,b]≤[c,d] iffb≤c来定义这些区间的排序。这是一个总排序,因为所有范围都是不相交的。因此,您可以将所有间隔放在一起形成静态数组,然后按此顺序对它们进行排序。这意味着最左边的间隔位于数组的第一个插槽中,最右边的间隔位于最右边的插槽中。这种结构需要O(n lg n)时间。
要检查某个时间间隔是否包含给定的整数,您可以对此数组执行二进制搜索。从中间间隔开始,检查该间隔中是否包含整数。如果是这样,你就完成了。否则,如果该值小于范围中的最小值,则继续左侧的搜索,如果该值大于该范围中的最大值,则继续右侧的搜索。这本质上是一个标准的二进制搜索,它应该在O(lg n)时间内运行。
希望这有帮助!
答案 2 :(得分:2)
AFAIK没有这样的算法可以搜索O(1)中的整数列表。
只能用大量内存进行O(1)搜索。
因此尝试在整数范围列表上找到O(1)搜索算法并不是很有希望。
另一方面,您可以通过仔细检查数据集(最终构建一种哈希表)来尝试时间/内存权衡方法。
答案 3 :(得分:2)
您可以使用y-fast树或van Emde Boas树来实现O(lg w)时间查询,其中w是单词中的位数,您可以使用融合树来实现O(lg_w n)时间查询。用n表示的最优权衡是O(sqrt(lg(n)))。
最容易实现的可能是快速树。它们可能比进行二进制搜索更快,尽管它们需要大约O(lg w)= O(lg 32)= O(5)哈希表查询,而二进制搜索大致需要O(lg n)= O(lg 10000)= O(13)比较,因此二进制搜索可能更快。
答案 4 :(得分:1)
而不是基于'比较'的存储/检索(总是O(log(n))), 您需要处理基于'radix'的存储/检索。
换句话说..从uint32中提取半字节,并制作一个trie ..
答案 5 :(得分:1)
将您的范围保持为已排序的数组,并使用二进制搜索进行查找。
它很容易实现,O(log N),并且使用的内存更少,并且需要的内存访问量比任何其他基于树的方法都少,所以它可能也会快得多。
答案 6 :(得分:1)
从您对问题的描述中,听起来以下可能是一个很好的妥协。我使用面向对象语言描述它,但可以使用带有类型成员和指针的联合类型或结构轻松转换为C.
使用前16位索引对象数组(大小为65536)。在该数组中有5个可能的对象
当然,您不需要以16位分割,您可以调整以反映您的设置的统计数据。事实上,你不需要使用连续的位,但是它会加速钻头的速度,并且如果你声称的许多元素是连续的,那么它将提供良好的属性。
希望这是有道理的,如果我需要更充分地解释,请发表评论。实际上,您已经将深度2二叉树与范围和位集相结合,以进行时间/速度权衡。如果你需要节省内存,那么使树更深,查找时间略有增加。