半可逆整数散列(请保持开放态度)

时间:2013-09-10 02:30:18

标签: algorithm math hash integer inverse

我需要探索特定应用程序的整数哈希主题。我有一些要求:

  • 整数到整数哈希
  • “半”可逆性。我知道哈希不会是1-1可逆的,所以请尝试理解我在n-1哈希方面的想法。假设我有一个数字0 ... n的原始域,我将其哈希到一个较小的域0 ... k。如果我使用函数f(n)= k进行哈希,那么我想要一些可逆的东西,我也有一个“逆”g(k)= {n1,n2,n3,...,nj}都是可能的散列到k的域成员
  • 合理均匀和“随机”分布
  • 对于我的“逆”函数g,我对返回的集合的大小有一个紧密的约束,并且对于任何给定的k,这个大小大致相同
  • 快速整数哈希

在这里解释一下应用程序......我在一个非常受内存限制的环境中运行。我打算不允许碰撞。也就是说,如果与表中的现有值发生冲突,则插入操作将失败。没关系。我不需要每个插件都能成功。我准备做出有利于空间和速度的交易。现在关键是这一点,当我将值存储在表中时,我需要绝对最小化所表示的位数。我希望的基本上是:

如果我哈希值k,我可以立即缩小我存储到原始域的一小部分。如果散列是“半可逆的”并且如果我可以枚举所有可能的域元素散列到k,那么我可以对它们进行排序并为每种可能性分配序数。然后我想存储那么小的序数而不是原始值,这将需要更少的位。然后我应该能够通过枚举存储序数i的第i种可能性来完全逆转这一点。

对逆集g(k)大小的严格限制的重要性是因为我需要知道每个序数需要分配多少位,我想通过分配相同的数字来保持相对简单每个表条目的位数。是。我可能会在小于一个字节的值上工作。原始域的大小相对较小。

我对你的任何想法和任何人可能参考的例子感兴趣。我认为这应该是可行的,但我想了解一系列可能的解决方案。

提前致谢! 标记

2 个答案:

答案 0 :(得分:2)

随意播放

0..(n-1)域中应用一些双射以稍微改变一下。如果n是素数,这将特别容易,因为在这种情况下,您可以将模运算视为一个字段,并执行各种不错的数学函数。可以根据您的需要均匀分配数字的一件事可能是乘以固定数字c,然后是模数:

a ↦ (c*a) mod n

您必须选择c,使其成为n的互质,即gcd(c,n)=1。如果n是素数,那么只要c≠0这是微不足道的,如果n是2的幂,那么任何奇数都可以满足要求。这种互易性条件确保存在另一个d c c*d ≡ 1 (mod n) d,即它满足c,因此乘以n将撤消效果乘以n。你可以例如在Java或inverse中使用BigInteger.modInverse来计算此数字。

