什么是适用于 2D 点结构的“GetHashCode()”算法(避免冲突)
Posted
技术标签:
【中文标题】什么是适用于 2D 点结构的“GetHashCode()”算法(避免冲突)【英文标题】:What is an appropriate `GetHashCode()` algorithm for a 2D point struct (avoiding ***es) 【发布时间】:2011-07-10 10:25:27 【问题描述】:考虑以下代码:
struct Vec2 : IEquatable<Vec2>
double X,Y;
public bool Equals(Vec2 other)
return X.Equals(other.X) && Y.Equals(other.Y);
public override bool Equals(object obj)
if (obj is Vec2)
return Equals((Vec2)obj);
return false;
// this will return the same value when X, Y are swapped
public override int GetHashCode()
return X.GetHashCode() ^ Y.GetHashCode();
除了比较双精度值是否相等(这只是演示代码)之外,我关心的是交换 X、Y 值时会出现哈希冲突。例如:
Vec2 A = new Vec2() X=1, Y=5 ;
Vec2 B = new Vec2() X=5, Y=1 ;
bool test1 = A.Equals(B); // returns false;
bool test2 = A.GetHashCode() == B.GetHashCode() // returns true !!!!!
这应该会破坏字典集合。所以问题是如何为 2,3 甚至 4 个浮点值设置 GetHashCode()
函数的属性,以使结果不对称并且哈希不会冲突。
编辑 1:
Point
实现了不合适的x ^ y
解决方案,而PointF
包装了ValueType.GetHashCode()
。
Rectangle
有一个非常奇特的 (((X ^ ((Y << 13) | (Y >> 19))) ^ ((Width << 26) | (Width >> 6))) ^ ((Height << 7) | (Height >> 25)))
哈希码表达式,它似乎按预期执行。
编辑 2:
'System.Double' 有一个很好的实现,因为它并不认为每一位都同等重要
public override unsafe int GetHashCode() //from System.Double
double num = this;
if (num == 0.0)
return 0;
long num2 = *((long*) &num);
return (((int) num2) ^ ((int) (num2 >> 32)));
【问题讨论】:
可以尽量减少冲突,但是你的代码必须期待它们;它们总会发生 哈希 将 冲突 -int
的可能值范围比 double
小,与 double
xdouble
相比更小。此外,您可能需要考虑更宽松的相等比较 - 由于四舍五入,两个浮点值可能非常接近(任何人“通过眼睛”进行比较都会认为相等),但仍然不会完全相等。
不仅仅是 (A,B) 与 (B,A) 发生碰撞。由于 X^X --> 0 all (C,C) 与 all (D,D) 碰撞,这是一个更大的碰撞空间。
Create a hashcode of two numbers的可能重复
【参考方案1】:
Jon skeet 对此进行了介绍:
What is the best algorithm for an overridden System.Object.GetHashCode?
public override int GetHashCode()
unchecked // Overflow is fine, just wrap
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + X.GetHashCode();
hash = hash * 23 + Y.GetHashCode();
return hash;
另外,将您的 Equals(object)
实现更改为:
return Equals(obj as FVector2);
但是请注意,这可能会认为派生类型是相等的。如果您不希望这样,则必须将运行时类型 other.GetType()
与 typeof(FVector2)
进行比较(并且不要忘记空值检查)感谢您指出它是一个结构,LukH
Resharper 为相等和哈希码生成了很好的代码,所以如果你有 resharper,你可以让它做它的事情
【讨论】:
所讨论的类型是一个结构体,因此不能将Equals(object)
实现更改为使用as
。 OP 当前的Equals(object)
方法没有任何问题。
这可能效果很好,但是如果您查看Double.GetHashCode()
的计算,您会注意到使用了对重要位的更仔细考虑,我想效仿。
感谢您的回复,因为我已经为许多结构实现了这个版本。
很高兴为您提供帮助!【参考方案2】:
哈希冲突不会对字典集合造成严重破坏。如果你不幸得到它们,它们会降低效率,但字典必须处理它们。
如果可能的话,冲突应该很少见,但这并不意味着实现不正确。由于您给出的原因(高冲突),XOR 通常很糟糕 - ohadsc 发布了我之前提供的示例作为替代方案,这应该没问题。
请注意,实现Vec2
与no 冲突是不可能的——GetHashCode
只有 232 个可能的返回值,但还有更多可能的 X 和 Y 值,即使在您删除了 NaN 和无限值之后...
Eric Lippert 在GetHashCode
上有一个recent blog post,您可能会发现它很有用。
【讨论】:
请注意,当字段是浮点数时,位不密集,但遵循某些模式,给定合理的数字以使结构保持。因此,如果 xor 操作避免了最高有效数字的对称性,它就足够了。这有意义吗? @ja72:可能。这取决于现实世界的数据。如果它们实际上往往是整数,那仍然很糟糕。【参考方案3】:坐标的合理界限是什么?
除非它可以是所有可能的整数值,否则你可以简单地:
常量 SOME_LARGE_NUMBER=100000; 返回 SOME_LARGE_NUMBER * x + y;
【讨论】:
如果你知道界限,这真的是最好的。您也可以将它用于int
s 和 int.MaxValue
和 unchecked
子句【参考方案4】:
如果哈希码的大小小于结构的大小,那么冲突是不可避免的。
【讨论】:
但是例如与 (X=1, Y=5) 和 (X=5, Y=1) 发生冲突是不可接受的。与 (3.287e308, -7,.228e175) 及其对称对的冲突更容易接受。【参考方案5】:哈希码方法适用于整数坐标,但不建议用于浮点值。使用浮点坐标,可以通过使用排序的序列结构来创建点集/池。
有序序列是叶子版本的平衡二叉树。
这里的键是点坐标。
【讨论】:
您能否展示一些代码来解释您提到的内容。我很难想象它。我知道浮点精度会使GetHashCode()
不确定,但我不明白您打算如何解决这个问题。
忘掉 GetHashCode(),你不使用散列。刚刚使用了带有 xy 比较器的平衡二叉树。
我经常需要实现IEquatable<>
,因此需要哈希来覆盖。能否请您显示一些代码,以便我们了解您在说什么。
顺便说一句,您可以使用快照网格转换为整数,然后使用这些函数之一获得没有冲突的散列。 dmauro.com/post/77011214305/…
以独特且确定的方式将两个整数映射为一个。 ***.com/questions/919612/…以上是关于什么是适用于 2D 点结构的“GetHashCode()”算法(避免冲突)的主要内容,如果未能解决你的问题,请参考以下文章
gl.texImage2D适用于Image,但不适用于ImageData
适用于 Android 和 iOS 的 2D 跨平台游戏引擎? [关闭]
寻找适用于 iOS 和 Android 的基本 2D/3D 图形的跨平台方法
使用适用于 iOS 的 Google Vision API 添加 2D 或 3D 人脸过滤器,例如 MSQRD/SnapChat