C# 为近似相似的数字生成相同的哈希代码

C# 为近似相似的数字生成相同的哈希代码,c#,math,coordinates,autocad,C#,Math,Coordinates,Autocad,我正在C#3.5中创建一个应用程序,它使用AutoCAD API读取二维AutoCAD图形,使用定义的业务逻辑对图形进行更改,然后在AutoCAD中对其进行调整。由于逻辑的性质,必须重新构造图纸的形状-例如,矩形由4条连接直线组成 我使用AutoCAD中每条线的起点坐标和终点坐标创建这些形状,但有些坐标并不完全匹配。例如,一个点可以位于0.69912839(在一个轴上),但从同一点开始的直线可以是0.69990821。这些单位为毫米,因此距离为分钟(0.00078毫米!) 我创建了自己的类(称之

我正在C#3.5中创建一个应用程序,它使用AutoCAD API读取二维AutoCAD图形,使用定义的业务逻辑对图形进行更改,然后在AutoCAD中对其进行调整。由于逻辑的性质,必须重新构造图纸的形状-例如,矩形由4条连接直线组成

我使用AutoCAD中每条线的起点坐标和终点坐标创建这些形状,但有些坐标并不完全匹配。例如,一个点可以位于0.69912839(在一个轴上),但从同一点开始的直线可以是0.69990821。这些单位为毫米,因此距离为分钟(0.00078毫米!)

我创建了自己的类(称之为MyPoint,类似于PointF),因为我需要向它添加一些额外的逻辑。在这个类中,我创建了一个方法,它接受两个double并根据两点之间的距离是否在0.001mm内返回true或false。然后我重写了Equals方法,==和!=所以我可以做(point1==point2或point1.Equals(point2))来检查所有轴是否彼此在0.001mm范围内-如果在0.001mm范围内,我将其归类为同一点

那很好,工作很出色。现在,我需要检查这些点类的集合以消除所有重复项,因此我在集合上使用LINQ的Distinct()方法。但是,此方法使用GetHashcode(),而不是Equals()来确定实例是否相等。因此,我重写了GetHashcode(),它使用double类的GetHashcode

但是,上面的示例失败了,因为它们显然是不同的值,因此生成不同的哈希代码。有没有办法让两个相距在0.001以内的数字生成相同的哈希代码?(请注意,由于GetHashcode是在不同的类实例上单独调用的,所以这些数字彼此并不了解。)我尝试了许多方法,这些方法对某些示例有效,但对其他示例无效

一个例子是将数字截断为3dp(乘以10^3,然后截断),并在结果上创建哈希代码——这适用于上面的例子(699==699。),但这不适用于0.69990821和0.70000120(699!=700)。我尝试了舍入,它适用于第二组数字(0.700==0.700),但不适用于第一组(0.699!=0.700。)我甚至尝试将该数字截断为3dp,然后将其调整为下一个偶数,这对前面的两个示例都有效,但对12.9809和12.9818(12980!=12982)则无效


还有其他方法吗?或者我应该放弃Equals、==、!=和GetHashcode覆盖,创建我自己的MyPoint.IsEqualTo()和MyPointCollection.Distinct()方法吗?

这里有一些代码来说明我在做什么。“original”中的每对数字都应该返回相同的值

int tolerance = 3;
double[] original = new double[] {
0.69912839,
0.69990821,

0.69990821,
0.70000120,

12.980984087,
12.981808908
};
double[] modified = new double[original.Length];

for (int i = 0; i < original.Length; i++)
{
modified[i] = original[i];

/* Begin number adjustment logic */
modified[i] *= Math.Pow(10, tolerance);
modified[i] = Math.Truncate(modified[i]);

if (modified[i] % 2 != 0)
{
modified[i]++;
}
/* End number adjustment logic */

Console.WriteLine(modified[i]);

if (i % 2 != 0)
{
Console.WriteLine(string.Empty);
}
}
这是四舍五入法:

/* Begin number adjustment logic */
modified[i] = Math.Round(modified[i], tolerance);
/* End number adjustment logic */

无法编写正确的哈希代码。请验证: 我们得2分。 var a=point1.GetHashCode(); var b=point2.GetHashCode()

如果a!=b,让我们在点1和点2之间创建点。依此类推

在这些操作之后,我们将创建一条直线,其中每个点都靠近另一个点,并且它们的哈希代码将是相同的。 所以点1和点2的哈希代码应该相等

因此,我们应该这样做:

public override int GetHashCode()
{
    return 0;
}
if(P1 == P2 && P2 == P3 && P1 != P3)
{
    // Code here gets executed
}

我想如果你总是返回相同的散列(比如0),LinQ会尝试将所有元素与
equals
进行比较。毕竟,散列有助于证明两个元素是不同的,而不是相等的


但无论如何,我建议您使用更适合此域的结构和算法,例如二进制分割分区(BSP)树。

我认为您不应该覆盖
Equals()
==
!=
GetHashCode()

如果您重写了其中任何一个,您应该确保它们的语义不会改变。在您的示例中,它们会改变

例如,你的
=
不能是可传递的,因为它是可传递的,如果P1距离P2 0.001毫米,P2距离P3 0.001毫米,P1距离P3 0.002毫米,那么P1==P2,P2==P3和P1==P3,这不是你想要的。通常,你最终得到的所有点都等于所有其他点

我会坚持使用单独的方法来确定点是否足够接近

编辑

通过覆盖
==
,您现在可以编写如下代码:

public override int GetHashCode()
{
    return 0;
}
if(P1 == P2 && P2 == P3 && P1 != P3)
{
    // Code here gets executed
}

仅删除对Distinct方法的依赖性会更容易。实现System.Collections.IComparer(或等效的泛型)并使用简单的集合(如列表)。然后使用比较器确定该项是否在列表中,如果已经包含,则不要添加它

我应该放弃Equals、==、!=和GetHashcode重写,创建自己的MyPoint.IsEqualTo()和MyPointCollection.Distinct()方法吗

不过,它不一定是完全不同的数据结构。在检查副本时,需要检查相邻的哈希代码,例如(x+0.001,y)、(x,y-0.001)的哈希,等等。与通常的重复数据消除查找相比,这只会导致恒定因素的速度减慢,而且并不复杂,因此这可能是一种可行的方法。(这是一个显而易见的观点,但我认为在这里还没有明确指出。)

<强>更新:,让我们看一个问题的一维版本。“点”是单数,x。我们考虑X1和X2在<代码> ABS(X1-X2)< 001 >代码>时匹配。现在,我们想找出X是否匹配XX0,…,Xyn}。XXI被保存在一个哈希表中,其中<代码>散列(x)=H(Lead(1000×x))。对于一些散播东西的函数h()。为了查看x是否已经在表中,我们计算

散列(x-.001)
散列(x)
,和
散列(x+.001)
,然后测试x是否匹配三个存储桶中的任何一个。任何匹配的x\u i都不能在任何其他存储桶中

在二维变型中,