如何生成在C#中的字节数组的哈希码?(How do I generate a hashcode fr

2019-09-01 18:16发布

说我有一个存储字节数组的对象,我希望能够有效地生成一个散列码吧。 我已经使用了加密散列函数为这个在过去,因为他们很容易实现,但他们做了比他们应该是加密的单向很多工作,我不关心这个(我只是用哈希码作为重点成哈希表)。

这是我今天有:

struct SomeData : IEquatable<SomeData>
{
    private readonly byte[] data;
    public SomeData(byte[] data)
    {
        if (null == data || data.Length <= 0)
        {
            throw new ArgumentException("data");
        }
        this.data = new byte[data.Length];
        Array.Copy(data, this.data, data.Length);
    }

    public override bool Equals(object obj)
    {
        return obj is SomeData && Equals((SomeData)obj);
    }

    public bool Equals(SomeData other)
    {
        if (other.data.Length != data.Length)
        {
            return false;
        }
        for (int i = 0; i < data.Length; ++i)
        {
            if (data[i] != other.data[i])
            {
                return false;
            }
        }
        return true;
    }
    public override int GetHashCode()
    {
        return BitConverter.ToInt32(new MD5CryptoServiceProvider().ComputeHash(data), 0);
    }
}

有什么想法吗?


DP:你说得对,我错过了在等于支票,我已经更新了。 从字节数组使用现有的哈希码将导致参考平等(或至少是同一概念翻译成哈希码)。 例如:

byte[] b1 = new byte[] { 1 };
byte[] b2 = new byte[] { 1 };
int h1 = b1.GetHashCode();
int h2 = b2.GetHashCode();

与该代码,尽管有在其中相同的值的两个字节数组,它们指的是存储器的不同部分,并且将导致在(可能)不同的散列码。 我需要的散列码为具有相同内容的两个字节数组是相等的。

Answer 1:

对象的哈希码不必是唯一的。

检查的规则是:

  • 是哈希码等于? 然后调用全(慢) Equals方法。
  • 是不是哈希码等于? 那么这两个项目是绝对不相等。

所有你想要的是一个GetHashCode算法您的收藏分裂成大致相抵群体-它不应该构成键作为HashTableDictionary<>将需要使用哈希来优化检索。

多久你期望的数据呢? 如何随机的? 如果长度差别很大(说的文件),然后只返回长度。 如果长度很可能是在那个改变字节的一个子集相似的外观。

GetHashCode应该比快了很多Equals ,但不必是唯一的。

两个相同的东西绝不能有不同的散列码。 两个不同的对象不应该有相同的散列码,但有些冲突是可以预期的(毕竟,还有比可能32个整数多个排列)。



Answer 2:

不要使用密码散列的哈希表,这是荒谬/矫枉过正。

这里亚去...修改FNV哈希在C#

http://bretm.home.comcast.net/hash/6.html

    public static int ComputeHash(params byte[] data)
    {
        unchecked
        {
            const int p = 16777619;
            int hash = (int)2166136261;

            for (int i = 0; i < data.Length; i++)
                hash = (hash ^ data[i]) * p;

            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;
            return hash;
        }
    }


Answer 3:

从JetBrains公司软件生成的代码借用,我已经看中了这个功能:

    public override int GetHashCode()
    {
        unchecked
        {
            var result = 0;
            foreach (byte b in _key)
                result = (result*31) ^ b;
            return result;
        }
    }

与刚刚进行异或字节的问题是,所返回的值的3/4(3个字节)仅具有2可能的值(全部导通或全部断开)。 这四处传播更多一点位。

设置在等于断点是一个很好的建议。 增加约200,000我的数据记录到词典,看到约10的Equals电话(或1 / 20,000)。



Answer 4:

你是否已经比较SHA1CryptoServiceProvider.ComputeHash方法? 它需要一个字节数组,并返回一个SHA1哈希,我相信这是很好的优化。 我用它在Identicon处理器在负载下表现不俗。



Answer 5:

我发现有趣的结果:

我有类:

public class MyHash : IEquatable<MyHash>
{        
    public byte[] Val { get; private set; }

    public MyHash(byte[] val)
    {
        Val = val;
    }

    /// <summary>
    /// Test if this Class is equal to another class
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public bool Equals(MyHash other)
    {
        if (other.Val.Length == this.Val.Length)
        {
            for (var i = 0; i < this.Val.Length; i++)
            {
                if (other.Val[i] != this.Val[i])
                {
                    return false;
                }
            }

            return true;
        }
        else
        {
            return false;
        }            
    }

    public override int GetHashCode()
    {            
        var str = Convert.ToBase64String(Val);
        return str.GetHashCode();          
    }
}

