【问题标题】:Hash 16-bit integer to a 256-bit space efficiently有效地将 16 位整数散列到 256 位空间
【发布时间】:2014-01-13 09:38:45
【问题描述】:

变得更大听起来很奇怪,但这就是我想要做的。我想获取整个 16 位整数序列,并以这样的方式散列每个整数,使其均匀映射到 256 位空间。

这样做的原因是我试图将 16 位数字空间的子集放入 256 位布隆过滤器中,以进行快速成员资格测试。

我可以在每个整数上使用一些众所周知的散列函数,但我正在寻找一种极其高效的实现(只需几条指令),以便它在 GPU 着色器程序中运行良好。我觉得散列输入已知只有 16 位这一事实可以告知散列函数是以某种方式设计的,但我看不到解决方案。

有什么想法吗?


编辑

根据回复,我最初的问题令人困惑。对于那个很抱歉。我将尝试用一个更具体的例子来重申它:

我有来自集合 S 的 n 个数字的子集 S1,它在 (0, 2^16-1) 范围内。我需要用一个用单个散列函数构造的 256 位布隆过滤器来表示这个子集 S1。布隆过滤器的原因是空间考虑。我选择了 256 位布隆过滤器,因为它符合我的空间要求,并且误报概率足够低。我正在寻找一个非常简单的散列函数,它可以从集合 S 中获取一个数字并将其表示为 256 位,以便每个位具有大致相等的 1 或 0 概率。

哈希函数要求简单的原因是这个哈希函数每个像素必须运行数千次,所以任何可以修剪指令的地方都是一个胜利。

【问题讨论】:

  • 您能否澄清一下身份功能如何不适用于您正在尝试做的事情?速度很快。
  • @PascalCuoq:通常布隆过滤器使用一组哈希来测试成员资格。在完美的情况下,OP 可以找到填充 256 位空间并且冲突次数最少的 n 哈希函数(理想情况下没有,但这在这里是不可能的)。也就是说,存在2^16 16 位整数,因此自256 < 2^16 以来,标识函数不会映射到这256 位(即1 位以将整数标记为集合中)。
  • @RageD 问题不是关于识别 n 个哈希函数,它要求一个,并且没有说明身份如何不是一个令人满意的解决方案。
  • @PascalCuoq:为了完整起见,我添加了这一点,因为他使用的是布隆过滤器。我怀疑 OP 认为 16 位整数的空间是 16 位而不是 2^16。假设我正确理解了 OP,他最多有 2^16 整数,他想将这些整数散列成 256 位 位向量。如果 OP 确实在内存中保存了 2^256 位,他应该使用第一个 2^16 位(您建议的身份函数)作为注入并将每个位视为简单的布尔值(0 或 1)。
  • @PascalCuoq:这可能不会一致。再说一次,我们没有measure,所以无论如何也没有统一的定义。简单地说:x << 240 在度量为x-y 时非常统一,但在度量为x^y 时则完全不统一。

标签: c++ algorithm hash glsl shader


【解决方案1】:

如果你在 2^31 和 2^32 之间乘以(使用uint32_t)一个 16 位值乘以素数(或任何奇数)p,那么你“可能”会相当均匀地涂抹结果32 位空间。然后您可能想要添加另一个素值,以防止 0 映射到 0(您希望每个位具有相同的概率是 01,只有一个输入值在 2^256 应该有输出全零,因为只有 2^16 个输入,这意味着您不希望它们都输出全零)。

这就是如何通过一次操作(加上加载常量所需的任何指令)将 16 位扩展到 32 位。使用四个不同的值 p1 ... p4 来获得 256 位,并使用不同的 p 值运行一些测试以找到好的值(即那些产生的误报不会比您对 Bloom 的期望值多的那些给定您正在编码的集合的大小并假设一个理想的散列函数)。例如,我很确定 -1 是一个糟糕的 p 值。

无论值多么好,您都会看到一些相关性:例如,正如我在上面所描述的,所有 4 个单独值的最低位将相等,这是一个非常严重的依赖关系。所以你可能想要更多的“混合”操作。例如,您可能会说最终输出的每个字节应该是我所描述的两个字节的 XOR(而不是两个最不重要的字节!),只是为了摆脱简单的算术关系。

除非我误解了这个问题,否则布隆过滤器通常不是这样工作的。通常你希望你的散列为每个输入产生一个精确固定数量的设置位,并且计算误报率的所有算法都依赖于此。这就是为什么对于大小为 256 位的 Bloom 过滤器,您通常会有 k 8 位哈希,而不是一个 256 位哈希。 k 通常小于过滤器大小的一半(以比特为单位)(最佳值是过滤器中每个值的比特数乘以 ln(2),约为 0.7)。所以通常你希望每个位为 1 的概率高达 0.5。