如果你的c是2的幂,那么你可以避免模运算(以及需要的时间),而是做简单的位掩码操作。但即使对于d的其他值,您有时也可以提出避免泛型除法运算的方案。当您选择c(以及d时,您可以这样做,(25*a)&1023// n = 1024 // c = 25 = 16+8+1 // d = 41 = 32+8+1 static unsigned shuffle(unsigned a) { return (a + (a << 3) + (a << 4)) & 1023; } static unsigned unshuffle(unsigned a) { return (a + (a << 3) + (a << 5)) & 1023; } 只有很少的非零位。然后乘法可能用位移和加法表示。您的优化编译器应该为您处理,只要您确保这些数字是编译时常量。

这是一个使这种优化明确的例子。请注意,以这种方式编写代码不是必要的:通常写入n之类的内容就足够了。

0..(n-1)

另一种适用于0..(k-1)是2的幂的情况的混洗方法是使用位移,掩码和xors的某些组合来修改该值。这可以与上面的乘法方法相结合,或者在乘法之前或之后进行比特,或者甚至两者。做出选择在很大程度上取决于价值的实际分配。

拆分并存储

仍然在lo范围内的结果值可以分为两个值:一个部分位于0..(ceil(n/k)-1)范围内,将被称为hi,另一个位于lo = a mod k hi = floor(a/k) 范围k,我称之为lo

hi

如果hi是2的幂,则可以使用位掩码获取lo,使用位移获得hi。然后,您可以使用lo来表示哈希桶,并使用lo来表示要存储在该桶中的值。具有相同k值的所有值都会发生冲突,但它们的k部分将有助于检索实际存储的值。

如果要识别哈希映射的未占用插槽,则应确保在每个插槽中为此目的保留一个特定的hi值(例如零)。如果您无法在原始值集中实现此预留,则可能需要选择lo作为2减1的幂,以便您可以存储n自身的值以表示空单元格。或者您可以交换hilo的含义,这样您就可以调整a=k*hi+lo的值以省略一些值。我将在下面的示例中使用它。

反演

要反转这一切,您可以使用密钥0..(n-1)和存储的值n=4032,将它们合并到k=64范围内的值n/k=63,然后撤消最初改组以恢复原来的价值。

实施例

这个例子适用于避免所有乘法和除法。它在c=577个广告位上分配d=1153个值,其中unsigned char bitseq[50] = { 0 }; int store(unsigned a) { unsigned b, lo, hi, bitpos, byteno, cur; assert(a < 4032); // a has range 0 .. 0xfbf // shuffle b = (a << 9) + (a << 6) + a + 64; // range 0x40 ..0x237dbf b = (b & 0xfff) + ((b & 0xfff000) >> 6); // range 0x40 .. 0x9d7f b = (b & 0xfff) + ((b & 0xfff000) >> 6); // range 0x40 .. 0x11ff b = (b & 0xfff) + ((b & 0xfff000) >> 6); // range 0x40 .. 0xfff b -= 64; // range 0x00 .. 0xfbf // split lo = b & 63; // range 0x00 .. 0x3f hi = b >> 6; // range 0x00 .. 0x3e // access bit sequence bitpos = (lo << 2) + (lo << 1); // range 0x00 .. 0x17a byteno = (bitpos >> 3); // range 0x00 .. 0x30 bitpos &= 7; // range 0x00 .. 0x7 cur = (((bitseq[byteno + 1] << 8) | bitseq[byteno]) >> bitpos) & 0xff; if (cur != 0) return 1; // slot already occupied. cur = hi + 1; // range 0x01 .. 0x3f means occupied bitseq[byteno] |= (cur << bitpos) & 0xff; bitseq[byteno + 1] |= ((cur << bitpos) & 0xff00) >> 8; return 0; // slot was free, value stored } void list_all() { unsigned b, lo, hi, bitpos, byteno, cur; for (lo = 0; lo != 64; ++lo) { // access bit sequence bitpos = (lo << 2) + (lo << 1); byteno = (bitpos >> 3); bitpos &= 7; cur = (((bitseq[byteno + 1] << 8) | bitseq[byteno]) >> bitpos) & 0x3f; if (cur == 0) continue; // recombine hi = cur - 1; b = (hi << 6) | lo; // unshuffle b = (b << 10) + (b << 7) + b + 64; b = (b & 0xfff) + ((b & 0xfff000) >> 6); b = (b & 0xfff) + ((b & 0xfff000) >> 6); b = (b & 0xfff) + ((b & 0xfff000) >> 6); b -= 64; // report printf("%4d was stored in slot %2d using value %2d.\n", b, lo, cur); } } 个不同的值加上每个广告位可能的一个特殊空值。它使用{{1}}和{{1}}进行随机播放。

{{1}}

如您所见,可以避免所有乘法和除法运算,以及所有显式模调用。生成的代码是否比每次调用使用单个模调用的代码具有更高的性能仍有待测试。事实上,您需要最多三个减少步骤以避免单个模数,这使得这相当昂贵。

您可以观看Wolfram Alpha

答案 1 :(得分:0)

没有免费的午餐。

如果您的分布均匀,那么g(k1)每个n/k的值都会为k1。因此,您最终必须存储k*n/kn值,这些值恰好与您开始时的数字相同。

您可能应该寻找压缩算法而不是哈希函数。它会改善你的谷歌charma。

也就是说,在不知道数字分布的情况下很难建议压缩算法。如果它真的是随机的,那么就很难压缩。