【问题标题】:fast rule out if a member is in a set快速排除成员是否在集合中
【发布时间】:2014-03-10 14:32:46
【问题描述】:

我有一小组数字,我需要经常搜索。

该组是静态的并且在开始时是已知的。

我从观察中知道,大多数时候我正在搜索的数字不在组中。

我正在寻找的是一种算法,只需一两条指令即可:

  1. 永远不要说某个号码不在组中,而是在组中
  2. 该算法大部分或所有时间都预测是否是

例如,

如果数字是 x,y,z 我可以执行以下操作:

保存:

tmp = (x | y | z)

当我搜索一个我可以做的数字时:

if ((num & tmp) == (num))
    do the real search

如果数字是 x、y 或 z,则保证在与它进行 AND 时返回 num。 如果不是,我可能什么也不搜索 - 但基本上没问题。

这个测试的主要问题是大多数时候对于组中超过 5 个数字,即使 num 不在组中,我也会得到 TRUE。

我正在考虑使用 XOR 魔法:

tmp = (x ^ y ^ z)

并且在搜索时执行以下操作:

(num ^ tmp)

但我不明白这如何帮助我确定元素是否在组中。

有什么想法吗?

谢谢,

伊泰

更新

我发现有用的是使用非常简单的布隆过滤器:

我已将 x、y 和 z 散列到一个位数组(例如 8 位)。 然后,我将结果转移到正确的位:

uint8_t digest = (1 << (x % 8)) | (1 << (y % 8)) | (1 << (z % 8))

以及我使用过的搜索功能:

if ( (1 << (num % 8)) & digest )

我使用随机数进行了一些分析,发现使用 8 位在大约 30% 的时间给了我错误的指示。 使用 16 位让它变得更好。

【问题讨论】:

  • 您应该提供一个或多个您需要代表的组的示例,因为这可以帮助我们找到有趣的模式。
  • 你为什么不使用布尔数组查找?如果数字范围太大,可能会使用一些原始散列。
  • 您是否考虑过使用例如qsort 对列表进行排序,然后使用bsearch 进行搜索?您的按位算术是一个很酷的想法,但是随着您的数字列表的增长,将它们组合在一起将提供所有 1 的可能性越来越大,而且我看不到对它们进行异或运算将如何为您提供所需的结果。
  • 我没有对它们进行排序的原因是该组非常小(最多 7 个数字)并且例程对性能非常敏感。这就是为什么我试图找到最快的排除检查。
  • @rcgldr 分配 528 兆字节的零,整个过程中只设置了 7 位,这是通过缓存抖动降低性能的好方法,并且可能是最慢的方法。

标签: c++ c performance algorithm


【解决方案1】:

只有七个数字,你应该在你的集合中进行暴力搜索;它会比任何其他方法都快。如果您的值是 16 位或更少,则可以在单个 SIMD 相等测试中进行;如果它们是 32 位,则可以分成两部分。

【讨论】:

  • 对集合进行排序仍然会产生边缘结果,因为一旦找到大于搜索词的数字,您就可以退出搜索。
  • @DanFarrell 提前退出很可能会因为分支错误预测停顿而降低性能。
  • @ItayMarom 大多数与高效做事相关的好想法都依赖于平台。
  • 实际上,您的第一个答案(您已删除的那个)帮助我找到了解决方案:一个简单的布隆过滤器。 billmill.org/bloomfilter-tutorial
  • 我实际上认为布隆过滤器在这里不是一个好主意。由于集合中的元素数量很少,评估哈希函数可能比简单的搜索更昂贵。
【解决方案2】:

使用一个简单的散列函数将您的域映射到一个恒定范围(比如a.x mod b 或类似的)和一个b 布尔值数组。如果您足够幸运/足够小心,您最终可能不会遇到哈希冲突和精确测试。

【讨论】:

    【解决方案3】:

    数据结构

    设 min 为目标集的最小值。 让 max 成为目标集的最大值。

    设 bset 为位向量,大小为保持 max - min 所需的总位数。

    对于目标集中的每个数字 x,令 n = x - min。设置集合中的第 n 位。

    谓词

    对于给定的数字 x,令 n = x - min,如果设置了第 n 位,则它是一个成员。

    每个测试的成本应该是,1 次减法,2 次位移,一次数组访问和一次逻辑与。如果您的位向量由一组 32 位或 64 位值表示,则您可以通过在屏蔽位之前先测试该值是否非零,从概率上减少更多。

    内存成本取决于最大值和最小值之间的差异。

    根据输入集的大小,我也倾向于为您拥有的数字编译一个比较器。如果您即时编写二进制搜索,您会看到最坏情况下的 log2(n) 比较,它与最多约 32 个数字的二进制向量相当。

    【讨论】:

      【解决方案4】:

      如果“静态和开始时间”表示编译时,您可以使用开关。

      bool inGroup(int i) {
      switch(i) {
      case X1: return true;
      case X2: return true;
      // ...
      case X7: return true;
      default: return false;
      }
      

      如果数字没有在整数范围内广泛分布,这将导致跳转表。如果数字范围分布广泛,您可以计算最佳哈希函数,例如使用 gperf。

      如果数字不是编译时常量,您应该考虑将它们存储在一个数组中,对其进行排序并使用二分查找。排序只需要进行一次,搜索将需要最大。三个比较。

      【讨论】:

        猜你喜欢
        • 2012-03-10
        • 1970-01-01
        • 2014-02-10
        • 1970-01-01
        • 2010-09-13
        • 1970-01-01
        • 2018-10-04
        • 1970-01-01
        • 2014-09-24
        相关资源
        最近更新 更多