3

AutoCADAPIを使用して2DAutoCAD図面を読み取り、定義されたビジネスロジックを使用して図面に変更を加えてから、AutoCADで調整するアプリケーションをC#3.5で作成しています。ロジックの性質上、図面の形状を再構築する必要があります。たとえば、長方形は4本の直線で構成されています。

AutoCADの各線の開始座標と終了座標を使用してこれらの形状を作成していますが、一部の座標が正確に一致していません。たとえば、1つのポイントは0.69912839(1つの軸上)にある可能性がありますが、同じポイントから始まる線は0.69990821である可能性があります。これらはmm単位であるため、距離は分です(0.00078mm!)

ロジックを追加する必要があるため、独自のクラス(PointFと同様にMyPointと呼びます)を作成しました。そのクラスでは、2つのdoubleを取り、2つのポイントが互いに0.001mm以内にあるかどうかに応じて、trueまたはfalseを返すメソッドを作成しました。次に、Equalsメソッド、==および!=演算子をオーバーライドして、すべての軸が互いに0.001mm以内にあるかどうかをチェックする(point1 == point2またはpoint1.Equals(point2))ことができるようにしました。同じ点として分類します。

それは問題なく、見事に機能しています。ここで、これらのポイントクラスのコレクションをチェックして、すべての重複を取り除く必要があるため、コレクションでLINQのDistinct()メソッドを使用しています。ただし、このメソッドは、Equals()ではなくGetHashcode()を使用して、インスタンスが等しいかどうかを判断します。そこで、doubleクラスのGetHashcodeを使用するGetHashcode()をオーバーライドしました。

ただし、上記の例は、明らかに異なる値であり、したがって異なるハッシュコードを生成するため、失敗します。互いに0.001以内にある2つの数値が同じハッシュコードを生成できる方法はありますか?(GetHashcodeは異なるクラスインスタンスで別々に呼び出されるため、番号はお互いを知らないことに注意してください。)いくつかの例では機能するが、他の例では機能しない多くの方法を試しました。

1つの例は、数値を3dpに切り捨て(10 ^ 3を掛けてから切り捨て)、結果にハッシュコードを作成することです。これは、上記の例(699 == 699)では機能しますが、0.69990821では機能しません。 0.70000120(699!= 700.)丸めを試しました。これは、2番目の数値セット(0.700 == 0.700)では機能しますが、最初の数値セット(0.699!= 0.700)では機能しません。数値を3dpに切り捨てることも試みました。次に、次の偶数まで調整します。これは、前の両方の例では機能しますが、12.9809と12.9818(12980!= 12982)では機能しません。

別の方法はありますか、またはEquals、==、!=、およびGetHashcodeオーバーライドを破棄して、独自のMyPoint.IsEqualTo()およびMyPointCollection.Distinct()メソッドを作成する必要がありますか?

4

7 に答える 7

3

正しいハッシュコードを書くことができません。それを証明しましょう:2つのポイントがあります。var a = point1.GetHashCode(); var b = point2.GetHashCode();

a!= bの場合、point1とpoint2の間にポイントを作成します。等々。

このような操作の後、各ポイントが他のポイントの近くにあり、それらのハッシュコードが同じである線を作成します。したがって、point1とpoint2のハッシュコードは等しくなければなりません。

だからこのように過激になります:

public override int GetHashCode()
{
    return 0;
}

そしてあなたが等しいことを実装します。

于 2010-02-12T09:23:02.567 に答える
3

Equals()、、、または==をオーバーライドするべきではないと思います!=GetHashCode()

これらのいずれかをオーバーライドする場合は、それらのセマンティクスが変更されないようにする必要があります。あなたの例では、彼らはそうします。

たとえば==、P1がP2から0.001 mm、P2がP3から0.001 mm、P1がP3から0.002 mmの場合、P1 == P2、P2 == P3、P1 == P3となります。これは、推移的ではありません。あなたが望むものではありません。一般に、すべてのポイントが他のすべてのポイントと等しくなることになります。

ポイントが十分に近いかどうかを判断するために、別の方法を使用することに固執します。

編集

をオーバーライドすると、次の==ようなコードを記述できます。

if(P1 == P2 && P2 == P3 && P1 != P3)
{
    // Code here gets executed
}
于 2010-02-12T09:37:05.320 に答える
2

Distinctメソッドへの依存関係を削除する方が簡単です。System.Collections.IComparer(または同等の一般的なもの)を実装し、リストのような単純なコレクションを使用します。次に、アイテムが比較対象のリストにあるかどうかを判断し、すでに含まれている場合は追加しないでください。

于 2010-02-12T09:45:22.700 に答える
1

Equals、==、!=、およびGetHashcodeオーバーライドを破棄し、独自のMyPoint.IsEqualTo()およびMyPointCollection.Distinct()メソッドを作成する必要がありますか?

はい。

ただし、必ずしも完全に異なるデータ構造である必要はありません。重複をチェックする際には、隣接するハッシュコードをチェックする必要があります。たとえば、(x + 0.001、y)、(x、y-0.001)などのハッシュです。これにより、通常の重複排除ルックアップと比較して一定の要素が遅くなり、複雑ではないため、これが進むべき道かもしれません。(明らかなポイントですが、ここではまだ明示的に作成されていません。)

更新:明確にするために、問題の1次元バージョンを見てみましょう。「ポイント」は単一の数値xです。の場合、x1とx2が一致すると見なしabs(x1 - x2) < .001ます。ここで、xが{x_0、...、x_n}のいずれかに一致するかどうかを調べます。x_iはハッシュテーブルに保持されます。ここhash(x) = h(floor(1000*x))で、関数h()は、物事を分散させます。xがすでにテーブルにあるかどうかを確認するために、、、、を計算してhash(x-.001)からhash(x)、 xが3つのバケットのいずれかhash(x+.001)のx_iのいずれかに一致するかどうかをテストします。一致するx_iを他のバケットに含めることはできません。

2次元バリアントでは、チェックする隣接バケットが9つあります(中央を数えます)。3Dで、27。

于 2010-02-12T10:43:24.893 に答える
1

常に同じハッシュ(たとえば0)を返す場合、LinQはすべての要素を。と比較しようとしequalsます。結局のところ、ハッシュは、2つの要素が等しくなく、別個であることを証明するのに役立ちます。

ただし、とにかく、このドメインには、バイナリ分割パーティション(BSP)ツリーなどのより適切な構造とアルゴリズムを使用することをお勧めします。

于 2010-02-12T09:23:51.340 に答える
1

これは、SteckとPaoloが言ったことのより明確な説明になるはずです。

GetHashCodeメソッドを希望どおりに記述できたとします。

次に、任意のポイントab、それらの間の距離に関係なく、a.GetHashCode() == b.GetHashCode()

証明:仮定しますa < baとの間の距離をb0.001未満のセグメントに分割します。つまりa0 = aa1 = a0 + 0.0005a2 = a1 + 0.0005,到達するまでなどb

次にa.GetHashCode() == a1.GetHashCode() == a2.GetHashCode() == ... == b.GetHashCode()

于 2010-02-12T14:36:28.597 に答える
0

これが私がしていることを示すためのいくつかのコードです。「元の」の数値の各ペアは、同じ値を返す必要があります。

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);
}
}

上記の方法は、「3dpに切り捨ててから、最も近い偶数に調整する」方法です。次はtruncateメソッドです(開始/終了コメント間のコードを置き換えます):

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

これはラウンドメソッドです:

/* Begin number adjustment logic */
modified[i] = Math.Round(modified[i], tolerance);
/* End number adjustment logic */
于 2010-02-12T09:18:02.730 に答える