基于三组数字生成交换哈希?

时间:2009-05-05 19:41:00

标签: c# hash

我需要根据三组“得分”结构生成一个可交换的哈希值。

每个分数都有“开始”,“结束”和“数字”。

开始和结束通常都是大数字(8-9位数),但数字只是从1到4。

我需要它们是可交换的,所以顺序无关紧要。我现在正在使用XOR,但它似乎给出了糟糕的结果。

由于我正在使用大型数据集,因此我更喜欢性能友好的解决方案。 有什么建议?谢谢=]

    public static int getCustomHash(cnvRegion c1, cnvRegion c2, cnvRegion c3)
    {
        int part1 = (c1.startLocation * c2.startLocation * c3.startLocation);
        int part2 = (c1.endLocation * c2.endLocation * c3.endLocation);
        int part3 = (c1.copyNumber + c2.copyNumber + c3.copyNumber)*23735160;
        return part1 ^ part2 ^ part3;
    }

2 个答案:

答案 0 :(得分:1)

Thomas Wang讨论了哈希函数here

  • 请参阅knuth的方法和64到32位混音功能。

Paul Hsieh也有a page on integer hashing,描述了他的“SuperFastHash”功能,该功能得到了不同的反馈。

修改

因为你希望你的自定义哈希是可交换的(我假设在cnvRegion参数之间)你可能会写这样的东西:

public int hash6432shift(long key)
{
   key = (~key) + (key << 18); // key = (key << 18) - key - 1;
   key = key ^ (key >>> 31);
   key = key * 21; // key = (key + (key << 2)) + (key << 4);
   key = key ^ (key >>> 11);
   key = key + (key << 6);
   key = key ^ (key >>> 22);
   return (int) key;
}

public static int getCustomHash(cnvRegion c1, cnvRegion c2, cnvRegion c3)
{
    int part1 = (c1.startLocation ^ c2.startLocation ^ c3.startLocation);
    int part2 = (c1.endLocation ^ c2.endLocation ^ c3.endLocation);
    int part3 = (c1.copyNumber ^ c2.copyNumber ^ c3.copyNumber);

    int hash1 = hash6432shift(((long)part1 << 0x20) | part2);
    return hash6432shift(((long)hash1 << 0x20) | part3);
}

然而,最终找到快速且提供良好抗冲突性的散列函数的任务非常依赖于您正在处理的数据。

让我举个例子:

假设您正在散列的值是大的,10位数字,它们代表UNIX时间戳(自1970年1月1日以来经过的时间,以秒为单位)。在这种情况下,散列在有限的时间跨度内发生的大量时间戳 - 比如超过一个月只是消除不改变的部分,并且仅使用时间戳的一部分变化很多。这与说你正在消除熵低的部分是一样的。

v1 = 1241536920   // 5/5/2009 3:22:00 PM
v2 = 1241529720   // 5/5/2009 1:22:00 PM
v3 = 1241270520   // 5/2/2009 1:22:00 PM
v4 = 1242825720   // 5/20/2009 1:22:00 PM

很明显,我们可以安全地消除前3-4位数字,只使用剩余的数字作为哈希值。 此外,如果这些值通常在几分钟之内发生,您也可以删除最后2-3位数字。

通过这种方式,您只剩下4位数字,您可以将它们用作具有相当好的抗冲击性的散列,用于我们的示例。

我的观点是,如果您知道要尝试散列的值的统计分布,则可以高度优化 散列函数。

答案 1 :(得分:1)

首先,我认为要求不是很清楚。如果您散列三个数据集c1,c2和c3。然后,如果你切换c1.copyNumber和c2.copyNumber并再次哈希。这应该给出相同的结果吗? 如果使用c1.endLocation切换c1.startLocation。这应该导致相同的哈希值吗?

我将假设您希望在两种情况下都有不同的哈希结果,并且唯一不应更改哈希结果的排列是数据集c1,c2,c3的排列。

如果是这种情况,那么我建议首先将三个数据集独立地散列为较小的值。即   h1 = H(c1)   h2 = H(c2)   h3 = H(c3) 其中H可以是任何散列函数(例如,CRC32,Adler32,SHA1等),具体取决于您希望避免碰撞的难度。

下一步是计算h1,h2,h3的交换散列。如果你想避免碰撞,除非h1,h2,h3被置换,那么以下工作。 计算多项式

  • P(x)=(x-h1)(x-h2)(x-h3)

然后用任何好的散列函数散列多项式(rsp。其系数)。即那 会是

  • H(h1 + h2 + h3 || h1 * h2 + h1 * h3 + h2 * h3 || h1 * h2 * h3),其中||是串联。

如果你想不惜一切代价避免任何不必要的碰撞,那么系数应该被计算为多精度整数,并且应该使用诸如SHA1的抗冲突散列函数。由于多项式的独特因式分解,如果h1,h2和h3不同,则多项式的系数不同。 但似乎不惜一切代价避免碰撞在你的情况下是过度的。

因此,不是象征性地计算多项式P(x),而是可以在任意值R处对其进行评估。如果h1,h2,h3只是32位值,那么计算以下内容 可能就够了:(一些C类伪代码如下。我不知道C#用于64位整数)

const long long R = SOME_RANDOM_64_BIT_CONSTANT;
long long hash0 = (R - h1) * (R - h2) * (R - h3);
int hash = (int) (hash0 >> 32);

我这里是64位乘法,因为它们在现代CPU上足够快,我使用的是高位32位的hash0而不是低位32位,因为低32位有些偏差。即,最低有效位更可能是0而不是1。