【问题标题】:Generating a good hash code (GetHashCode) for a BitArray为 BitArray 生成良好的哈希码 (GetHashCode)
【发布时间】:2011-03-08 17:29:15
【问题描述】:

我需要在 GetHashCode 中为 BitArray 生成一个快速哈希码。我有一个字典,其中的键是 BitArrays,并且所有 BitArrays 的长度都相同。

有没有人知道一种从可变位数生成良好哈希的快速方法,就像在这种情况下一样?

更新:

我最初采用的方法是直接通过反射访问内部整数数组(在这种情况下,速度比封装更重要),然后对这些值进行异或。 XOR 方法似乎运作良好,即在 Dictionary 中搜索时不会过度调用我的“Equals”方法:

    public int GetHashCode(BitArray array)
    {
        int hash = 0;
        foreach (int value in array.GetInternalValues())
        {
            hash ^= value;
        }
        return hash;
    }

但是,Mark Byers 建议并在 StackOverflow 其他地方看到的方法稍微好一些(我的测试数据的 XOR 调用为 16570 Equals 和 16608)。请注意,这种方法修复了前一种方法中的一个错误,即位数组末尾之外的位可能会影响哈希值。如果位数组的长度减少,就会发生这种情况。

    public int GetHashCode(BitArray array)
    {
        UInt32 hash = 17;
        int bitsRemaining = array.Length;
        foreach (int value in array.GetInternalValues())
        {
            UInt32 cleanValue = (UInt32)value;
            if (bitsRemaining < 32)
            {
                //clear any bits that are beyond the end of the array
                int bitsToWipe = 32 - bitsRemaining;
                cleanValue <<= bitsToWipe;
                cleanValue >>= bitsToWipe;
            }

            hash = hash * 23 + cleanValue;
            bitsRemaining -= 32;
        }
        return (int)hash;
    }

GetInternalValues 扩展方法是这样实现的:

public static class BitArrayExtensions
{
    static FieldInfo _internalArrayGetter = GetInternalArrayGetter();

    static FieldInfo GetInternalArrayGetter()
    {
        return typeof(BitArray).GetField("m_array", BindingFlags.NonPublic | BindingFlags.Instance);
    }

    static int[] GetInternalArray(BitArray array)
    {
        return (int[])_internalArrayGetter.GetValue(array);
    }

    public static IEnumerable<int> GetInternalValues(this BitArray array)
    {
        return GetInternalArray(array);
    }

... more extension methods
}

欢迎提出任何改进建议!

【问题讨论】:

  • 您最大的问题是 GetHashCode 返回一个 int32。这仅允许大约 40 亿个不同的值。因此,对于最大大小为 32 位的位数组,您的 HashCode 只能是无冲突/唯一的。如果你想用你的 BitArrays 做大,我建议你去一个自定义的字典实现,它支持一个自定义的 GetHashCode,它支持 long 作为结果类型,甚至可能是一个 bool 数组

标签: c# .net dictionary gethashcode bitarray


【解决方案1】:

在字典中充当键是一个糟糕的类。实现 GetHashCode() 的唯一合理方法是使用其 CopyTo() 方法将位复制到 byte[] 中。这不是很好,它会产生大量垃圾。

乞求、偷窃或借用 BitVector32。它对 GetHashCode() 有很好的实现。如果你有超过 32 位,那么考虑旋转你自己的类,这样你就可以得到底层数组而无需复制。

【讨论】:

  • 我需要超过 32 位。我正在考虑编写自己的课程(在 Reflector 的帮助下),但不利用内置的 BitArray 似乎很可惜。一点反射黑客让我得到了内部数组,这当然可以在框架的未来版本中改变 - 例如64 位版本在 64 位硬件上可能更高效。不过,我现在对这个解决方案很满意。
【解决方案2】:

如果位数组是 32 位或更短,那么您只需将它们转换为 32 位整数(必要时用零位填充)。

如果它们可以更长,那么您可以将它们转换为一系列 32 位整数并对它们进行异或,或者更好:使用有效 Java 中描述的算法。

public int GetHashCode()
{
    int hash = 17;
    hash = hash * 23 + field1.GetHashCode();
    hash = hash * 23 + field2.GetHashCode();
    hash = hash * 23 + field3.GetHashCode();
    return hash;
}

取自here。 field1、field2对应前32位、后32位等。

【讨论】:

  • 我在其他地方看到过你的方法,但我并不真正理解它背后的理论或“神奇”素数的选择。这种方法比我最初采用的 XOR 方法稍微有效(16570 Equals 调用 vs 16608 用于我的测试数据的 XOR)。有关更多详细信息,请参阅我的编辑。
猜你喜欢
  • 2010-11-22
  • 1970-01-01
  • 2011-10-01
  • 1970-01-01
  • 2014-12-31
  • 2014-05-14
  • 2017-03-06
  • 2013-04-11
相关资源
最近更新 更多