更好的64位字节数组哈希

时间:2015-07-16 21:32:43

标签: c# hashcode

我需要一个产生 64位哈希码long)的哈希算法,其冲突少于String.GetHashCode()且速度很快(对加密函数没有昂贵的调用) )。这是FNV的一个实现,在测试200万个随机字符串后仍然显示3%的冲突。我需要这个数字更低。

void Main()
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#@$%^&*()_+}{\":?><,./;'[]0123456789\\";
    const int n = 2000000;
    var random = new Random();
    var hashes = new HashSet<long>();
    int collisions = 0;
    for(int i = 0; i < n; i++)
    {
        var len = random.Next(chars.Length);
        var str = new char[len];
        for (int j = 0; j < len; j++)
        {
            str[j] = chars[random.Next(chars.Length)];
        }
        var s = new String(str);
        if(!hashes.Add(Get64BitHash( s ))) collisions++;
    }
    Console.WriteLine("Collision Percentage after " + n + " random strings: " + ((double)collisions * 100 / n));
}


public long Get64BitHash(string str)
{
  unchecked
  {
     byte[] data = new byte[str.Length * sizeof(char)];
     System.Buffer.BlockCopy(str.ToCharArray(), 0, data, 0, data.Length);

     const ulong p = 1099511628211UL;
     var hash = 14695981039346656037UL;
     foreach(var d in data)
     {
        hash ^= d;
        hash *= p;
     }
     return (long) hash;
  }
}

上面代码的示例输出:

2000000随机字符串后的碰撞百分比:3.01485%

3%与仅调用String.GetHashCode()的碰撞百分比相同。我需要更好的方法。

PS:我有可能做一些非常长的事情。

修改: 的解决即可。上面的Get64BitHash方法是正确的。问题是我的字符串不是随机的。在确保字符串是唯一的之后(参见下面的修订代码),我在近5000万个唯一字符串上发生冲突,而使用String.GetHashCode()的冲突约为1%。

void Main()
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#@$%^&*()_+}{\":?><,./;'[]0123456789\\";
    const int n = 200000000;
    var random = new Random();
    var hashes = new HashSet<long>();
    var strings = new HashSet<string>();
    int collisions = 0;
    while(strings.Count < n)
    {
        var len = random.Next(chars.Length);
        var str = new char[len];
        for (int j = 0; j < len; j++)
        {
            str[j] = chars[random.Next(chars.Length)];
        }
        var s = new String(str);
        if(!strings.Add(s)) continue;
        if(!hashes.Add(s.GetHashCode())) collisions++;
    }
    Console.WriteLine("Collision Percentage after " + n + " random strings: " + ((double)collisions * 100 / strings.Count));
}

3 个答案:

答案 0 :(得分:3)

问题是你的字符串不是随机的。 在第二次散列之前测试你的字符串。

答案 1 :(得分:1)

  

3%与仅调用String.GetHashCode()

的碰撞百分比相同

也许这是理论上的最佳选择。内置的哈希码也不错。尝试使用SHA2来确认这是你能做的最好的事情。

由于您的测试字符串是随机的,因此哈希码也可能分布良好。

通过不创建两个似乎没有任何用途的临时缓冲区来优化该功能。只需直接访问字符(str[0])。这样就可以保存副本并每次迭代处理两个字节。

答案 2 :(得分:0)

你应该统计真正的哈希碰撞,因为大多数碰撞是由碰撞的字符串造成的。

声明以下内容:

var hashesString = new HashSet<string>();
int collisionsString = 0 ;
int testedCollisions = 0 ;

然后修改您的代码如下:

   if(hashesString.Add(s))
   { // Count collisions only for new strings
     testedCollisions++ ;
     if (!hashes.Add(Get64BitHash( s ))) collisions++;
   }
 }
 Console.WriteLine("Collision Percentage after " + testedCollisions + " random strings: " + ((double)collisions * 100 / testedCollisions));

我使用更新的代码进行了运行并获得了没有真正的冲突(只有60 000个重复的字符串)。