【问题标题】:Efficient Function to Map (or Hash) Integers and Integer Ranges into Index将整数和整数范围映射(或散列)到索引的高效函数
【发布时间】:2013-08-14 03:11:10
【问题描述】:

我们正在寻找计算上最简单的函数,该函数将使函数的索引查找能够由广泛分布的整数和整数范围的高频输入流确定。

如果哈希/映射函数选择本身根据特定的整数和范围要求而变化,则可以,并且与选择此算法的代码部分相关的性能并不重要。在大多数情况下,感兴趣的整数/范围的数量会很小(零到几千)。性能关键部分是处理传入的流并选择适当的函数。

作为一个简单的例子,请考虑以下伪代码:

switch (highFrequencyIntegerStream)
    case(2)      : func1();
    case(3)      : func2();
    case(8)      : func3();
    case(33-122) : func4();

    ...

    case(10,000) : func40();

在典型示例中,上面显示的“案例”只有几千个,其中可能包括完整的 32 位整数值和范围。 (在上面的伪代码中,33-122 代表了从 33 到 122 的所有整数。)会有大量的对象包含这些“switch 语句”。

(请注意,实际的实现不会包括 switch 语句。它将是一个跳转表(它是一个函数指针数组)或者可能是 Command 和 Observer 模式的组合等。实现细节与请求,但提供以帮助可视化。)

许多对象将包含只有几个条目的“switch 语句”。感兴趣的值会受到实时变化的影响,但与管理这些变化相关的性能并不重要。哈希/映射算法可以根据特定的整数和感兴趣的范围(对于给定时间的给定对象)在每次更新时缓慢地重新生成。

我们在互联网上进行了搜索,查看了 Bloom 过滤器、维基百科的“散列函数”页面和其他地方列出的各种散列函数、相当多的堆栈溢出问题、抽象代数(主要是伽罗瓦理论,其计算简单的操作数很有吸引力)、各种密码等,但尚未找到似乎针对此问题的解决方案。 (我们甚至找不到将这些类型的范围作为输入的哈希或映射函数,更不用说高效的了。也许我们没有在正确的地方寻找或使用正确的白话。)

目前的计划是创建一个自定义算法,预处理感兴趣的整数和范围列表(在给定时间针对给定对象),寻找可应用于输入流的移位和掩码,以帮助描绘范围。请注意,大多数传入的整数将是无趣的,并且对尽可能大比例的流的那部分做出非常快速的决定至关重要(这就是为什么布隆过滤器一开始看起来很有趣(在我们开始之前)认为他们的实现比其他解决方案需要更多的计算复杂性))。

因为第一个决定非常重要,我们也在考虑拥有多个表,其中第一个是反向掩码(用于选择不感兴趣的数字的掩码),以便轻松找到给定“开关”中未包含的大范围数据声明”,后面是扩展较小范围的后续表格。我们认为,对于大多数输入流情况,这将比在范围边界上进行二分搜索快得多。

注意输入流可以认为是随机分布的。

【问题讨论】:

    标签: hash map integer range


    【解决方案1】:

    我认为有一个非常广泛的最小完美哈希函数理论可以满足您的要求。最小完美哈希的想法是一组不同的输入以 1-1 的方式映射到一组密集的整数。在您的情况下,一组 N 个 32 位整数和范围将分别映射到一个大小为 N 的小倍数的唯一整数。Gnu 有一个完美的哈希函数生成器,称为 gperf,它适用于字符串,但可能可能处理您的数据。我一定会试一试的。只需添加一个长度字节,使整数为 5 字节字符串,范围为 9 字节。 the Wikipedia page 上有一些正式的参考资料。 ACM 和 IEEE 文献中的文献搜索肯定会出现更多。

    我刚刚遇到了this library,我以前没见过。

    加法

    我现在看到您正在尝试将范围内的所有整数映射到相同的函数值。正如我在评论中所说,这与散列不太兼容,因为散列函数故意尝试“擦除”位位置的幅度信息,以便具有相似幅度的值不太可能映射到相同的散列值。

    因此,我认为您不会比最佳二叉搜索树做得更好,或者等效地是生成“if else”语句的最佳“树”的代码生成器。

    如果我们想要构建您所要求的类型的函数,我们可以尝试使用实数,其中单个域值映射到共同域中的连续整数,范围映射到共同域中的单位间隔。因此,一个简单的地板操作将为您提供您正在寻找的跳跃表索引。

    在您提供的示例中,您将拥有以下映射:

    2 -> 0.0
    3 -> 1.0
    8 -> 2.0
    33 -> 3.0
    122 -> 3.99999
    ...
    10000 -> 42.0 (for example)
    

    诀窍是找到对这些点进行插值的单调递增多项式。这当然是可能的,但我敢肯定,由于有数千分,您最终会得到比最佳搜索慢得多的评估结果。

    【讨论】:

    • 诱使 gperf 认为我们的数据是字符串是一个有趣的想法。我们也在按照您的建议调查 CMPH。我们搜索了 IEEE 和 ACM,但没有找到任何涉及范围的内容。粗略浏览一下 gperf 和 CMPH 似乎也表明它们不处理范围。例如,如果要使用 gperf 字符串中范围的边界,散列函数将不知道选择该范围内的某些内容并将其映射到正确的索引。例如,在我们上面的伪代码中,gperf 如何知道将输入 100 映射到与 func4 关联的索引?
    • 对不起,我误会了。从字节流中提取思想范围。现在我明白你的意思了。让我考虑一下。
    • 我们昨晚花了一些时间按照建议使用 gperf。请注意,如果我们要将它用于上面提出的解决方案中的第二个表,(在屏蔽大范围(和反向范围)并扩展小范围之后),我们将必须包含 gperf 和和编译器作为我们的释放。因此,CMPH 可能是更好的选择。话虽如此,我们仍然希望找到一种将范围作为算法不可或缺的一部分的好方法。
    • 我应该提到执行此操作的经典方法是使用标记的平衡二叉树来表示区间限制。是不是太慢了?您可以确定不超过 1+log P 个节点需要遍历,其中 P 是目标范围值的数量(将单个值视为 1 元素范围)。因此,对于您有大约 2000 个间隔的人来说,如果仔细编码,这可能是 12 个,每个级别大约 5 条指令。因此,如果树适合 L1 缓存,则使用 x86 类型的处理器每次查找可能需要 100ns。
    • 我们确实考虑了边界上的二分搜索(如原始问题的最后一段中所暗示的那样),并且理解这非常快。感觉必须有一种方法可以使用简单的操作数在类似哈希的算法中处理整数范围。我们还期望掩蔽方法优于二叉树方法。顺便说一句,这里有一个 link 用于一些简单的哈希函数,其他搜索此类问题的人可能会发现这些函数有帮助。
    【解决方案2】:

    也许我们的thoughts on hashing integers 可以提供一点帮助。您还将找到一个基于 Bob Jenkins 工作的散列库 (hashlib.zip),它以一种智能的方式处理整数。 我建议在单个案例被散列机制拒绝后处理更大的范围。

    【讨论】:

    • 请注意 link-only answers 是不鼓励的,所以答案应该是寻找解决方案的终点(与另一个中途停留的参考相比,随着时间的推移往往会变得陈旧)。请考虑在此处添加独立的概要,并保留链接作为参考。
    • 您可能错过了我的陈述“我建议在单个案例被散列机制拒绝后处理更大的范围”。这确实可以成为一个解决方案。另一方面,我认为简单地重复引用文档的内容是没有用的。
    猜你喜欢
    • 2013-04-29
    • 1970-01-01
    • 2020-03-03
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 2018-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多