然后,我创建了类型MyHash的键的字典,以测试多快我可以插入,我也可以知道有多少冲突也有。 我做了以下

        // dictionary we use to check for collisions
        Dictionary<MyHash, bool> checkForDuplicatesDic = new Dictionary<MyHash, bool>();

        // used to generate random arrays
        Random rand = new Random();



        var now = DateTime.Now;

        for (var j = 0; j < 100; j++)
        {
            for (var i = 0; i < 5000; i++)
            {
                // create new array and populate it with random bytes
                byte[] randBytes = new byte[byte.MaxValue];
                rand.NextBytes(randBytes);

                MyHash h = new MyHash(randBytes);

                if (checkForDuplicatesDic.ContainsKey(h))
                {
                    Console.WriteLine("Duplicate");
                }
                else
                {
                    checkForDuplicatesDic[h] = true;
                }
            }
            Console.WriteLine(j);
            checkForDuplicatesDic.Clear(); // clear dictionary every 5000 iterations
        }

        var elapsed = DateTime.Now - now;

        Console.Read();

我每次插入新项目的字典词典将计算对象的哈希值。 所以,你能告诉什么方法是最有效的通过将在这里找到的方法好几个答案public override int GetHashCode() ,这是迄今为止最快,有碰撞数最少的是方法:

    public override int GetHashCode()
    {            
        var str = Convert.ToBase64String(Val);
        return str.GetHashCode();          
    }

这花了2秒执行。 方法

    public override int GetHashCode()
    {
        // 7.1 seconds
        unchecked
        {
            const int p = 16777619;
            int hash = (int)2166136261;

            for (int i = 0; i < Val.Length; i++)
                hash = (hash ^ Val[i]) * p;

            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;
            return hash;
        }
    }

有没有冲突也不过花了7秒执行!



Answer 6:

使用从字节数组场不够好现有的哈希码? 还要注意的是,在Equals方法,你应该检查数组做比较之前的大小相同。



Answer 7:

生成一个很好的散列谈何容易。 请记住,你基本上代表与信息m位数据的n个字节。 您的数据集较大和较小的m是,就越有可能你会得到一个碰撞...数据解析为相同的散列的两件。

我学过的最简单的散列值被简单地异或所有字节在一起。 这很容易,不是最复杂的哈希算法和小数据集半路出家通用散列算法更快。 这是冒泡排序哈希算法真的。 因为简单的实现将让你与8位,这是只有256哈希......没有这么热。 你可以XOR块,而不是individal字节,但随后的算法变得更加复杂。

因此可以肯定,加密算法也许做一些东西,你不需要......但他们也达在通用散列质量的一大步。 您正在使用的MD5哈希有128位,与数十亿可能哈希值。 你可能会得到更好的东西,唯一的办法就是把你期望通过应用程序来准备一些数据的代表性样本,并尝试在其上的各种算法,看看你有多少碰撞获得。

所以,直到我看到一些理由不使用罐装的散列算法(性能,也许?),我不得不建议你坚持你有什么。



Answer 8:

无论你想要一个完美的散列函数(不同的计算结果等于每个对象的值),或只是一个相当不错的始终是一个性能折衷,它通常需要时间来计算一个良好的散列函数,如果你的数据集是短小你提供更好的一个快速的功能。 最重要的(如你的第二个职位指出)是正确性,并实现了所有你需要的是返回数组的长度。 根据您的数据集,即使可能是好的。 如果不是(说你所有的阵列等长),您可以用便宜的东西看着像在第一和最后一个值和异或它们的值,然后添加更多的复杂性,你认为合适你的数据去。

一个快速的方法,看看你的散列函数如何执行你的数据是将所有数据添加到哈希表和计数的时间的Equals函数被调用的次数,如果过于频繁,你有更多的工作要做功能。 如果你这样做只是记住,哈希表的大小需要设置比你的数据集,当你开始变大,否则你要老调重弹,这将触发重新插入更多的Equals评估数据(尽管可能更现实吗?)

对于某些对象(不是这一个)可以通过toString()方法来生成一个快速的哈希码。GetHashCode()方法,肯定不是最佳的,但有用的,因为人们往往会返回一些接近从的ToString(对象的身份),而这正是什么的GetHashCode正在寻找

花絮:我所见过的最糟糕的表现,当有人错误地返回从GetHashCode的一个常数,虽然容易与调试发现,特别是如果你做大量的查找在哈希表



Answer 9:

如果你正在寻找的表现,我测试了几个哈希键,我建议鲍勃·詹金的哈希函数 。 它既是快疯了计算,并给尽可能少的碰撞,你使用到现在的加密哈希。

我不知道C#的一切,我不知道它是否可以使用C链接,但这里是它的C实现 。



Answer 10:

private int? hashCode;

public override int GetHashCode()
{
    if (!hashCode.HasValue)
    {
        var hash = 0;
        for (var i = 0; i < bytes.Length; i++)
        {
            hash = (hash << 4) + bytes[i];
        }
        hashCode = hash;
    }
    return hashCode.Value;
}


Answer 11:

RuntimeHelpers.GetHashCode可能会有所帮助:

从MSDN:

用作特定类型,适用于在哈希算法和数据结构中使用的哈希函数,诸如哈希表。



文章来源: How do I generate a hashcode from a byte array in C#?
标签: c# hash