原因是,一旦您将少至 4 个这样的 256 位值组合在一起,过滤器中的几乎所有位都会被设置(其中 16 位中有 15 位)。所以你已经看到了很多误报。

但是,如果您已经完成了数学计算,并且您对单个哈希函数感到满意,该函数产生了可变数量的集合位,平均其中一半,那么就足够公平了。或者数字 256 的重复出现只是一个巧合,因为 k 对于您选择的设置大小恰好是 32,而您实际上将 256 位散列用作 32 个 8 位散列?

[编辑:您的评论澄清了这一点,但无论如何k 不应该太高以至于您总共需要 256 位哈希。显然,在这种情况下,使用每个值超过 16 位(即少于 16 个值)的 Bloom 过滤器是没有意义的,因为使用相同数量的空间,您可以只列出值,并且误报率为 0。每个值 16 位的过滤器会给出 2200 分之 1 的误报率。即使在那里,最佳 k 也只有 23,也就是说,您应该在过滤器中为集合中的每个值设置 23 位。如果您希望集合大于 16 个值,那么您希望为每个元素设置更少的位,并且您将获得更高的误报率。]

【讨论】:

  • 感谢您的回复。之前没有实现过布隆过滤器,我低估了多个哈希函数的重要性。你给了我很多东西要看,但考虑到有缺陷的问题,我认为你回答得最好。接受
【解决方案2】:

我认为提出的问题存在一些混淆。我将首先尝试消除我在上面注意到的任何不一致之处。

OP 最初声明他正在尝试将较小的空间映射到较大的空间。如果确实如此,那么就没有必要使用布隆过滤器算法了。相反,正如上面 cmets 中所建议的,恒等函数是设置和测试每个位所必需的唯一“散列”函数。但是,我断言这并不是 OP 真正想要的。如果是这样,那么 OP 必须将2^256 位存储在内存中(基于问题的陈述方式),以使 16 位整数的空间(即2^16)小于他的设置大小;这是一个不合理的内存使用量,而且不太可能出现这种情况。

因此,我假设问题约束如下:我们有一个 256 位的位向量,我们要在其中映射 16 位整数的空间。也就是说,我们有 256 位可用于映射 2^16 可能的不同整数。因此,我们实际上并没有映射到一个更大的空间,而是一个更小的空间。同样,它确实出现(再次,如前面在上面的 cmets 中指出的那样)OP 正在请求单个哈希函数。如果是这种情况,则对布隆过滤器的工作方式存在明显的误解。

布隆过滤器通常使用一组独立于哈希的哈希函数来减少误报。无需过多介绍,布隆过滤器的每个输入都将运行所有n 哈希函数,然后针对每个函数测试位向量中的结果索引。如果测试的所有索引都设置为1,则值可能在集合中(所有n哈希函数中的适当冲突或重叠,都会发生误报)。此外,如果任何索引设置为0,则该值在集合中绝对不是。考虑到这一点,重要的是要注意完全饱和的布隆过滤器没有任何好处。也就是说,对布隆过滤器的每个查询都将返回该项目在集合中。

哈希函数问题

现在,回到 OP 的原始问题。最好使用已知的散列算法(因为这些算法在数学上很难编写,并且“滚动你自己的”通常不会很好地结束)。如果您担心时钟周期的效率,请使用适合您架构的汇编语言自己实现算法,以减少每个散列函数的运行时间。请记住,从算法上讲,哈希函数应该在O(1) 时间运行,因此如果实施得当,它们不应该贡献太多开销。首先,我建议您考虑修改后的 bernstein 哈希。我在下面为您的具体情况编写了一个版本(主要用于示例目的):

unsigned char modified_bernstein(short key)
{
  unsigned ret = key & 0xff;
  ret = 33 *  ret ^ (key >> 8);
  return ret % 256; // Try to do some modulo math to keep it in range
}

我采用的 bernstein 方法通常作为输入字节数的函数运行。由于short 类型是2 字节或16-bits,我已经从算法中删除了所有变量和循环,并简单地进行了一些操作以获取每个字节。最后,unsigned char 可以返回 [0,256) 范围内的值,这会强制哈希函数返回位向量中的有效索引。

【讨论】:

  • 感谢您抽出宝贵时间回复。我不认为这回答了我想问的问题,我已经更新了我原来的问题,使其更加清晰。
  • @amoffat:我看到了你的编辑。任何“好的”散列函数都应该均匀地分配其输出(注意:SUHA 原则)。这就是为什么我给你留下了一个简单/已知算法的实现。更重要的是,你为什么要受限于单个散列函数?当您这样做Bloom filters 时,您将失去布隆过滤器的大部分数学优势。如果您更喜欢学术文献而不是非正式的维基百科参考,我可以链接这些论文。此外,最后,您可能会使布隆过滤器饱和。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-12
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 1970-01-01
  • 2019-09-12
相关资源
最近更新 更多