【问题标题】:How can I make a hashcode for a custom data structure?如何为自定义数据结构制作哈希码?
【发布时间】:2011-12-17 00:22:37
【问题描述】:

我已经制作了一个自定义的“坐标”数据结构,它根据某个系统定义对象的位置。

坐标定义如下:

public class Coordinate
{
    public int X;
    public int Y;
    private int face;
    public int Face
    {
        get { return face; }
        set
        {
            if (value >= 6 | value < 0)
                throw new Exception("Invalid face number");
            else
                face = value;
        }
    }
    private int shell;
    public int Shell
    {
        get { return shell; }
        set
        {
            if (value < 0)
                throw new Exception("No negative shell value allowed");
            else
                shell = value;
        }
    }

    public Coordinate(int face, int x, int y, int shell)
    {
        this.X = x;
        this.Y = y;
        this.face = face;
        this.shell = shell;
    }

    public static Coordinate operator +(Coordinate a, Coordinate b)
    {
        return new Coordinate(a.Face + b.Face, a.X + b.X, a.Y + b.Y, a.Shell + b.Shell);
    }

    public override bool Equals(object obj)
    {
        Coordinate other = (obj as Coordinate);
        if (other == null)
            return false;
        else
            return (Face == other.Face && Shell == other.Shell && X == other.X && Y == other.Y);
    }
}

或者,总而言之,它包含一个 int Face(0 到 5)、一个 int X、int Y 和 int Shell。 X、Y 和 Shell 都绑定在 0(含)以下。

我完全没有哈希码方面的经验。我需要比较它们以查看它们是否相等。我试过这个:

private const int MULTIPLIER = 89;

[...]

int hashCode = 1;
hashCode = MULTIPLIER * hashCode + obj.X.GetHashCode();
hashCode = MULTIPLIER * hashCode + obj.Y.GetHashCode();
hashCode = MULTIPLIER * hashCode + obj.Face.GetHashCode();
hashCode = MULTIPLIER * hashCode + obj.Shell.GetHashCode();
return hashCode;

关闭我在谷歌搜索时发现的东西。但是当我尝试用这种方法编译代码时,我很确定它会遇到冲突,因为它永远不会完成构建。可能会陷入各种混乱的循环,认为一堆坐标是相同的或类似的。

对不起,这个问题相当初级,但出于某种原因,我很难过。我只是在寻找有关如何编写此哈希码以使其不会发生冲突的建议。

【问题讨论】:

  • hashcode 碰撞完全没有问题。最好不要发生碰撞,但这不是必需的(在数学上也是不可能的)。
  • msdn.microsoft.com/en-us/library/… -- "派生类必须使用返回唯一哈希码的实现覆盖 GetHashCode。"
  • @MattFenwick 由于鸽巢原理,大多数类型都没有唯一的哈希码。* 那篇文章有点不准确。他们在后续版本中删除了该行。 *- int.GetHashCode() 可能对于每个数字都是唯一的。
  • @IlianPinzon -- 很好,我很高兴这是错误的,因为它让我感到困惑。感谢您清除它!

标签: c# hash


【解决方案1】:

如果这不是最好的方法,它可能是一个足够好的方法:

public override int GetHashCode()
{
   return string.Format("{0}-{1}-{2}-{3}", X, Y, Face, Shell).GetHashCode();
}

更新: 看看这篇文章:http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

【讨论】:

  • 这看起来很不错,谢谢。我之前尝试将每个组件乘以 100000、10000、1000 和 1 来得到这个,但这更干净和可扩展。
  • 好吧,它仍然没有构建......我会保留这个哈希码并做一些单元测试,看看我是否做错了什么。
  • 我应该说“它仍然卡住”,而不是“无法构建”。没有编译器错误。游戏只是陷入了使用哈希码的循环中。
  • 原来我在使用异常时应该更聪明地处理代码并使用布尔值来告诉我某些东西是否有效。异常使程序的初始化速度变慢。这种放缓以前没有出现的事实实际上证明了事情以前是错的,我没有意识到。谢谢你的帮助;我的室友提醒我,为了按值进行比较,结构比摆弄哈希码要好。但是你的方法也行,我确实学到了一些东西!
  • 如果您需要编写可扩展的代码,此答案的效率非常低,因为首先您需要将每个 int 转换为字符串,然后对字符串进行哈希计算会产生进一步的成本。为了有效地散列多个 int 字段,请在此处查看我的答案:stackoverflow.com/a/34006336/1911540
【解决方案2】:

基本上,在编写哈希码函数时,您需要确保:

  • 您没有过时的哈希码(即,在生成哈希码后对象的状态不应改变,这样哈希码会在重新生成时改变)
  • 具有相同值的对象返回相同的哈希码
  • 相同的对象总是返回相同的哈希码(如果它没有被修改)——确定性

另外,如果:

  • 您的哈希码均匀分布在可能的值上(来源:Wikipedia

不需要确保不同的对象返回不同的哈希码。它只是不受欢迎,因为它会降低 Hashtables 之类的性能(如果你有很多冲突)。

但是,如果您仍然希望哈希码函数返回唯一值,那么您想了解perfect hashing

【讨论】:

    【解决方案3】:

    如果你使用dotnetcore 2.1+,可以使用HashCodestruct的Combile方法,非常好用,效率很高。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-31
      • 1970-01-01
      • 2016-06-20
      • 1970-01-01
      相关资源
      最近更新 更多